htmlediting_impl.cpp
来自「手机浏览器源码程序,功能强大」· C++ 代码 · 共 1,886 行 · 第 1/5 页
CPP
1,886 行
}
//------------------------------------------------------------------------------------------
// ApplyStyleCommandImpl: style-application helpers
bool ApplyStyleCommandImpl::splitTextAtStartIfNeeded(const Position &start, const Position &end)
{
if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
TextImpl *text = static_cast<TextImpl *>(start.node());
SplitTextNodeCommand cmd(document(), text, start.offset());
applyCommandToComposite(cmd);
setEndingSelection(Selection(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment)));
return true;
}
return false;
}
NodeImpl *ApplyStyleCommandImpl::splitTextAtEndIfNeeded(const Position &start, const Position &end)
{
if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
TextImpl *text = static_cast<TextImpl *>(end.node());
SplitTextNodeCommand cmd(document(), text, end.offset());
applyCommandToComposite(cmd);
NodeImpl *startNode = start.node() == end.node() ? cmd.node()->previousSibling() : start.node();
ASSERT(startNode);
setEndingSelection(Selection(Position(startNode, start.offset()), Position(cmd.node()->previousSibling(), cmd.node()->previousSibling()->caretMaxOffset())));
return cmd.node()->previousSibling();
}
return end.node();
}
void ApplyStyleCommandImpl::surroundNodeRangeWithElement(NodeImpl *startNode, NodeImpl *endNode, ElementImpl *element)
{
ASSERT(startNode);
ASSERT(endNode);
ASSERT(element);
NodeImpl *node = startNode;
while (1) {
NodeImpl *next = node->traverseNextNode();
if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
removeNode(node);
appendNode(element, node);
}
if (node == endNode)
break;
node = next;
}
}
void ApplyStyleCommandImpl::applyStyleIfNeeded(DOM::NodeImpl *startNode, DOM::NodeImpl *endNode)
{
StyleChange styleChange = computeStyleChange(Position(startNode, 0), style());
int exceptionCode = 0;
if (styleChange.cssStyle.length() > 0) {
ElementImpl *styleElement = document()->createHTMLElement("SPAN", exceptionCode);
ASSERT(exceptionCode == 0);
styleElement->setAttribute(ATTR_STYLE, styleChange.cssStyle);
styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
insertNodeBefore(styleElement, startNode);
surroundNodeRangeWithElement(startNode, endNode, styleElement);
}
if (styleChange.applyBold) {
ElementImpl *boldElement = document()->createHTMLElement("B", exceptionCode);
ASSERT(exceptionCode == 0);
insertNodeBefore(boldElement, startNode);
surroundNodeRangeWithElement(startNode, endNode, boldElement);
}
if (styleChange.applyItalic) {
ElementImpl *italicElement = document()->createHTMLElement("I", exceptionCode);
ASSERT(exceptionCode == 0);
insertNodeBefore(italicElement, startNode);
surroundNodeRangeWithElement(startNode, endNode, italicElement);
}
}
bool ApplyStyleCommandImpl::currentlyHasStyle(const Position &pos, const CSSProperty *property) const
{
ASSERT(pos.notEmpty());
CSSStyleDeclarationImpl *decl = document()->defaultView()->getComputedStyle(pos.element(), 0);
ASSERT(decl);
CSSValueImpl *value = decl->getPropertyCSSValue(property->id());
return strcasecmp(value->cssText(), property->value()->cssText()) == 0;
}
ApplyStyleCommandImpl::StyleChange ApplyStyleCommandImpl::computeStyleChange(const Position &insertionPoint, CSSStyleDeclarationImpl *style)
{
ASSERT(insertionPoint.notEmpty());
ASSERT(style);
StyleChange styleChange;
for (QPtrListIterator<CSSProperty> it(*(style->values())); it.current(); ++it) {
CSSProperty *property = it.current();
if (!currentlyHasStyle(insertionPoint, property)) {
switch (property->id()) {
case CSS_PROP_FONT_WEIGHT:
if (strcasecmp(property->value()->cssText(), "bold") == 0)
styleChange.applyBold = true;
else
styleChange.cssStyle += property->cssText();
break;
case CSS_PROP_FONT_STYLE: {
DOMString cssText(property->value()->cssText());
if (strcasecmp(cssText, "italic") == 0 || strcasecmp(cssText, "oblique") == 0)
styleChange.applyItalic = true;
else
styleChange.cssStyle += property->cssText();
}
break;
default:
styleChange.cssStyle += property->cssText();
break;
}
}
}
return styleChange;
}
Position ApplyStyleCommandImpl::positionInsertionPoint(Position pos)
{
if (pos.node()->isTextNode() && (pos.offset() > 0 && pos.offset() < pos.node()->maxOffset())) {
SplitTextNodeCommand split(document(), static_cast<TextImpl *>(pos.node()), pos.offset());
split.apply();
pos = Position(split.node(), 0);
}
#if 0
// EDIT FIXME: If modified to work with the internals of applying style,
// this code can work to optimize cases where a style change is taking place on
// a boundary between nodes where one of the nodes has the desired style. In other
// words, it is possible for content to be merged into existing nodes rather than adding
// additional markup.
if (currentlyHasStyle(pos))
return pos;
// try next node
if (pos.offset() >= pos.node()->caretMaxOffset()) {
NodeImpl *nextNode = pos.node()->traverseNextNode();
if (nextNode) {
Position next = Position(nextNode, 0);
if (currentlyHasStyle(next))
return next;
}
}
// try previous node
if (pos.offset() <= pos.node()->caretMinOffset()) {
NodeImpl *prevNode = pos.node()->traversePreviousNode();
if (prevNode) {
Position prev = Position(prevNode, prevNode->maxOffset());
if (currentlyHasStyle(prev))
return prev;
}
}
#endif
return pos;
}
//------------------------------------------------------------------------------------------
// DeleteCollapsibleWhitespaceCommandImpl
DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document)
: CompositeEditCommandImpl(document), m_charactersDeleted(0), m_hasSelectionToCollapse(false)
{
}
DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document, const Selection &selection)
: CompositeEditCommandImpl(document), m_charactersDeleted(0), m_selectionToCollapse(selection), m_hasSelectionToCollapse(true)
{
}
DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl()
{
}
int DeleteCollapsibleWhitespaceCommandImpl::commandID() const
{
return DeleteCollapsibleWhitespaceCommandID;
}
static bool shouldDeleteUpstreamPosition(const Position &pos)
{
if (!pos.node()->isTextNode())
return false;
RenderObject *renderer = pos.node()->renderer();
if (!renderer)
return true;
TextImpl *textNode = static_cast<TextImpl *>(pos.node());
if (pos.offset() >= (long)textNode->length())
return false;
if (pos.isLastRenderedPositionInEditableBlock())
return false;
if (pos.isFirstRenderedPositionOnLine() || pos.isLastRenderedPositionOnLine())
return false;
RenderText *textRenderer = static_cast<RenderText *>(renderer);
for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if (pos.offset() < box->m_start) {
return true;
}
if (pos.offset() >= box->m_start && pos.offset() < box->m_start + box->m_len)
return false;
}
return true;
}
Position DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(const Position &pos)
{
Position upstream = pos.equivalentUpstreamPosition();
Position downstream = pos.equivalentDownstreamPosition();
bool del = shouldDeleteUpstreamPosition(upstream);
LOG(Editing, "pos: %s [%p:%d]\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
if (upstream == downstream) {
LOG(Editing, "same: %s [%p:%d]\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
}
else {
LOG(Editing, "upstream: %s %s [%p:%d]\n", del ? "DELETE" : "SKIP", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
PositionIterator it(upstream);
for (it.next(); it.current() != downstream; it.next()) {
if (it.current().node()->isTextNode() && (long)static_cast<TextImpl *>(it.current().node())->length() == it.current().offset())
LOG(Editing, " node: AT END %s [%p:%d]\n", getTagName(it.current().node()->id()).string().latin1(), it.current().node(), it.current().offset());
else
LOG(Editing, " node: DELETE %s [%p:%d]\n", getTagName(it.current().node()->id()).string().latin1(), it.current().node(), it.current().offset());
}
LOG(Editing, "downstream: %s [%p:%d]\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
}
if (upstream == downstream)
return upstream;
PositionIterator it(upstream);
Position deleteStart = upstream;
if (!del) {
deleteStart = it.peekNext();
if (deleteStart == downstream)
return upstream;
}
Position endingPosition = upstream;
while (it.current() != downstream) {
Position next = it.peekNext();
if (next.node() != deleteStart.node()) {
ASSERT(deleteStart.node()->isTextNode());
TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
unsigned long count = it.current().offset() - deleteStart.offset();
if (count == textNode->length()) {
LOG(Editing, " removeNodeAndPrune 1: [%p]\n", textNode);
if (textNode == endingPosition.node())
endingPosition = Position(next.node(), next.node()->caretMinOffset());
removeNodeAndPrune(textNode);
}
else {
LOG(Editing, " deleteText 1: [%p:%d:%d:%d]\n", textNode, textNode->length(), deleteStart.offset(), it.current().offset() - deleteStart.offset());
deleteText(textNode, deleteStart.offset(), count);
}
deleteStart = next;
}
else if (next == downstream) {
ASSERT(deleteStart.node() == downstream.node());
ASSERT(downstream.node()->isTextNode());
TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
unsigned long count = downstream.offset() - deleteStart.offset();
ASSERT(count <= textNode->length());
if (count == textNode->length()) {
LOG(Editing, " removeNodeAndPrune 2: [%p]\n", textNode);
removeNodeAndPrune(textNode);
}
else {
LOG(Editing, " deleteText 2: [%p:%d:%d:%d]\n", textNode, textNode->length(), deleteStart.offset(), count);
deleteText(textNode, deleteStart.offset(), count);
m_charactersDeleted = count;
endingPosition = Position(downstream.node(), downstream.offset() - m_charactersDeleted);
}
}
it.setPosition(next);
}
return endingPosition;
}
void DeleteCollapsibleWhitespaceCommandImpl::doApply()
{
// If selection has not been set to a custom selection when the command was created,
// use the current ending selection.
if (!m_hasSelectionToCollapse)
m_selectionToCollapse = endingSelection();
int state = m_selectionToCollapse.state();
if (state == Selection::CARET) {
Position endPosition = deleteWhitespace(m_selectionToCollapse.start());
setEndingSelection(endPosition);
//LOG(Editing, "-----------------------------------------------------\n");
}
else if (state == Selection::RANGE) {
Position startPosition = deleteWhitespace(m_selectionToCollapse.start());
// LOG(Editing, "-----------------------------------------------------\n");
Position endPosition = m_selectionToCollapse.end();
if (m_charactersDeleted > 0 && startPosition.node() == endPosition.node()) {
LOG(Editing, "adjust end position by %d\n", m_charactersDeleted);
endPosition = Position(endPosition.node(), endPosition.offset() - m_charactersDeleted);
}
endPosition = deleteWhitespace(endPosition);
setEndingSelection(Selection(startPosition, endPosition));
// LOG(Editing, "=====================================================\n");
}
}
//------------------------------------------------------------------------------------------
// DeleteSelectionCommandImpl
DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document)
: CompositeEditCommandImpl(document), m_hasSelectionToDelete(false)
{
}
DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document, const Selection &selection)
: CompositeEditCommandImpl(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true)
{
}
DeleteSelectionCommandImpl::~DeleteSelectionCommandImpl()
{
}
int DeleteSelectionCommandImpl::commandID() const
{
return DeleteSelectionCommandID;
}
void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle()
{
Selection selection = endingSelection();
if (selection.state() != Selection::CARET)
return;
Position pos(selection.start());
if (!pos.node()->isTextNode())
return;
TextImpl *textNode = static_cast<TextImpl *>(pos.node());
if (pos.offset() == 0) {
PositionIterator it(pos);
Position prev = it.previous();
if (prev == pos)
return;
if (prev.node()->isTextNode()) {
TextImpl *prevTextNode = static_cast<TextImpl *>(prev.node());
if (textNodesAreJoinable(prevTextNode, textNode)) {
joinTextNodes(prevTextNode, textNode);
setEndingSelection(Position(textNode, prevTextNode->length()));
// LOG(Editing, "joinTextNodesWithSameStyle [1]\n");
}
}
}
else if (pos.offset() == (long)textNode->length()) {
PositionIterator it(pos);
Position next = it.next();
if (next == pos)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?