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 "selection.h"
32 #include "dom/dom_node.h"
33 #include "dom/dom_string.h"
34 #include "khtml_part.h"
35 #include "khtmlview.h"
36 #include "misc/htmltags.h"
37 #include "rendering/render_object.h"
38 #include "rendering/render_style.h"
39 #include "rendering/render_text.h"
40 #include "visible_position.h"
41 #include "visible_units.h"
42 #include "xml/dom_docimpl.h"
43 #include "xml/dom_elementimpl.h"
44 #include "xml/dom_nodeimpl.h"
45 #include "xml/dom_positioniterator.h"
46 #include "xml/dom_textimpl.h"
47 #include "xml/dom2_rangeimpl.h"
50 #include "KWQAssertions.h"
52 #define ASSERT(assertion) assert(assertion)
58 using DOM::ElementImpl;
64 using DOM::StayInBlock;
68 static Selection selectionForLine(const Position &position, EAffinity affinity);
70 Selection::Selection()
75 Selection::Selection(const Position &pos)
76 : m_base(pos), m_extent(pos)
82 Selection::Selection(const Range &r)
83 : m_base(startPosition(r)), m_extent(endPosition(r))
89 Selection::Selection(const Position &base, const Position &extent)
90 : m_base(base), m_extent(extent)
96 Selection::Selection(const VisiblePosition &base, const VisiblePosition &extent)
97 : m_base(base.position()), m_extent(extent.position())
103 Selection::Selection(const Selection &o)
104 : m_base(o.m_base), m_extent(o.m_extent)
105 , m_start(o.m_start), m_end(o.m_end)
106 , m_state(o.m_state), m_affinity(o.m_affinity)
107 , m_baseIsStart(o.m_baseIsStart)
108 , m_needsLayout(o.m_needsLayout)
109 , m_modifyBiasSet(o.m_modifyBiasSet)
111 // Only copy the coordinates over if the other object
112 // has had a layout, otherwise keep the current
113 // coordinates. This prevents drawing artifacts from
114 // remaining when the caret is painted and then moves,
115 // and the old rectangle needs to be repainted.
116 if (!m_needsLayout) {
117 m_caretRect = o.m_caretRect;
118 m_expectedVisibleRect = o.m_expectedVisibleRect;
122 void Selection::init()
125 m_affinity = UPSTREAM;
126 m_baseIsStart = true;
127 m_needsLayout = true;
128 m_modifyBiasSet = false;
131 Selection &Selection::operator=(const Selection &o)
134 m_extent = o.m_extent;
139 m_affinity = o.m_affinity;
141 m_baseIsStart = o.m_baseIsStart;
142 m_needsLayout = o.m_needsLayout;
143 m_modifyBiasSet = o.m_modifyBiasSet;
145 // Only copy the coordinates over if the other object
146 // has had a layout, otherwise keep the current
147 // coordinates. This prevents drawing artifacts from
148 // remaining when the caret is painted and then moves,
149 // and the old rectangle needs to be repainted.
150 if (!m_needsLayout) {
151 m_caretRect = o.m_caretRect;
152 m_expectedVisibleRect = o.m_expectedVisibleRect;
158 void Selection::setAffinity(EAffinity affinity)
160 if (affinity == m_affinity)
163 m_affinity = affinity;
167 void Selection::modifyAffinity(EAlter alter, EDirection dir, ETextGranularity granularity)
169 switch (granularity) {
172 m_affinity = DOWNSTREAM;
176 if (dir == BACKWARD || dir == LEFT)
177 m_affinity = UPSTREAM;
179 case PARAGRAPH_BOUNDARY:
180 case DOCUMENT_BOUNDARY:
181 // These granularities should not change affinity.
183 case LINE_BOUNDARY: {
184 // When extending, leave affinity unchanged.
189 m_affinity = UPSTREAM;
193 m_affinity = DOWNSTREAM;
203 void Selection::moveTo(const Range &r)
205 m_affinity = UPSTREAM;
206 m_base = startPosition(r);
207 m_extent = endPosition(r);
211 void Selection::moveTo(const Selection &o)
213 m_affinity = UPSTREAM;
219 void Selection::moveTo(const Position &pos)
221 m_affinity = UPSTREAM;
227 void Selection::moveTo(const Position &base, const Position &extent)
229 m_affinity = UPSTREAM;
235 void Selection::setModifyBias(EAlter alter, EDirection direction)
239 m_modifyBiasSet = false;
242 if (!m_modifyBiasSet) {
243 m_modifyBiasSet = true;
245 // FIXME: right for bidi?
262 VisiblePosition Selection::modifyExtendingRightForward(ETextGranularity granularity)
264 VisiblePosition pos(m_extent);
265 switch (granularity) {
270 pos = nextWordPosition(pos);
273 pos = nextParagraphPosition(pos, m_affinity, xPosForVerticalArrowNavigation(EXTENT));
276 pos = nextLinePosition(pos, m_affinity, xPosForVerticalArrowNavigation(EXTENT));
279 pos = VisiblePosition(selectionForLine(m_end, m_affinity).end());
281 case PARAGRAPH_BOUNDARY:
282 pos = endOfParagraph(VisiblePosition(m_end));
284 case DOCUMENT_BOUNDARY: {
285 NodeImpl *de = m_start.node()->getDocument()->documentElement();
286 pos = VisiblePosition(de, de ? de->childNodeCount() : 0);
293 VisiblePosition Selection::modifyMovingRightForward(ETextGranularity granularity)
296 switch (granularity) {
299 pos = VisiblePosition(m_end);
301 pos = VisiblePosition(m_extent).next();
304 pos = nextWordPosition(VisiblePosition(m_extent));
307 pos = nextParagraphPosition(VisiblePosition(m_end), m_affinity, xPosForVerticalArrowNavigation(END, isRange()));
310 // This somewhat complicated code is needed to handle the case where there is a
311 // whole line selected (like when the user clicks at the start of a line and hits shift+down-arrow),
312 // and then hits an (unshifted) down arrow. Since the whole-line selection considers its
313 // ending point to be the start of the next line, it may be necessary to juggle the
314 // position to use as the VisiblePosition to pass to nextLinePosition(). If this juggling
315 // is not done, you can wind up skipping a line. See these two bugs for more information:
316 // <rdar://problem/3875618> REGRESSION (Mail): Hitting down arrow with full line selected skips line (br case)
317 // <rdar://problem/3875641> REGRESSION (Mail): Hitting down arrow with full line selected skips line (div case)
319 pos = VisiblePosition(m_end);
321 else if (isRange()) {
322 Position p(m_end.upstream());
323 if (p.node()->id() == ID_BR)
324 pos = VisiblePosition(Position(p.node(), 0));
326 pos = VisiblePosition(p);
328 pos = nextLinePosition(pos, m_affinity, xPosForVerticalArrowNavigation(END, isRange()));
332 pos = VisiblePosition(selectionForLine(m_end, m_affinity).end());
334 case PARAGRAPH_BOUNDARY:
335 pos = endOfParagraph(VisiblePosition(m_end));
337 case DOCUMENT_BOUNDARY: {
338 NodeImpl *de = m_start.node()->getDocument()->documentElement();
339 pos = VisiblePosition(de, de ? de->childNodeCount() : 0);
346 VisiblePosition Selection::modifyExtendingLeftBackward(ETextGranularity granularity)
348 VisiblePosition pos(m_extent);
349 switch (granularity) {
351 pos = pos.previous();
354 pos = previousWordPosition(pos);
357 pos = previousParagraphPosition(pos, m_affinity, xPosForVerticalArrowNavigation(EXTENT));
360 pos = previousLinePosition(pos, m_affinity, xPosForVerticalArrowNavigation(EXTENT));
363 pos = VisiblePosition(selectionForLine(m_start, m_affinity).start());
365 case PARAGRAPH_BOUNDARY:
366 pos = startOfParagraph(VisiblePosition(m_start));
368 case DOCUMENT_BOUNDARY:
369 pos = VisiblePosition(m_start.node()->getDocument()->documentElement(), 0);
375 VisiblePosition Selection::modifyMovingLeftBackward(ETextGranularity granularity)
378 switch (granularity) {
381 pos = VisiblePosition(m_start);
383 pos = VisiblePosition(m_extent).previous();
386 pos = previousWordPosition(VisiblePosition(m_extent));
389 pos = previousParagraphPosition(VisiblePosition(m_start), m_affinity, xPosForVerticalArrowNavigation(START, isRange()));
392 pos = previousLinePosition(VisiblePosition(m_start), m_affinity, xPosForVerticalArrowNavigation(START, isRange()));
395 pos = VisiblePosition(selectionForLine(m_start, m_affinity).start());
397 case PARAGRAPH_BOUNDARY:
398 pos = startOfParagraph(VisiblePosition(m_start));
400 case DOCUMENT_BOUNDARY:
401 pos = VisiblePosition(m_start.node()->getDocument()->documentElement(), 0);
407 bool Selection::modify(EAlter alter, EDirection dir, ETextGranularity granularity)
409 setModifyBias(alter, dir);
414 // EDIT FIXME: These need to handle bidi
418 pos = modifyExtendingRightForward(granularity);
420 pos = modifyMovingRightForward(granularity);
425 pos = modifyExtendingLeftBackward(granularity);
427 pos = modifyMovingLeftBackward(granularity);
434 // Save and restore affinity here before calling setAffinity.
435 // The moveTo() and setExtent() calls reset affinity and this
436 // is undesirable here.
437 EAffinity savedAffinity = m_affinity;
440 moveTo(pos.deepEquivalent());
443 setExtent(pos.deepEquivalent());
446 m_affinity = savedAffinity;
447 modifyAffinity(alter, dir, granularity);
452 // FIXME: Maybe baseline would be better?
453 static bool caretY(const VisiblePosition &c, int &y)
455 Position p = c.deepEquivalent();
456 NodeImpl *n = p.node();
459 RenderObject *r = p.node()->renderer();
462 QRect rect = r->caretRect(p.offset());
465 y = rect.y() + rect.height() / 2;
469 bool Selection::modify(EAlter alter, int verticalDistance)
471 if (verticalDistance == 0)
474 bool up = verticalDistance < 0;
476 verticalDistance = -verticalDistance;
478 m_affinity = UPSTREAM;
479 setModifyBias(alter, up ? BACKWARD : FORWARD);
482 int xPos = 0; /* initialized only to make compiler happy */
486 pos = VisiblePosition(up ? m_start : m_end);
487 xPos = xPosForVerticalArrowNavigation(up ? START : END, isRange());
490 pos = VisiblePosition(m_extent);
491 xPos = xPosForVerticalArrowNavigation(EXTENT);
496 if (!caretY(pos, startY))
502 VisiblePosition result;
504 VisiblePosition next;
505 for (VisiblePosition p = pos; ; p = next) {
506 next = (up ? previousLinePosition : nextLinePosition)(p, m_affinity, xPos);
507 if (next.isNull() || next == p)
510 if (!caretY(next, nextY))
514 if (nextY - startY > verticalDistance)
516 if (nextY >= lastY) {
527 moveTo(result.deepEquivalent());
530 setExtent(result.deepEquivalent());
537 bool Selection::expandUsingGranularity(ETextGranularity granularity)
541 validate(granularity);
545 int Selection::xPosForVerticalArrowNavigation(EPositionType type, bool recalc) const
568 KHTMLPart *part = pos.node()->getDocument()->part();
572 if (recalc || part->xPosForVerticalArrowNavigation() == KHTMLPart::NoXPosForVerticalArrowNavigation) {
573 switch (m_affinity) {
575 pos = VisiblePosition(pos).downstreamDeepEquivalent();
578 pos = VisiblePosition(pos).deepEquivalent();
581 x = pos.node()->renderer()->caretRect(pos.offset(), m_affinity).x();
582 part->setXPosForVerticalArrowNavigation(x);
585 x = part->xPosForVerticalArrowNavigation();
590 void Selection::clear()
592 m_affinity = UPSTREAM;
598 void Selection::setBase(const Position &pos)
600 m_affinity = UPSTREAM;
605 void Selection::setExtent(const Position &pos)
607 m_affinity = UPSTREAM;
612 void Selection::setBaseAndExtent(const Position &base, const Position &extent)
614 m_affinity = UPSTREAM;
620 void Selection::setNeedsLayout(bool flag)
622 m_needsLayout = flag;
625 Range Selection::toRange() const
630 // Make sure we have an updated layout since this function is called
631 // in the course of running edit commands which modify the DOM.
632 // Failing to call this can result in equivalentXXXPosition calls returning
633 // incorrect results.
634 m_start.node()->getDocument()->updateLayout();
638 // If the selection is a caret, move the range start upstream. This helps us match
639 // the conventions of text editors tested, which make style determinations based
640 // on the character before the caret, if any.
641 s = m_start.upstream(StayInBlock).equivalentRangeCompliantPosition();
645 // If the selection is a range, select the minimum range that encompasses the selection.
646 // Again, this is to match the conventions of text editors tested, which make style
647 // determinations based on the first character of the selection.
648 // For instance, this operation helps to make sure that the "X" selected below is the
649 // only thing selected. The range should not be allowed to "leak" out to the end of the
650 // previous text node, or to the beginning of the next text node, each of which has a
653 // On a treasure map, <b>X</b> marks the spot.
657 s = m_start.downstream(StayInBlock);
658 e = m_end.upstream(StayInBlock);
659 if (RangeImpl::compareBoundaryPoints(s.node(), s.offset(), e.node(), e.offset()) > 0) {
660 // Make sure the start is before the end.
661 // The end can wind up before the start if collapsed whitespace is the only thing selected.
666 s = s.equivalentRangeCompliantPosition();
667 e = e.equivalentRangeCompliantPosition();
670 // Use this roundabout way of creating the Range in order to have defined behavior
671 // when there is a DOM exception.
672 int exceptionCode = 0;
673 Range result(s.node()->getDocument());
674 RangeImpl *handle = result.handle();
676 handle->setStart(s.node(), s.offset(), exceptionCode);
678 ERROR("Exception setting Range start from Selection: %d", exceptionCode);
681 handle->setEnd(e.node(), e.offset(), exceptionCode);
683 ERROR("Exception setting Range end from Selection: %d", exceptionCode);
689 void Selection::layout()
691 if (isNone() || !m_start.node()->inDocument() || !m_end.node()->inDocument()) {
692 m_caretRect = QRect();
693 m_expectedVisibleRect = QRect();
697 m_start.node()->getDocument()->updateRendering();
700 Position pos = m_start;
701 switch (m_affinity) {
703 pos = VisiblePosition(m_start).downstreamDeepEquivalent();
706 pos = VisiblePosition(m_start).deepEquivalent();
709 if (pos.isNotNull()) {
710 ASSERT(pos.node()->renderer());
711 m_caretRect = pos.node()->renderer()->caretRect(pos.offset(), m_affinity);
712 m_expectedVisibleRect = m_caretRect;
715 m_caretRect = QRect();
716 m_expectedVisibleRect = QRect();
720 // Calculate which position to use based on whether the base is the start.
721 // We want the position, start or end, that was calculated using the extent.
722 // This makes the selection follow the extent position while scrolling as a
723 // result of arrow navigation.
725 // Note: no need to get additional help from VisiblePosition. The m_start and
726 // m_end positions should already be visible, and we're only interested in
727 // a rectangle for m_expectedVisibleRect, hence affinity is not a factor
728 // like it is when drawing a caret.
730 Position pos = m_baseIsStart ? m_end : m_start;
731 ASSERT(pos.node()->renderer());
732 m_expectedVisibleRect = pos.node()->renderer()->caretRect(pos.offset(), m_affinity);
733 m_caretRect = QRect();
736 m_needsLayout = false;
739 QRect Selection::caretRect() const
742 const_cast<Selection *>(this)->layout();
748 QRect Selection::expectedVisibleRect() const
751 const_cast<Selection *>(this)->layout();
754 return m_expectedVisibleRect;
757 QRect Selection::caretRepaintRect() const
759 // FIXME: Add one pixel of slop on each side to make sure we don't leave behind artifacts.
760 QRect r = caretRect();
763 return QRect(r.left() - 1, r.top() - 1, r.width() + 2, r.height() + 2);
766 void Selection::needsCaretRepaint()
771 if (!m_start.node()->getDocument())
774 KHTMLView *v = m_start.node()->getDocument()->view();
779 // repaint old position and calculate new position
780 v->updateContents(caretRepaintRect(), false);
783 // EDIT FIXME: This is an unfortunate hack.
784 // Basically, we can't trust this layout position since we
785 // can't guarantee that the check to see if we are in unrendered
786 // content will work at this point. We may have to wait for
787 // a layout and re-render of the document to happen. So, resetting this
788 // flag will cause another caret layout to happen the first time
789 // that we try to paint the caret after this call. That one will work since
790 // it happens after the document has accounted for any editing
791 // changes which may have been done.
792 // And, we need to leave this layout here so the caret moves right
793 // away after clicking.
794 m_needsLayout = true;
796 v->updateContents(caretRepaintRect(), false);
799 void Selection::paintCaret(QPainter *p, const QRect &rect)
801 if (m_state != CARET)
807 if (m_caretRect.isValid())
808 p->fillRect(m_caretRect & rect, QBrush());
811 void Selection::validate(ETextGranularity granularity)
813 // Move the selection to rendered positions, if possible.
814 Position originalBase(m_base);
815 bool baseAndExtentEqual = m_base == m_extent;
816 bool updatedLayout = false;
817 if (m_base.isNotNull()) {
818 m_base.node()->getDocument()->updateLayout();
819 updatedLayout = true;
820 m_base = VisiblePosition(m_base).deepEquivalent();
821 if (baseAndExtentEqual)
824 if (m_extent.isNotNull() && !baseAndExtentEqual) {
826 m_extent.node()->getDocument()->updateLayout();
827 m_extent = VisiblePosition(m_extent).deepEquivalent();
830 // Make sure we do not have a dangling start or end
831 if (m_base.isNull() && m_extent.isNull()) {
832 // Move the position to the enclosingBlockFlowElement of the original base, if possible.
833 // This has the effect of flashing the caret somewhere when a rendered position for
834 // the base and extent cannot be found.
835 if (originalBase.isNotNull()) {
836 Position pos(originalBase.node()->enclosingBlockFlowElement(), 0);
841 // We have no position to work with. See if the BODY element of the page
842 // is contentEditable. If it is, put the caret there.
843 //NodeImpl *node = document()
847 m_baseIsStart = true;
849 else if (m_base.isNull()) {
851 m_baseIsStart = true;
853 else if (m_extent.isNull()) {
855 m_baseIsStart = true;
858 m_baseIsStart = RangeImpl::compareBoundaryPoints(m_base.node(), m_base.offset(), m_extent.node(), m_extent.offset()) <= 0;
864 // calculate the correct start and end positions
865 switch (granularity) {
877 m_end = endOfWord(VisiblePosition(m_extent)).deepEquivalent();
878 // If at the end of the document, expand to the left.
879 EWordSide side = (m_end == m_extent) ? LeftWordIfOnBoundary : RightWordIfOnBoundary;
880 m_start = startOfWord(VisiblePosition(m_base), side).deepEquivalent();
882 m_start = startOfWord(VisiblePosition(m_extent)).deepEquivalent();
883 m_end = endOfWord(VisiblePosition(m_base)).deepEquivalent();
887 case LINE_BOUNDARY: {
888 Selection baseSelection = *this;
889 Selection extentSelection = *this;
890 Selection baseLine = selectionForLine(m_base, m_affinity);
891 if (baseLine.isCaretOrRange()) {
892 baseSelection = baseLine;
894 Selection extentLine = selectionForLine(m_extent, m_affinity);
895 if (extentLine.isCaretOrRange()) {
896 extentSelection = extentLine;
899 m_start = baseSelection.m_start;
900 m_end = extentSelection.m_end;
902 m_start = extentSelection.m_start;
903 m_end = baseSelection.m_end;
909 m_start = startOfParagraph(VisiblePosition(m_base)).deepEquivalent();
910 m_end = endOfParagraph(VisiblePosition(m_extent), IncludeLineBreak).deepEquivalent();
912 m_start = startOfParagraph(VisiblePosition(m_extent)).deepEquivalent();
913 m_end = endOfParagraph(VisiblePosition(m_base), IncludeLineBreak).deepEquivalent();
916 case DOCUMENT_BOUNDARY: {
917 NodeImpl *de = m_start.node()->getDocument()->documentElement();
918 m_start = VisiblePosition(de, 0).deepEquivalent();
919 m_end = VisiblePosition(de, de ? de->childNodeCount() : 0).deepEquivalent();
922 case PARAGRAPH_BOUNDARY:
924 m_start = startOfParagraph(VisiblePosition(m_base)).deepEquivalent();
925 m_end = endOfParagraph(VisiblePosition(m_extent)).deepEquivalent();
927 m_start = startOfParagraph(VisiblePosition(m_extent)).deepEquivalent();
928 m_end = endOfParagraph(VisiblePosition(m_base)).deepEquivalent();
934 if (m_start.isNull()) {
935 ASSERT(m_end.isNull());
938 else if (m_start == m_end || m_start.upstream(StayInBlock) == m_end.upstream(StayInBlock)) {
943 // "Constrain" the selection to be the smallest equivalent range of nodes.
944 // This is a somewhat arbitrary choice, but experience shows that it is
945 // useful to make to make the selection "canonical" (if only for
946 // purposes of comparing selections). This is an ideal point of the code
947 // to do this operation, since all selection changes that result in a RANGE
948 // come through here before anyone uses it.
949 m_start = m_start.downstream(StayInBlock);
950 m_end = m_end.upstream(StayInBlock);
953 m_needsLayout = true;
960 static Position startOfFirstRunAt(RenderObject *renderNode, int y)
962 for (RenderObject *n = renderNode; n; n = n->nextSibling()) {
964 RenderText *textRenderer = static_cast<RenderText *>(n);
965 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
967 n->absolutePosition(absx, absy);
968 int top = absy + box->root()->topOverflow();
970 return Position(textRenderer->element(), box->m_start);
974 Position position = startOfFirstRunAt(n->firstChild(), y);
975 if (position.isNotNull())
982 static Position endOfLastRunAt(RenderObject *renderNode, int y)
984 RenderObject *n = renderNode;
987 if (RenderObject *parent = n->parent())
988 n = parent->lastChild();
991 Position position = endOfLastRunAt(n->firstChild(), y);
992 if (position.isNotNull())
995 if (n->isText() && !n->isBR()) {
996 RenderText *textRenderer = static_cast<RenderText *>(n);
997 for (InlineTextBox* box = textRenderer->lastTextBox(); box; box = box->prevTextBox()) {
999 n->absolutePosition(absx, absy);
1000 int top = absy + box->root()->topOverflow();
1002 return Position(textRenderer->element(), box->m_start + box->m_len);
1006 if (n == renderNode)
1009 n = n->previousSibling();
1013 static Selection selectionForLine(const Position &position, EAffinity affinity)
1015 NodeImpl *node = position.node();
1016 if (!node || !node->renderer())
1019 QRect rect = node->renderer()->caretRect(position.offset(), affinity);
1020 int selectionPointY = rect.y();
1022 // Go up to first non-inline element.
1023 RenderObject *renderNode = node->renderer();
1024 while (renderNode && renderNode->isInline())
1025 renderNode = renderNode->parent();
1026 renderNode = renderNode->firstChild();
1028 // Look for all the first child in the block that is on the same line
1029 // as the selection point.
1030 Position start = startOfFirstRunAt(renderNode, selectionPointY);
1034 // Look for all the last child in the block that is on the same line
1035 // as the selection point.
1036 Position end = endOfLastRunAt(renderNode, selectionPointY);
1040 return Selection(start, end);
1043 void Selection::debugRenderer(RenderObject *r, bool selected) const
1045 if (r->node()->isElementNode()) {
1046 ElementImpl *element = static_cast<ElementImpl *>(r->node());
1047 fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->tagName().string().latin1());
1049 else if (r->isText()) {
1050 RenderText *textRenderer = static_cast<RenderText *>(r);
1051 if (textRenderer->stringLength() == 0 || !textRenderer->firstTextBox()) {
1052 fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
1056 static const int max = 36;
1057 QString text = DOMString(textRenderer->string()).string();
1058 int textLength = text.length();
1061 if (r->node() == m_start.node())
1062 offset = m_start.offset();
1063 else if (r->node() == m_end.node())
1064 offset = m_end.offset();
1067 InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
1068 text = text.mid(box->m_start, box->m_len);
1074 // text is shorter than max
1075 if (textLength < max) {
1080 // too few characters to left
1081 else if (pos - mid < 0) {
1082 show = text.left(max - 3) + "...";
1086 // enough characters on each side
1087 else if (pos - mid >= 0 && pos + mid <= textLength) {
1088 show = "..." + text.mid(pos - mid + 3, max - 6) + "...";
1092 // too few characters on right
1094 show = "..." + text.right(max - 3);
1095 caret = pos - (textLength - show.length());
1098 show.replace('\n', ' ');
1099 show.replace('\r', ' ');
1100 fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.latin1(), pos);
1101 fprintf(stderr, " ");
1102 for (int i = 0; i < caret; i++)
1103 fprintf(stderr, " ");
1104 fprintf(stderr, "^\n");
1107 if ((int)text.length() > max)
1108 text = text.left(max - 3) + "...";
1110 text = text.left(max);
1111 fprintf(stderr, " #text : \"%s\"\n", text.latin1());
1116 void Selection::debugPosition() const
1118 if (!m_start.node())
1121 //static int context = 5;
1123 //RenderObject *r = 0;
1125 fprintf(stderr, "Selection =================\n");
1127 if (m_start == m_end) {
1128 Position pos = m_start;
1129 Position upstream = pos.upstream();
1130 Position downstream = pos.downstream();
1131 fprintf(stderr, "upstream: %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
1132 fprintf(stderr, "pos: %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
1133 fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
1136 Position pos = m_start;
1137 Position upstream = pos.upstream();
1138 Position downstream = pos.downstream();
1139 fprintf(stderr, "upstream: %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
1140 fprintf(stderr, "start: %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
1141 fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
1142 fprintf(stderr, "-----------------------------------\n");
1144 upstream = pos.upstream();
1145 downstream = pos.downstream();
1146 fprintf(stderr, "upstream: %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
1147 fprintf(stderr, "end: %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
1148 fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
1149 fprintf(stderr, "-----------------------------------\n");
1154 r = m_start.node()->renderer();
1155 for (int i = 0; i < context; i++, back++) {
1156 if (r->previousRenderer())
1157 r = r->previousRenderer();
1161 for (int i = 0; i < back; i++) {
1162 debugRenderer(r, false);
1163 r = r->nextRenderer();
1167 fprintf(stderr, "\n");
1169 if (m_start.node() == m_end.node())
1170 debugRenderer(m_start.node()->renderer(), true);
1172 for (r = m_start.node()->renderer(); r && r != m_end.node()->renderer(); r = r->nextRenderer())
1173 debugRenderer(r, true);
1175 fprintf(stderr, "\n");
1177 r = m_end.node()->renderer();
1178 for (int i = 0; i < context; i++) {
1179 if (r->nextRenderer()) {
1180 r = r->nextRenderer();
1181 debugRenderer(r, false);
1188 fprintf(stderr, "================================\n");
1192 #define FormatBufferSize 1024
1193 void Selection::formatForDebugger(char *buffer, unsigned length) const
1202 char s[FormatBufferSize];
1204 m_start.formatForDebugger(s, FormatBufferSize);
1207 m_end.formatForDebugger(s, FormatBufferSize);
1211 strncpy(buffer, result.string().latin1(), length - 1);
1213 #undef FormatBufferSize