?? htmlediting.cpp
字號(hào):
void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position &pos)
{
if (pos.isNull())
return;
document()->updateLayout();
VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
VisiblePosition visibleParagraphEnd(endOfParagraph(visiblePos, IncludeLineBreak));
Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream(StayInBlock);
Position paragraphEnd = visibleParagraphEnd.deepEquivalent().upstream(StayInBlock);
Position beforeParagraphStart = paragraphStart.upstream(DoNotStayInBlock);
// Perform some checks to see if we need to perform work in this function.
if (paragraphStart.node()->isBlockFlow()) {
if (paragraphEnd.node()->isBlockFlow()) {
if (!paragraphEnd.node()->isAncestor(paragraphStart.node())) {
// If the paragraph end is a descendant of paragraph start, then we need to run
// the rest of this function. If not, we can bail here.
return;
}
}
else if (paragraphEnd.node()->enclosingBlockFlowElement() != paragraphStart.node()) {
// The paragraph end is in another block that is an ancestor of the paragraph start.
// We can bail as we have a full block to work with.
ASSERT(paragraphStart.node()->isAncestor(paragraphEnd.node()->enclosingBlockFlowElement()));
return;
}
else if (isEndOfDocument(visibleParagraphEnd)) {
// At the end of the document. We can bail here as well.
return;
}
}
// Create the block to insert. Most times, this will be a shallow clone of the block containing
// the start of the selection (the start block), except for two cases:
// 1) When the start block is a body element.
// 2) When the start block is a mail blockquote and we are not in a position to insert
// the new block as a peer of the start block. This prevents creating an unwanted
// additional level of quoting.
NodeImpl *startBlock = paragraphStart.node()->enclosingBlockFlowElement();
NodeImpl *newBlock = 0;
if (startBlock->id() == ID_BODY || (isMailBlockquote(startBlock) && paragraphStart.node() != startBlock))
newBlock = createDefaultParagraphElement(document());
else
newBlock = startBlock->cloneNode(false);
NodeImpl *moveNode = paragraphStart.node();
if (paragraphStart.offset() >= paragraphStart.node()->caretMaxOffset())
moveNode = moveNode->traverseNextNode();
NodeImpl *endNode = paragraphEnd.node();
if (paragraphStart.node()->id() == ID_BODY) {
insertNodeAt(newBlock, paragraphStart.node(), 0);
}
else if (paragraphStart.node()->id() == ID_BR) {
insertNodeAfter(newBlock, paragraphStart.node());
}
else if (paragraphStart.node()->isBlockFlow()) {
insertNodeBefore(newBlock, paragraphStart.node());
}
else if (beforeParagraphStart.node()->enclosingBlockFlowElement()->id() != ID_BODY) {
insertNodeAfter(newBlock, beforeParagraphStart.node()->enclosingBlockFlowElement());
}
else {
insertNodeAfter(newBlock, beforeParagraphStart.node());
}
while (moveNode && !moveNode->isBlockFlow()) {
NodeImpl *next = moveNode->traverseNextSibling();
removeNode(moveNode);
appendNode(moveNode, newBlock);
if (moveNode == endNode)
break;
moveNode = next;
}
}
static bool isSpecialElement(NodeImpl *n)
{
if (!n->isHTMLElement())
return false;
if (n->id() == ID_A && n->hasAnchor())
return true;
if (n->id() == ID_UL || n->id() == ID_OL || n->id() == ID_DL)
return true;
RenderObject *renderer = n->renderer();
if (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE))
return true;
if (renderer && renderer->style()->isFloating())
return true;
if (renderer && renderer->style()->position() != STATIC)
return true;
return false;
}
// This version of the function is meant to be called on positions in a document fragment,
// so it does not check for a root editable element, it is assumed these nodes will be put
// somewhere editable in the future
static bool isFirstVisiblePositionInSpecialElementInFragment(const Position& pos)
{
VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
return false;
if (isSpecialElement(n))
return true;
}
return false;
}
static bool isFirstVisiblePositionInSpecialElement(const Position& pos)
{
VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
return false;
if (n->rootEditableElement() == NULL)
return false;
if (isSpecialElement(n))
return true;
}
return false;
}
static Position positionBeforeNode(NodeImpl *node)
{
return Position(node->parentNode(), node->nodeIndex());
}
static Position positionBeforeContainingSpecialElement(const Position& pos)
{
ASSERT(isFirstVisiblePositionInSpecialElement(pos));
VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
NodeImpl *outermostSpecialElement = NULL;
for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
break;
if (n->rootEditableElement() == NULL)
break;
if (isSpecialElement(n))
outermostSpecialElement = n;
}
ASSERT(outermostSpecialElement);
Position result = positionBeforeNode(outermostSpecialElement);
if (result.isNull() || !result.node()->rootEditableElement())
return pos;
return result;
}
static bool isLastVisiblePositionInSpecialElement(const Position& pos)
{
// make sure to get a range-compliant version of the position
Position rangePos = VisiblePosition(pos, DOWNSTREAM).position();
VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
return false;
if (n->rootEditableElement() == NULL)
return false;
if (isSpecialElement(n))
return true;
}
return false;
}
static Position positionAfterNode(NodeImpl *node)
{
return Position(node->parentNode(), node->nodeIndex() + 1);
}
static Position positionAfterContainingSpecialElement(const Position& pos)
{
ASSERT(isLastVisiblePositionInSpecialElement(pos));
// make sure to get a range-compliant version of the position
Position rangePos = VisiblePosition(pos, DOWNSTREAM).position();
VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
NodeImpl *outermostSpecialElement = NULL;
for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
break;
if (n->rootEditableElement() == NULL)
break;
if (isSpecialElement(n))
outermostSpecialElement = n;
}
ASSERT(outermostSpecialElement);
Position result = positionAfterNode(outermostSpecialElement);
if (result.isNull() || !result.node()->rootEditableElement())
return pos;
return result;
}
static Position positionOutsideContainingSpecialElement(const Position &pos)
{
if (isFirstVisiblePositionInSpecialElement(pos)) {
return positionBeforeContainingSpecialElement(pos);
} else if (isLastVisiblePositionInSpecialElement(pos)) {
return positionAfterContainingSpecialElement(pos);
}
return pos;
}
static Position positionBeforePossibleContainingSpecialElement(const Position &pos)
{
if (isFirstVisiblePositionInSpecialElement(pos)) {
return positionBeforeContainingSpecialElement(pos);
}
return pos;
}
static Position positionAfterPossibleContainingSpecialElement(const Position &pos)
{
if (isLastVisiblePositionInSpecialElement(pos)) {
return positionAfterContainingSpecialElement(pos);
}
return pos;
}
//==========================================================================================
// Concrete commands
//------------------------------------------------------------------------------------------
// AppendNodeCommand
AppendNodeCommand::AppendNodeCommand(DocumentImpl *document, NodeImpl *appendChild, NodeImpl *parentNode)
: EditCommand(document), m_appendChild(appendChild), m_parentNode(parentNode)
{
ASSERT(m_appendChild);
m_appendChild->ref();
ASSERT(m_parentNode);
m_parentNode->ref();
}
AppendNodeCommand::~AppendNodeCommand()
{
ASSERT(m_appendChild);
m_appendChild->deref();
ASSERT(m_parentNode);
m_parentNode->deref();
}
void AppendNodeCommand::doApply()
{
ASSERT(m_appendChild);
ASSERT(m_parentNode);
int exceptionCode = 0;
m_parentNode->appendChild(m_appendChild, exceptionCode);
ASSERT(exceptionCode == 0);
}
void AppendNodeCommand::doUnapply()
{
ASSERT(m_appendChild);
ASSERT(m_parentNode);
ASSERT(state() == Applied);
int exceptionCode = 0;
m_parentNode->removeChild(m_appendChild, exceptionCode);
ASSERT(exceptionCode == 0);
}
//------------------------------------------------------------------------------------------
// ApplyStyleCommand
ApplyStyleCommand::ApplyStyleCommand(DocumentImpl *document, CSSStyleDeclarationImpl *style, EditAction editingAction, EPropertyLevel propertyLevel)
: CompositeEditCommand(document), m_style(style->makeMutable()), m_editingAction(editingAction), m_propertyLevel(propertyLevel)
{
ASSERT(m_style);
m_style->ref();
}
ApplyStyleCommand::~ApplyStyleCommand()
{
ASSERT(m_style);
m_style->deref();
}
void ApplyStyleCommand::doApply()
{
switch (m_propertyLevel) {
case PropertyDefault: {
// apply the block-centric properties of the style
CSSMutableStyleDeclarationImpl *blockStyle = m_style->copyBlockProperties();
blockStyle->ref();
applyBlockStyle(blockStyle);
// apply any remaining styles to the inline elements
// NOTE: hopefully, this string comparison is the same as checking for a non-null diff
if (blockStyle->length() < m_style->length()) {
CSSMutableStyleDeclarationImpl *inlineStyle = m_style->copy();
inlineStyle->ref();
applyRelativeFontStyleChange(inlineStyle);
blockStyle->diff(inlineStyle);
applyInlineStyle(inlineStyle);
inlineStyle->deref();
}
blockStyle->deref();
break;
}
case ForceBlockProperties:
// Force all properties to be applied as block styles.
applyBlockStyle(m_style);
break;
}
setEndingSelectionNeedsLayout();
}
EditAction ApplyStyleCommand::editingAction() const
{
return m_editingAction;
}
void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclarationImpl *style)
{
// update document layout once before removing styles
// so that we avoid the expense of updating before each and every call
// to check a computed style
document()->updateLayout();
// get positions we want to use for applying style
Position start(endingSelection().start());
Position end(endingSelection().end());
// remove current values, if any, of the specified styles from the blocks
// NOTE: tracks the previous block to avoid repeated processing
// Also, gather up all the nodes we want to process in a QPtrList before
// doing anything. This averts any bugs iterating over these nodes
// once you start removing and applying style.
NodeImpl *beyondEnd = end.node()->traverseNextNode();
QPtrList<NodeImpl> nodes;
for (NodeImpl *node = start.node(); node != beyondEnd; node = node->traverseNextNode())
nodes.append(node);
NodeImpl *prevBlock = 0;
for (QPtrListIterator<NodeImpl> it(nodes); it.current(); ++it) {
NodeImpl *block = it.current()->enclosingBlockFlowElement();
if (block != prevBlock && block->isHTMLElement()) {
removeCSSStyle(style, static_cast<HTMLElementImpl *>(block));
prevBlock = block;
}
}
// apply specified styles to the block flow elements in the selected range
prevBlock = 0;
for (QPtrListIterator<NodeImpl> it(nodes); it.current(); ++it) {
NodeImpl *node = it.current();
if (node->renderer()) {
NodeImpl *block = node->enclosingBlockFlowElement();
if (block != prevBlock) {
addBlockStyleIfNeeded(style, node);
prevBlock = block;
}
}
}
}
#define NoFontDelta (0.0f)
#define MinimumFontSize (0.1f)
void ApplyStyleCommand::applyRelativeFontStyleChange(CSSMutableStyleDeclarationImpl *style)
{
if (style->getPropertyCSSValue(CSS_PROP_FONT_SIZE)) {
// Explicit font size overrides any delta.
style->removeProperty(CSS_PROP__KHTML_FONT_SIZE_DELTA);
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -