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_textimpl.h"
46 #include "xml/dom2_rangeimpl.h"
49 #include "KWQAssertions.h"
51 #define ASSERT(assertion) assert(assertion)
57 using DOM::ElementImpl;
63 using DOM::StayInBlock;
67 Selection::Selection()
72 Selection::Selection(const Position &pos, EAffinity affinity)
73 : m_base(pos), m_extent(pos)
79 Selection::Selection(const Range &r, EAffinity baseAffinity, EAffinity extentAffinity)
80 : m_base(startPosition(r)), m_extent(endPosition(r))
86 Selection::Selection(const Position &base, EAffinity baseAffinity, const Position &extent, EAffinity extentAffinity)
87 : m_base(base), m_extent(extent)
93 Selection::Selection(const VisiblePosition &visiblePos)
94 : m_base(visiblePos.position()), m_extent(visiblePos.position())
96 init(visiblePos.affinity());
100 Selection::Selection(const VisiblePosition &base, const VisiblePosition &extent)
101 : m_base(base.position()), m_extent(extent.position())
103 init(base.affinity());
107 Selection::Selection(const Selection &o)
108 : m_base(o.m_base), m_extent(o.m_extent)
109 , m_start(o.m_start), m_end(o.m_end)
110 , m_state(o.m_state), m_affinity(o.m_affinity)
111 , m_baseIsStart(o.m_baseIsStart)
112 , m_needsLayout(o.m_needsLayout)
113 , m_modifyBiasSet(o.m_modifyBiasSet)
115 // Only copy the coordinates over if the other object
116 // has had a layout, otherwise keep the current
117 // coordinates. This prevents drawing artifacts from
118 // remaining when the caret is painted and then moves,
119 // and the old rectangle needs to be repainted.
120 if (!m_needsLayout) {
121 m_caretRect = o.m_caretRect;
122 m_expectedVisibleRect = o.m_expectedVisibleRect;
126 void Selection::init(EAffinity affinity)
128 // FIXME: set extentAffinity
130 m_baseIsStart = true;
131 m_affinity = affinity;
132 m_needsLayout = true;
133 m_modifyBiasSet = false;
136 Selection &Selection::operator=(const Selection &o)
139 m_extent = o.m_extent;
144 m_affinity = o.m_affinity;
146 m_baseIsStart = o.m_baseIsStart;
147 m_needsLayout = o.m_needsLayout;
148 m_modifyBiasSet = o.m_modifyBiasSet;
150 // Only copy the coordinates over if the other object
151 // has had a layout, otherwise keep the current
152 // coordinates. This prevents drawing artifacts from
153 // remaining when the caret is painted and then moves,
154 // and the old rectangle needs to be repainted.
155 if (!m_needsLayout) {
156 m_caretRect = o.m_caretRect;
157 m_expectedVisibleRect = o.m_expectedVisibleRect;
163 void Selection::moveTo(const VisiblePosition &pos)
165 // FIXME: use extentAffinity
166 m_affinity = pos.affinity();
167 m_base = pos.deepEquivalent();
168 m_extent = pos.deepEquivalent();
172 void Selection::moveTo(const VisiblePosition &base, const VisiblePosition &extent)
174 // FIXME: use extentAffinity
175 m_affinity = base.affinity();
176 m_base = base.deepEquivalent();
177 m_extent = extent.deepEquivalent();
181 void Selection::moveTo(const Selection &o)
183 // FIXME: copy extentAffinity
184 m_affinity = o.m_affinity;
190 void Selection::moveTo(const Position &pos, EAffinity affinity)
192 // FIXME: use extentAffinity
193 m_affinity = affinity;
199 void Selection::moveTo(const Range &r, EAffinity baseAffinity, EAffinity extentAffinity)
201 // FIXME: use extentAffinity
202 m_affinity = baseAffinity;
203 m_base = startPosition(r);
204 m_extent = endPosition(r);
208 void Selection::moveTo(const Position &base, EAffinity baseAffinity, const Position &extent, EAffinity extentAffinity)
210 // FIXME: use extentAffinity
211 m_affinity = baseAffinity;
217 void Selection::setModifyBias(EAlter alter, EDirection direction)
221 m_modifyBiasSet = false;
224 if (!m_modifyBiasSet) {
225 m_modifyBiasSet = true;
227 // FIXME: right for bidi?
244 VisiblePosition Selection::modifyExtendingRightForward(ETextGranularity granularity)
246 VisiblePosition pos(m_extent, m_affinity);
247 switch (granularity) {
252 pos = nextWordPosition(pos);
255 pos = nextParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
258 pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
261 pos = endOfLine(VisiblePosition(m_end, m_affinity));
263 case PARAGRAPH_BOUNDARY:
264 pos = endOfParagraph(VisiblePosition(m_end, m_affinity));
266 case DOCUMENT_BOUNDARY:
267 pos = endOfDocument(pos);
274 VisiblePosition Selection::modifyMovingRightForward(ETextGranularity granularity)
277 switch (granularity) {
280 pos = VisiblePosition(m_end, m_affinity);
282 pos = VisiblePosition(m_extent, m_affinity).next();
285 pos = nextWordPosition(VisiblePosition(m_extent, m_affinity));
288 pos = nextParagraphPosition(VisiblePosition(m_end, m_affinity), xPosForVerticalArrowNavigation(END, isRange()));
291 // down-arrowing from a range selection that ends at the start of a line needs
292 // to leave the selection at that line start (no need to call nextLinePosition!)
293 pos = VisiblePosition(m_end, m_affinity);
294 if (!isRange() || !isStartOfLine(pos))
295 pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(END, isRange()));
299 pos = endOfLine(VisiblePosition(m_end, m_affinity));
301 case PARAGRAPH_BOUNDARY:
302 pos = endOfParagraph(VisiblePosition(m_end, m_affinity));
304 case DOCUMENT_BOUNDARY:
305 pos = endOfDocument(VisiblePosition(m_end, m_affinity));
311 VisiblePosition Selection::modifyExtendingLeftBackward(ETextGranularity granularity)
313 VisiblePosition pos(m_extent, m_affinity);
314 switch (granularity) {
316 pos = pos.previous();
319 pos = previousWordPosition(pos);
322 pos = previousParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
325 pos = previousLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
328 pos = startOfLine(VisiblePosition(m_start, m_affinity));
330 case PARAGRAPH_BOUNDARY:
331 pos = startOfParagraph(VisiblePosition(m_start, m_affinity));
333 case DOCUMENT_BOUNDARY:
334 pos = startOfDocument(pos);
340 VisiblePosition Selection::modifyMovingLeftBackward(ETextGranularity granularity)
343 switch (granularity) {
346 pos = VisiblePosition(m_start, m_affinity);
348 pos = VisiblePosition(m_extent, m_affinity).previous();
351 pos = previousWordPosition(VisiblePosition(m_extent, m_affinity));
354 pos = previousParagraphPosition(VisiblePosition(m_start, m_affinity), xPosForVerticalArrowNavigation(START, isRange()));
357 pos = previousLinePosition(VisiblePosition(m_start, m_affinity), xPosForVerticalArrowNavigation(START, isRange()));
360 pos = startOfLine(VisiblePosition(m_start, m_affinity));
362 case PARAGRAPH_BOUNDARY:
363 pos = startOfParagraph(VisiblePosition(m_start, m_affinity));
365 case DOCUMENT_BOUNDARY:
366 pos = startOfDocument(VisiblePosition(m_start, m_affinity));
372 bool Selection::modify(EAlter alter, EDirection dir, ETextGranularity granularity)
374 setModifyBias(alter, dir);
378 // EDIT FIXME: These need to handle bidi
382 pos = modifyExtendingRightForward(granularity);
384 pos = modifyMovingRightForward(granularity);
389 pos = modifyExtendingLeftBackward(granularity);
391 pos = modifyMovingLeftBackward(granularity);
412 // FIXME: Maybe baseline would be better?
413 static bool caretY(const VisiblePosition &c, int &y)
415 Position p = c.deepEquivalent();
416 NodeImpl *n = p.node();
419 RenderObject *r = p.node()->renderer();
422 QRect rect = r->caretRect(p.offset());
425 y = rect.y() + rect.height() / 2;
429 bool Selection::modify(EAlter alter, int verticalDistance)
431 if (verticalDistance == 0)
434 bool up = verticalDistance < 0;
436 verticalDistance = -verticalDistance;
438 // can dump this UPSTREAM when we have m_extentAffinity
439 m_affinity = UPSTREAM;
440 setModifyBias(alter, up ? BACKWARD : FORWARD);
446 pos = VisiblePosition(up ? m_start : m_end, m_affinity);
447 xPos = xPosForVerticalArrowNavigation(up ? START : END, isRange());
450 pos = VisiblePosition(m_extent, m_affinity);
451 xPos = xPosForVerticalArrowNavigation(EXTENT);
456 if (!caretY(pos, startY))
462 VisiblePosition result;
463 VisiblePosition next;
464 for (VisiblePosition p = pos; ; p = next) {
465 next = (up ? previousLinePosition : nextLinePosition)(p, xPos);
466 if (next.isNull() || next == p)
469 if (!caretY(next, nextY))
473 if (nextY - startY > verticalDistance)
475 if (nextY >= lastY) {
496 bool Selection::expandUsingGranularity(ETextGranularity granularity)
500 validate(granularity);
504 int Selection::xPosForVerticalArrowNavigation(EPositionType type, bool recalc) const
527 KHTMLPart *part = pos.node()->getDocument()->part();
531 if (recalc || part->xPosForVerticalArrowNavigation() == KHTMLPart::NoXPosForVerticalArrowNavigation) {
532 switch (m_affinity) {
534 pos = VisiblePosition(pos, m_affinity).downstreamDeepEquivalent();
537 pos = VisiblePosition(pos, m_affinity).deepEquivalent();
540 x = pos.node()->renderer()->caretRect(pos.offset(), m_affinity).x();
541 part->setXPosForVerticalArrowNavigation(x);
544 x = part->xPosForVerticalArrowNavigation();
549 void Selection::clear()
551 m_affinity = SEL_DEFAULT_AFFINITY;
557 void Selection::setBase(const VisiblePosition &pos)
559 m_affinity = pos.affinity();
560 m_base = pos.deepEquivalent();
564 void Selection::setExtent(const VisiblePosition &pos)
566 // FIXME: Support extentAffinity
567 m_extent = pos.deepEquivalent();
571 void Selection::setBaseAndExtent(const VisiblePosition &base, const VisiblePosition &extent)
573 // FIXME: Support extentAffinity
574 m_affinity = base.affinity();
575 m_base = base.deepEquivalent();
576 m_extent = extent.deepEquivalent();
581 void Selection::setBase(const Position &pos, EAffinity baseAffinity)
583 m_affinity = baseAffinity;
588 void Selection::setExtent(const Position &pos, EAffinity extentAffinity)
590 // FIXME: Support extentAffinity for real
591 m_affinity = extentAffinity;
596 void Selection::setBaseAndExtent(const Position &base, EAffinity baseAffinity, const Position &extent, EAffinity extentAffinity)
598 // FIXME: extentAffinity
599 m_affinity = baseAffinity;
605 void Selection::setNeedsLayout(bool flag)
607 m_needsLayout = flag;
610 Range Selection::toRange() const
615 // Make sure we have an updated layout since this function is called
616 // in the course of running edit commands which modify the DOM.
617 // Failing to call this can result in equivalentXXXPosition calls returning
618 // incorrect results.
619 m_start.node()->getDocument()->updateLayout();
623 // If the selection is a caret, move the range start upstream. This helps us match
624 // the conventions of text editors tested, which make style determinations based
625 // on the character before the caret, if any.
626 s = m_start.upstream(StayInBlock).equivalentRangeCompliantPosition();
630 // If the selection is a range, select the minimum range that encompasses the selection.
631 // Again, this is to match the conventions of text editors tested, which make style
632 // determinations based on the first character of the selection.
633 // For instance, this operation helps to make sure that the "X" selected below is the
634 // only thing selected. The range should not be allowed to "leak" out to the end of the
635 // previous text node, or to the beginning of the next text node, each of which has a
638 // On a treasure map, <b>X</b> marks the spot.
642 s = m_start.downstream(StayInBlock);
643 e = m_end.upstream(StayInBlock);
644 if (RangeImpl::compareBoundaryPoints(s.node(), s.offset(), e.node(), e.offset()) > 0) {
645 // Make sure the start is before the end.
646 // The end can wind up before the start if collapsed whitespace is the only thing selected.
651 s = s.equivalentRangeCompliantPosition();
652 e = e.equivalentRangeCompliantPosition();
655 // Use this roundabout way of creating the Range in order to have defined behavior
656 // when there is a DOM exception.
657 int exceptionCode = 0;
658 Range result(s.node()->getDocument());
659 RangeImpl *handle = result.handle();
661 handle->setStart(s.node(), s.offset(), exceptionCode);
663 ERROR("Exception setting Range start from Selection: %d", exceptionCode);
666 handle->setEnd(e.node(), e.offset(), exceptionCode);
668 ERROR("Exception setting Range end from Selection: %d", exceptionCode);
674 void Selection::layout()
676 if (isNone() || !m_start.node()->inDocument() || !m_end.node()->inDocument()) {
677 m_caretRect = QRect();
678 m_expectedVisibleRect = QRect();
682 m_start.node()->getDocument()->updateRendering();
685 Position pos = m_start;
686 switch (m_affinity) {
688 pos = VisiblePosition(m_start, m_affinity).downstreamDeepEquivalent();
691 pos = VisiblePosition(m_start, m_affinity).deepEquivalent();
694 if (pos.isNotNull()) {
695 ASSERT(pos.node()->renderer());
696 m_caretRect = pos.node()->renderer()->caretRect(pos.offset(), m_affinity);
697 m_expectedVisibleRect = m_caretRect;
700 m_caretRect = QRect();
701 m_expectedVisibleRect = QRect();
705 // Calculate which position to use based on whether the base is the start.
706 // We want the position, start or end, that was calculated using the extent.
707 // This makes the selection follow the extent position while scrolling as a
708 // result of arrow navigation.
710 // Note: no need to get additional help from VisiblePosition. The m_start and
711 // m_end positions should already be visible, and we're only interested in
712 // a rectangle for m_expectedVisibleRect, hence affinity is not a factor
713 // like it is when drawing a caret.
715 Position pos = m_baseIsStart ? m_end : m_start;
716 ASSERT(pos.node()->renderer());
717 m_expectedVisibleRect = pos.node()->renderer()->caretRect(pos.offset(), m_affinity);
718 m_caretRect = QRect();
721 m_needsLayout = false;
724 QRect Selection::caretRect() const
727 const_cast<Selection *>(this)->layout();
733 QRect Selection::expectedVisibleRect() const
736 const_cast<Selection *>(this)->layout();
739 return m_expectedVisibleRect;
742 QRect Selection::caretRepaintRect() const
744 // FIXME: Add one pixel of slop on each side to make sure we don't leave behind artifacts.
745 QRect r = caretRect();
748 return QRect(r.left() - 1, r.top() - 1, r.width() + 2, r.height() + 2);
751 void Selection::needsCaretRepaint()
756 if (!m_start.node()->getDocument())
759 KHTMLView *v = m_start.node()->getDocument()->view();
764 // repaint old position and calculate new position
765 v->updateContents(caretRepaintRect(), false);
768 // EDIT FIXME: This is an unfortunate hack.
769 // Basically, we can't trust this layout position since we
770 // can't guarantee that the check to see if we are in unrendered
771 // content will work at this point. We may have to wait for
772 // a layout and re-render of the document to happen. So, resetting this
773 // flag will cause another caret layout to happen the first time
774 // that we try to paint the caret after this call. That one will work since
775 // it happens after the document has accounted for any editing
776 // changes which may have been done.
777 // And, we need to leave this layout here so the caret moves right
778 // away after clicking.
779 m_needsLayout = true;
781 v->updateContents(caretRepaintRect(), false);
784 void Selection::paintCaret(QPainter *p, const QRect &rect)
786 if (m_state != CARET)
792 if (m_caretRect.isValid())
793 p->fillRect(m_caretRect & rect, QBrush());
796 void Selection::validate(ETextGranularity granularity)
798 // Move the selection to rendered positions, if possible.
799 Position originalBase(m_base);
800 bool baseAndExtentEqual = m_base == m_extent;
801 bool updatedLayout = false;
802 if (m_base.isNotNull()) {
803 m_base.node()->getDocument()->updateLayout();
804 updatedLayout = true;
805 m_base = VisiblePosition(m_base, m_affinity).deepEquivalent();
806 if (baseAndExtentEqual)
809 if (m_extent.isNotNull() && !baseAndExtentEqual) {
811 m_extent.node()->getDocument()->updateLayout();
812 m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent();
815 // Make sure we do not have a dangling start or end
816 if (m_base.isNull() && m_extent.isNull()) {
817 // Move the position to the enclosingBlockFlowElement of the original base, if possible.
818 // This has the effect of flashing the caret somewhere when a rendered position for
819 // the base and extent cannot be found.
820 if (originalBase.isNotNull()) {
821 Position pos(originalBase.node()->enclosingBlockFlowElement(), 0);
826 // We have no position to work with. See if the BODY element of the page
827 // is contentEditable. If it is, put the caret there.
828 //NodeImpl *node = document()
832 m_baseIsStart = true;
834 else if (m_base.isNull()) {
836 m_baseIsStart = true;
838 else if (m_extent.isNull()) {
840 m_baseIsStart = true;
843 m_baseIsStart = RangeImpl::compareBoundaryPoints(m_base.node(), m_base.offset(), m_extent.node(), m_extent.offset()) <= 0;
849 // calculate the correct start and end positions
850 switch (granularity) {
861 // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary).
862 // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in
863 // the document, select that last word (LeftWordIfOnBoundary).
864 // Edge case: If the caret is after the last word in a paragraph, select from the the end of the
865 // last word to the line break (also RightWordIfOnBoundary);
866 VisiblePosition start = m_baseIsStart ? VisiblePosition(m_base, m_affinity) : VisiblePosition(m_extent, m_affinity);
867 VisiblePosition end = m_baseIsStart ? VisiblePosition(m_extent, m_affinity) : VisiblePosition(m_base, m_affinity);
868 EWordSide side = RightWordIfOnBoundary;
869 if (isEndOfDocument(start) || (isEndOfLine(start) && !isStartOfLine(start) && !isEndOfParagraph(start)))
870 side = LeftWordIfOnBoundary;
871 m_start = startOfWord(start, side).deepEquivalent();
872 side = RightWordIfOnBoundary;
873 if (isEndOfDocument(end) || (isEndOfLine(end) && !isStartOfLine(end) && !isEndOfParagraph(end)))
874 side = LeftWordIfOnBoundary;
875 m_end = endOfWord(end, side).deepEquivalent();
882 m_start = startOfLine(VisiblePosition(m_base, m_affinity)).deepEquivalent();
883 m_end = endOfLine(VisiblePosition(m_extent, m_affinity), IncludeLineBreak).deepEquivalent();
885 m_start = startOfLine(VisiblePosition(m_extent, m_affinity)).deepEquivalent();
886 m_end = endOfLine(VisiblePosition(m_base, m_affinity), IncludeLineBreak).deepEquivalent();
891 VisiblePosition pos(m_base, m_affinity);
892 if (isStartOfLine(pos) && isEndOfDocument(pos))
893 pos = pos.previous();
894 m_start = startOfParagraph(pos).deepEquivalent();
895 m_end = endOfParagraph(VisiblePosition(m_extent, m_affinity), IncludeLineBreak).deepEquivalent();
897 m_start = startOfParagraph(VisiblePosition(m_extent, m_affinity)).deepEquivalent();
898 m_end = endOfParagraph(VisiblePosition(m_base, m_affinity), IncludeLineBreak).deepEquivalent();
901 case DOCUMENT_BOUNDARY:
902 m_start = startOfDocument(VisiblePosition(m_base, m_affinity)).deepEquivalent();
903 m_end = endOfDocument(VisiblePosition(m_base, m_affinity)).deepEquivalent();
905 case PARAGRAPH_BOUNDARY:
907 m_start = startOfParagraph(VisiblePosition(m_base, m_affinity)).deepEquivalent();
908 m_end = endOfParagraph(VisiblePosition(m_extent, m_affinity)).deepEquivalent();
910 m_start = startOfParagraph(VisiblePosition(m_extent, m_affinity)).deepEquivalent();
911 m_end = endOfParagraph(VisiblePosition(m_base, m_affinity)).deepEquivalent();
917 if (m_start.isNull()) {
918 ASSERT(m_end.isNull());
921 else if (m_start == m_end || m_start.upstream(StayInBlock) == m_end.upstream(StayInBlock)) {
926 // "Constrain" the selection to be the smallest equivalent range of nodes.
927 // This is a somewhat arbitrary choice, but experience shows that it is
928 // useful to make to make the selection "canonical" (if only for
929 // purposes of comparing selections). This is an ideal point of the code
930 // to do this operation, since all selection changes that result in a RANGE
931 // come through here before anyone uses it.
932 m_start = m_start.downstream(StayInBlock);
933 m_end = m_end.upstream(StayInBlock);
936 m_needsLayout = true;
943 void Selection::debugRenderer(RenderObject *r, bool selected) const
945 if (r->node()->isElementNode()) {
946 ElementImpl *element = static_cast<ElementImpl *>(r->node());
947 fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->tagName().string().latin1());
949 else if (r->isText()) {
950 RenderText *textRenderer = static_cast<RenderText *>(r);
951 if (textRenderer->stringLength() == 0 || !textRenderer->firstTextBox()) {
952 fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
956 static const int max = 36;
957 QString text = DOMString(textRenderer->string()).string();
958 int textLength = text.length();
961 if (r->node() == m_start.node())
962 offset = m_start.offset();
963 else if (r->node() == m_end.node())
964 offset = m_end.offset();
967 InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
968 text = text.mid(box->m_start, box->m_len);
974 // text is shorter than max
975 if (textLength < max) {
980 // too few characters to left
981 else if (pos - mid < 0) {
982 show = text.left(max - 3) + "...";
986 // enough characters on each side
987 else if (pos - mid >= 0 && pos + mid <= textLength) {
988 show = "..." + text.mid(pos - mid + 3, max - 6) + "...";
992 // too few characters on right
994 show = "..." + text.right(max - 3);
995 caret = pos - (textLength - show.length());
998 show.replace('\n', ' ');
999 show.replace('\r', ' ');
1000 fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.latin1(), pos);
1001 fprintf(stderr, " ");
1002 for (int i = 0; i < caret; i++)
1003 fprintf(stderr, " ");
1004 fprintf(stderr, "^\n");
1007 if ((int)text.length() > max)
1008 text = text.left(max - 3) + "...";
1010 text = text.left(max);
1011 fprintf(stderr, " #text : \"%s\"\n", text.latin1());
1016 void Selection::debugPosition() const
1018 if (!m_start.node())
1021 //static int context = 5;
1023 //RenderObject *r = 0;
1025 fprintf(stderr, "Selection =================\n");
1027 if (m_start == m_end) {
1028 Position pos = m_start;
1029 Position upstream = pos.upstream();
1030 Position downstream = pos.downstream();
1031 fprintf(stderr, "upstream: %s %p:%ld\n", upstream.node()->nodeName().string().latin1(), upstream.node(), upstream.offset());
1032 fprintf(stderr, "pos: %s %p:%ld\n", pos.node()->nodeName().string().latin1(), pos.node(), pos.offset());
1033 fprintf(stderr, "downstream: %s %p:%ld\n", downstream.node()->nodeName().string().latin1(), downstream.node(), downstream.offset());
1036 Position pos = m_start;
1037 Position upstream = pos.upstream();
1038 Position downstream = pos.downstream();
1039 fprintf(stderr, "upstream: %s %p:%ld\n", upstream.node()->nodeName().string().latin1(), upstream.node(), upstream.offset());
1040 fprintf(stderr, "start: %s %p:%ld\n", pos.node()->nodeName().string().latin1(), pos.node(), pos.offset());
1041 fprintf(stderr, "downstream: %s %p:%ld\n", downstream.node()->nodeName().string().latin1(), downstream.node(), downstream.offset());
1042 fprintf(stderr, "-----------------------------------\n");
1044 upstream = pos.upstream();
1045 downstream = pos.downstream();
1046 fprintf(stderr, "upstream: %s %p:%ld\n", upstream.node()->nodeName().string().latin1(), upstream.node(), upstream.offset());
1047 fprintf(stderr, "end: %s %p:%ld\n", pos.node()->nodeName().string().latin1(), pos.node(), pos.offset());
1048 fprintf(stderr, "downstream: %s %p:%ld\n", downstream.node()->nodeName().string().latin1(), downstream.node(), downstream.offset());
1049 fprintf(stderr, "-----------------------------------\n");
1054 r = m_start.node()->renderer();
1055 for (int i = 0; i < context; i++, back++) {
1056 if (r->previousRenderer())
1057 r = r->previousRenderer();
1061 for (int i = 0; i < back; i++) {
1062 debugRenderer(r, false);
1063 r = r->nextRenderer();
1067 fprintf(stderr, "\n");
1069 if (m_start.node() == m_end.node())
1070 debugRenderer(m_start.node()->renderer(), true);
1072 for (r = m_start.node()->renderer(); r && r != m_end.node()->renderer(); r = r->nextRenderer())
1073 debugRenderer(r, true);
1075 fprintf(stderr, "\n");
1077 r = m_end.node()->renderer();
1078 for (int i = 0; i < context; i++) {
1079 if (r->nextRenderer()) {
1080 r = r->nextRenderer();
1081 debugRenderer(r, false);
1088 fprintf(stderr, "================================\n");
1092 #define FormatBufferSize 1024
1093 void Selection::formatForDebugger(char *buffer, unsigned length) const
1102 char s[FormatBufferSize];
1104 m_start.formatForDebugger(s, FormatBufferSize);
1107 m_end.formatForDebugger(s, FormatBufferSize);
1111 strncpy(buffer, result.string().latin1(), length - 1);
1113 #undef FormatBufferSize