2 * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "visible_units.h"
31 #include "misc/helper.h"
32 #include "rendering/render_text.h"
33 #include "rendering/render_block.h"
34 #include "visible_position.h"
35 #include "visible_text.h"
36 #include "xml/dom_docimpl.h"
37 #include "xml/dom_elementimpl.h"
39 using DOM::DocumentImpl;
40 using DOM::ElementImpl;
47 static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const QChar *, unsigned))
49 Position pos = c.deepEquivalent();
50 NodeImpl *n = pos.node();
52 return VisiblePosition();
53 DocumentImpl *d = n->getDocument();
55 return VisiblePosition();
56 NodeImpl *de = d->documentElement();
58 return VisiblePosition();
59 NodeImpl *boundary = n->enclosingBlockFlowElement();
61 return VisiblePosition();
62 bool isContentEditable = boundary->isContentEditable();
63 while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable()) {
64 boundary = boundary->parentNode();
68 searchRange.setStartBefore(boundary);
69 Position end(pos.equivalentRangeCompliantPosition());
70 searchRange.setEnd(end.node(), end.offset());
71 SimplifiedBackwardsTextIterator it(searchRange);
74 while (!it.atEnd() && it.length() > 0) {
75 // iterate to get chunks until the searchFunction returns a non-zero value.
76 string.prepend(it.characters(), it.length());
77 next = searchFunction(string.unicode(), string.length());
83 if (it.atEnd() && next == 0) {
84 Range range(it.range());
85 pos = Position(range.startContainer().handle(), range.startOffset());
87 else if (!it.atEnd() && it.length() == 0) {
88 // Got a zero-length chunk.
89 // This means we have hit a replaced element.
90 // Make a check to see if the position should be before or after the replaced element
91 // by performing an additional check with a modified string which uses an "X"
92 // character to stand in for the replaced element.
96 string.prepend(chars, 2);
97 unsigned pastImage = searchFunction(string.unicode(), string.length());
98 Range range(it.range());
100 pos = Position(range.startContainer().handle(), range.startOffset());
102 pos = Position(range.endContainer().handle(), range.endOffset());
104 else if (next != 0) {
105 // The simpler iterator used in this function, as compared to the one used in
106 // nextWordPosition(), gives us results we can use directly without having to
107 // iterate again to translate the next value into a DOM position.
108 NodeImpl *node = it.range().startContainer().handle();
109 if (node->isTextNode() || (node->renderer() && node->renderer()->isBR())) {
110 // The next variable contains a usable index into a text node
111 pos = Position(node, next);
114 // If we are not in a text node, we ended on a node boundary, so the
115 // range start offset should be used.
116 pos = Position(node, it.range().startOffset());
120 return VisiblePosition(pos, DOWNSTREAM, VisiblePosition::INIT_DOWN);
123 static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const QChar *, unsigned))
125 Position pos = c.deepEquivalent();
126 NodeImpl *n = pos.node();
128 return VisiblePosition();
129 DocumentImpl *d = n->getDocument();
131 return VisiblePosition();
132 NodeImpl *de = d->documentElement();
134 return VisiblePosition();
135 NodeImpl *boundary = n->enclosingBlockFlowElement();
137 return VisiblePosition();
138 bool isContentEditable = boundary->isContentEditable();
139 while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable()) {
140 boundary = boundary->parentNode();
143 Range searchRange(d);
144 Position start(pos.equivalentRangeCompliantPosition());
145 searchRange.setStart(start.node(), start.offset());
146 searchRange.setEndAfter(boundary);
147 TextIterator it(searchRange, RUNFINDER);
150 while (!it.atEnd() && it.length() > 0) {
151 // Keep asking the iterator for chunks until the search function
152 // returns an end value not equal to the length of the string passed to it.
153 string.append(it.characters(), it.length());
154 next = searchFunction(string.unicode(), string.length());
155 if (next != string.length())
160 if (it.atEnd() && next == string.length()) {
161 Range range(it.range());
162 pos = Position(range.startContainer().handle(), range.startOffset());
164 else if (!it.atEnd() && it.length() == 0) {
165 // Got a zero-length chunk.
166 // This means we have hit a replaced element.
167 // Make a check to see if the position should be before or after the replaced element
168 // by performing an additional check with a modified string which uses an "X"
169 // character to stand in for the replaced element.
173 string.append(chars, 2);
174 unsigned pastImage = searchFunction(string.unicode(), string.length());
175 Range range(it.range());
176 if (next != pastImage)
177 pos = Position(range.endContainer().handle(), range.endOffset());
179 pos = Position(range.startContainer().handle(), range.startOffset());
181 else if (next != 0) {
182 // Use the character iterator to translate the next value into a DOM position.
183 CharacterIterator charIt(searchRange);
184 charIt.advance(next - 1);
185 pos = Position(charIt.range().endContainer().handle(), charIt.range().endOffset());
187 return VisiblePosition(pos, UPSTREAM, VisiblePosition::INIT_UP);
192 static unsigned startWordBoundary(const QChar *characters, unsigned length)
195 findWordBoundary(characters, length, length, &start, &end);
199 VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
201 VisiblePosition p = c;
202 if (side == RightWordIfOnBoundary) {
203 // at paragraph end, the startofWord is the current position
204 if (isEndOfParagraph(c))
211 return previousBoundary(p, startWordBoundary);
214 static unsigned endWordBoundary(const QChar *characters, unsigned length)
217 findWordBoundary(characters, length, 0, &start, &end);
221 VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side)
223 VisiblePosition p = c;
224 if (side == LeftWordIfOnBoundary) {
225 if (isStartOfParagraph(c))
232 // at paragraph end, the endOfWord is the start of next paragraph
233 if (isEndOfParagraph(c)) {
235 return p.isNotNull() ? p : c;
239 return nextBoundary(p, endWordBoundary);
242 static unsigned previousWordPositionBoundary(const QChar *characters, unsigned length)
244 return nextWordFromIndex(characters, length, length, false);
247 VisiblePosition previousWordPosition(const VisiblePosition &c)
249 return previousBoundary(c, previousWordPositionBoundary);
252 static unsigned nextWordPositionBoundary(const QChar *characters, unsigned length)
254 return nextWordFromIndex(characters, length, 0, true);
257 VisiblePosition nextWordPosition(const VisiblePosition &c)
259 return nextBoundary(c, nextWordPositionBoundary);
264 static RootInlineBox *rootBoxForLine(const VisiblePosition &c)
266 Position p = c.deepEquivalent();
267 NodeImpl *node = p.node();
271 RenderObject *renderer = node->renderer();
275 InlineBox *box = renderer->inlineBox(p.offset(), c.affinity());
282 VisiblePosition startOfLine(const VisiblePosition &c)
284 RootInlineBox *rootBox = rootBoxForLine(c);
286 return VisiblePosition();
288 InlineBox *startBox = rootBox->firstLeafChild();
290 return VisiblePosition();
292 RenderObject *startRenderer = startBox->object();
294 return VisiblePosition();
296 NodeImpl *startNode = startRenderer->element();
298 return VisiblePosition();
300 long startOffset = 0;
301 if (startBox->isInlineTextBox()) {
302 InlineTextBox *startTextBox = static_cast<InlineTextBox *>(startBox);
303 startOffset = startTextBox->m_start;
306 return VisiblePosition(startNode, startOffset, DOWNSTREAM);
309 VisiblePosition endOfLine(const VisiblePosition &c, EIncludeLineBreak includeLineBreak)
311 // FIXME: Need to implement the "include line break" version.
312 assert(includeLineBreak == DoNotIncludeLineBreak);
314 RootInlineBox *rootBox = rootBoxForLine(c);
316 return VisiblePosition();
318 InlineBox *endBox = rootBox->lastLeafChild();
320 return VisiblePosition();
322 RenderObject *endRenderer = endBox->object();
324 return VisiblePosition();
326 NodeImpl *endNode = endRenderer->element();
328 return VisiblePosition();
331 if (endNode->id() == ID_BR) {
333 } else if (endBox->isInlineTextBox()) {
334 InlineTextBox *endTextBox = static_cast<InlineTextBox *>(endBox);
335 endOffset = endTextBox->m_start + endTextBox->m_len;
338 // generate VisiblePosition with correct affinity
339 VisiblePosition result = VisiblePosition(endNode, endOffset, DOWNSTREAM);
340 VisiblePosition temp = result;
341 temp.setAffinity(UPSTREAM);
342 if (visiblePositionsOnDifferentLines(temp, result))
343 result.setAffinity(UPSTREAM);
348 bool inSameLine(const VisiblePosition &a, const VisiblePosition &b)
350 return a.isNotNull() && startOfLine(a) == startOfLine(b);
353 bool isStartOfLine(const VisiblePosition &p)
355 return p.isNotNull() && p == startOfLine(p);
358 bool isEndOfLine(const VisiblePosition &p)
360 return p.isNotNull() && p == endOfLine(p, DoNotIncludeLineBreak);
363 VisiblePosition previousLinePosition(const VisiblePosition &c, int x)
365 Position p = c.affinity() == UPSTREAM ? c.deepEquivalent() : c.downstreamDeepEquivalent();
366 NodeImpl *node = p.node();
367 if (!node || !node->getDocument())
368 return VisiblePosition();
370 node->getDocument()->updateLayout();
372 RenderObject *renderer = node->renderer();
374 return VisiblePosition();
376 RenderBlock *containingBlock = 0;
377 RootInlineBox *root = 0;
378 InlineBox *box = renderer->inlineBox(p.offset(), c.affinity());
380 root = box->root()->prevRootBox();
382 containingBlock = renderer->containingBlock();
386 // This containing editable block does not have a previous line.
387 // Need to move back to previous containing editable block in this root editable
388 // block and find the last root line box in that block.
389 NodeImpl *startBlock = node->enclosingBlockFlowElement();
390 NodeImpl *n = node->previousEditable();
391 while (n && startBlock == n->enclosingBlockFlowElement())
392 n = n->previousEditable();
394 if (!n->inSameRootEditableElement(node))
396 Position pos(n, n->caretMinOffset());
397 if (pos.inRenderedContent()) {
398 assert(n->renderer());
399 box = n->renderer()->inlineBox(n->caretMaxOffset());
401 // previous root line box found
403 containingBlock = n->renderer()->containingBlock();
407 // Work around <rdar://problem/4040763>. Need to
408 // doublecheck that pos is really on a different line,
409 // because it might not be a viable VisiblePosition
410 // (whereupon the next VP is returned, which could be
412 VisiblePosition c2 = VisiblePosition(pos, DOWNSTREAM);
413 if (visiblePositionsOnDifferentLines(c, c2))
416 n = n->previousEditable();
422 containingBlock->absolutePosition(absx, absy);
423 RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object();
424 return renderer->positionForCoordinates(x, absy + root->topOverflow());
427 // Could not find a previous line. This means we must already be on the first line.
428 // Move to the start of the content in this block, which effectively moves us
429 // to the start of the line we're on.
430 return VisiblePosition(node->rootEditableElement(), 0, DOWNSTREAM);
433 VisiblePosition nextLinePosition(const VisiblePosition &c, int x)
435 Position p = c.affinity() == UPSTREAM ? c.deepEquivalent() : c.downstreamDeepEquivalent();
436 NodeImpl *node = p.node();
437 if (!node || !node->getDocument())
438 return VisiblePosition();
440 node->getDocument()->updateLayout();
442 RenderObject *renderer = node->renderer();
444 return VisiblePosition();
446 RenderBlock *containingBlock = 0;
447 RootInlineBox *root = 0;
448 InlineBox *box = renderer->inlineBox(p.offset(), c.affinity());
450 root = box->root()->nextRootBox();
452 containingBlock = renderer->containingBlock();
456 // This containing editable block does not have a next line.
457 // Need to move forward to next containing editable block in this root editable
458 // block and find the first root line box in that block.
459 NodeImpl *startBlock = node->enclosingBlockFlowElement();
460 NodeImpl *n = node->nextEditable();
461 while (n && startBlock == n->enclosingBlockFlowElement())
462 n = n->nextEditable();
464 if (!n->inSameRootEditableElement(node))
466 Position pos(n, n->caretMinOffset());
467 if (pos.inRenderedContent()) {
468 assert(n->renderer());
469 box = n->renderer()->inlineBox(n->caretMinOffset());
471 // next root line box found
473 containingBlock = n->renderer()->containingBlock();
477 return VisiblePosition(pos, DOWNSTREAM);
479 n = n->nextEditable();
485 containingBlock->absolutePosition(absx, absy);
486 RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object();
487 return renderer->positionForCoordinates(x, absy + root->topOverflow());
490 // Could not find a next line. This means we must already be on the last line.
491 // Move to the end of the content in this block, which effectively moves us
492 // to the end of the line we're on.
493 ElementImpl *rootElement = node->rootEditableElement();
494 return VisiblePosition(rootElement, rootElement ? rootElement->childNodeCount() : 0, DOWNSTREAM);
499 static unsigned startSentenceBoundary(const QChar *characters, unsigned length)
502 findSentenceBoundary(characters, length, length, &start, &end);
506 VisiblePosition startOfSentence(const VisiblePosition &c)
508 return previousBoundary(c, startSentenceBoundary);
511 static unsigned endSentenceBoundary(const QChar *characters, unsigned length)
514 findSentenceBoundary(characters, length, 0, &start, &end);
518 VisiblePosition endOfSentence(const VisiblePosition &c)
520 return nextBoundary(c, endSentenceBoundary);
523 static unsigned previousSentencePositionBoundary(const QChar *characters, unsigned length)
525 return nextSentenceFromIndex(characters, length, length, false);
528 VisiblePosition previousSentencePosition(const VisiblePosition &c, int x)
530 return previousBoundary(c, previousSentencePositionBoundary);
533 static unsigned nextSentencePositionBoundary(const QChar *characters, unsigned length)
535 return nextSentenceFromIndex(characters, length, 0, true);
538 VisiblePosition nextSentencePosition(const VisiblePosition &c, int x)
540 return nextBoundary(c, nextSentencePositionBoundary);
543 VisiblePosition startOfParagraph(const VisiblePosition &c)
545 Position p = c.deepEquivalent();
546 NodeImpl *startNode = p.node();
548 return VisiblePosition();
550 NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
552 NodeImpl *node = startNode;
553 long offset = p.offset();
555 for (NodeImpl *n = startNode; n; n = n->traversePreviousNodePostOrder(startBlock)) {
556 RenderObject *r = n->renderer();
559 RenderStyle *style = r->style();
560 if (style->visibility() != VISIBLE)
562 if (r->isBR() || r->isBlockFlow())
565 if (style->whiteSpace() == PRE) {
566 QChar *text = static_cast<RenderText *>(r)->text();
567 long i = static_cast<RenderText *>(r)->length();
569 if (n == startNode && o < i)
573 return VisiblePosition(n, i + 1, DOWNSTREAM);
577 } else if (r->isReplaced()) {
583 return VisiblePosition(node, offset, DOWNSTREAM);
586 VisiblePosition endOfParagraph(const VisiblePosition &c, EIncludeLineBreak includeLineBreak)
588 Position p = c.deepEquivalent();
590 NodeImpl *startNode = p.node();
592 return VisiblePosition();
594 NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
595 NodeImpl *stayInsideBlock = includeLineBreak ? 0 : startBlock;
597 NodeImpl *node = startNode;
598 long offset = p.offset();
600 for (NodeImpl *n = startNode; n; n = n->traverseNextNode(stayInsideBlock)) {
601 if (n->isContentEditable() != startNode->isContentEditable())
603 RenderObject *r = n->renderer();
606 RenderStyle *style = r->style();
607 if (style->visibility() != VISIBLE)
610 if (includeLineBreak)
611 return VisiblePosition(n, 1, DOWNSTREAM);
614 if (r->isBlockFlow()) {
615 if (includeLineBreak)
616 return VisiblePosition(n, 0, DOWNSTREAM);
619 // FIXME: We avoid returning a position where the renderer can't accept the caret.
620 // We should probably do this in other cases such as startOfParagraph.
621 if (r->isText() && r->caretMaxRenderedOffset() > 0) {
622 if (includeLineBreak && !n->isAncestor(startBlock))
623 return VisiblePosition(n, 0, DOWNSTREAM);
624 long length = static_cast<RenderText *>(r)->length();
625 if (style->whiteSpace() == PRE) {
626 QChar *text = static_cast<RenderText *>(r)->text();
628 if (n == startNode && offset < length)
630 for (long i = o; i < length; ++i)
632 return VisiblePosition(n, i + includeLineBreak, DOWNSTREAM);
636 } else if (r->isReplaced()) {
639 if (includeLineBreak && !n->isAncestor(startBlock))
644 return VisiblePosition(node, offset, DOWNSTREAM);
647 bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b)
649 return a.isNotNull() && startOfParagraph(a) == startOfParagraph(b);
652 bool isStartOfParagraph(const VisiblePosition &pos)
654 return pos.isNotNull() && isEqualIgnoringAffinity(pos, startOfParagraph(pos));
657 bool isEndOfParagraph(const VisiblePosition &pos)
659 return pos.isNotNull() && isEqualIgnoringAffinity(pos, endOfParagraph(pos, DoNotIncludeLineBreak));
662 VisiblePosition previousParagraphPosition(const VisiblePosition &p, int x)
664 VisiblePosition pos = p;
666 VisiblePosition n = previousLinePosition(pos, x);
667 if (n.isNull() || n == pos) {
671 } while (inSameParagraph(p, pos));
675 VisiblePosition nextParagraphPosition(const VisiblePosition &p, int x)
677 VisiblePosition pos = p;
679 VisiblePosition n = nextLinePosition(pos, x);
680 if (n.isNull() || n == pos) {
684 } while (inSameParagraph(p, pos));
690 VisiblePosition startOfBlock(const VisiblePosition &c)
692 Position p = c.deepEquivalent();
693 NodeImpl *startNode = p.node();
695 return VisiblePosition();
696 return VisiblePosition(Position(startNode->enclosingBlockFlowElement(), 0), DOWNSTREAM);
699 // written, but not yet tested
700 VisiblePosition endOfBlock(const VisiblePosition &c)
702 Position p = c.deepEquivalent();
704 NodeImpl *startNode = p.node();
706 return VisiblePosition();
708 NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
710 return VisiblePosition(startBlock, startBlock->childNodeCount(), VP_DEFAULT_AFFINITY);
713 bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b)
715 return !a.isNull() && enclosingBlockFlowElement(a) == enclosingBlockFlowElement(b);
718 bool isStartOfBlock(const VisiblePosition &pos)
720 return pos.isNotNull() && isEqualIgnoringAffinity(pos, startOfBlock(pos));
723 bool isEndOfBlock(const VisiblePosition &pos)
725 return pos.isNotNull() && isEqualIgnoringAffinity(pos, endOfBlock(pos));
730 VisiblePosition startOfDocument(const VisiblePosition &c)
732 Position p = c.deepEquivalent();
733 NodeImpl *node = p.node();
735 return VisiblePosition();
737 DocumentImpl *doc = node->getDocument();
739 return VisiblePosition();
741 return VisiblePosition(doc->documentElement(), 0, DOWNSTREAM);
744 VisiblePosition endOfDocument(const VisiblePosition &c)
746 Position p = c.deepEquivalent();
747 NodeImpl *node = p.node();
749 return VisiblePosition();
751 DocumentImpl *doc = node->getDocument();
753 return VisiblePosition();
755 NodeImpl *docElem = doc->documentElement();
757 return VisiblePosition();
759 return VisiblePosition(docElem, docElem->childNodeCount(), DOWNSTREAM);
762 bool inSameDocument(const VisiblePosition &a, const VisiblePosition &b)
764 Position ap = a.deepEquivalent();
765 NodeImpl *an = ap.node();
768 Position bp = b.deepEquivalent();
769 NodeImpl *bn = bp.node();
773 return an->getDocument() == bn->getDocument();
776 bool isStartOfDocument(const VisiblePosition &p)
778 return p.isNotNull() && p.previous().isNull();
781 bool isEndOfDocument(const VisiblePosition &p)
783 return p.isNotNull() && p.next().isNull();
788 VisiblePosition startOfEditableContent(const VisiblePosition &c)
790 Position p = c.deepEquivalent();
791 NodeImpl *node = p.node();
793 return VisiblePosition();
795 if (!node->isContentEditable())
796 return VisiblePosition();
798 return VisiblePosition(node->rootEditableElement(), 0, DOWNSTREAM);
801 VisiblePosition endOfEditableContent(const VisiblePosition &c)
803 Position p = c.deepEquivalent();
804 NodeImpl *node = p.node();
806 return VisiblePosition();
808 if (!node->isContentEditable())
809 return VisiblePosition();
811 node = node->rootEditableElement();
813 return VisiblePosition();
815 return VisiblePosition(node, node->childNodeCount(), DOWNSTREAM);
818 bool inSameEditableContent(const VisiblePosition &a, const VisiblePosition &b)
820 Position ap = a.deepEquivalent();
821 NodeImpl *an = ap.node();
825 Position bp = b.deepEquivalent();
826 NodeImpl *bn = bp.node();
830 if (!an->isContentEditable() || !bn->isContentEditable())
833 return an->rootEditableElement() == bn->rootEditableElement();
836 bool isStartOfEditableContent(const VisiblePosition &p)
838 return !inSameEditableContent(p, p.previous());
841 bool isEndOfEditableContent(const VisiblePosition &p)
843 return !inSameEditableContent(p, p.next());