File indexing completed on 2024-05-05 12:16:46

0001 /*
0002  * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
0003  *
0004  * Redistribution and use in source and binary forms, with or without
0005  * modification, are permitted provided that the following conditions
0006  * are met:
0007  * 1. Redistributions of source code must retain the above copyright
0008  *    notice, this list of conditions and the following disclaimer.
0009  * 2. Redistributions in binary form must reproduce the above copyright
0010  *    notice, this list of conditions and the following disclaimer in the
0011  *    documentation and/or other materials provided with the distribution.
0012  *
0013  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
0014  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0015  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0016  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
0017  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0018  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0019  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0020  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
0021  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0022  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0023  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0024  */
0025 
0026 #include "dom_selection.h"
0027 
0028 #include "khtml_part.h"
0029 #include "khtmlpart_p.h"
0030 #include "khtmlview.h"
0031 #include "dom/dom2_range.h"
0032 #include "dom/dom_node.h"
0033 #include "dom/dom_string.h"
0034 #include "rendering/render_object.h"
0035 #include "rendering/render_style.h"
0036 #include "rendering/render_text.h"
0037 #include "xml/dom_docimpl.h"
0038 #include "xml/dom_positioniterator.h"
0039 #include "xml/dom_elementimpl.h"
0040 #include "xml/dom_nodeimpl.h"
0041 #include "xml/dom_textimpl.h"
0042 
0043 #include <QEvent>
0044 #include <QPainter>
0045 #include <QPaintEngine>
0046 #include <QRect>
0047 
0048 #define EDIT_DEBUG 0
0049 #define DEBUG_CARET
0050 
0051 using khtml::EditorContext;
0052 using khtml::findWordBoundary;
0053 using khtml::InlineTextBox;
0054 using khtml::RenderObject;
0055 using khtml::RenderText;
0056 using khtml::RenderPosition;
0057 
0058 namespace DOM
0059 {
0060 
0061 static bool firstRunAt(RenderObject *renderNode, int y, NodeImpl *&startNode, long &startOffset);
0062 static bool lastRunAt(RenderObject *renderNode, int y, NodeImpl *&endNode, long &endOffset);
0063 static bool startAndEndLineNodesIncludingNode(NodeImpl *node, int offset, Selection &selection);
0064 
0065 static inline Position &emptyPosition()
0066 {
0067     static Position EmptyPosition = Position();
0068     return EmptyPosition;
0069 }
0070 
0071 Selection::Selection()
0072 {
0073     init();
0074 }
0075 
0076 Selection::Selection(const Position &pos)
0077 {
0078     init();
0079     assignBaseAndExtent(pos, pos);
0080     validate();
0081 }
0082 
0083 Selection::Selection(const Range &r)
0084 {
0085     const Position start(r.startContainer().handle(), r.startOffset());
0086     const Position end(r.endContainer().handle(), r.endOffset());
0087 
0088     init();
0089     assignBaseAndExtent(start, end);
0090     validate();
0091 }
0092 
0093 Selection::Selection(const Position &base, const Position &extent)
0094 {
0095     init();
0096     assignBaseAndExtent(base, extent);
0097     validate();
0098 }
0099 
0100 Selection::Selection(const Selection &o)
0101 {
0102     init();
0103 
0104     assignBaseAndExtent(o.base(), o.extent());
0105     assignStartAndEnd(o.start(), o.end());
0106 
0107     m_state = o.m_state;
0108     m_affinity = o.m_affinity;
0109 
0110     m_baseIsStart = o.m_baseIsStart;
0111     m_needsCaretLayout = o.m_needsCaretLayout;
0112     m_modifyBiasSet = o.m_modifyBiasSet;
0113 
0114     // Only copy the coordinates over if the other object
0115     // has had a layout, otherwise keep the current
0116     // coordinates. This prevents drawing artifacts from
0117     // remaining when the caret is painted and then moves,
0118     // and the old rectangle needs to be repainted.
0119     if (!m_needsCaretLayout) {
0120         m_caretX = o.m_caretX;
0121         m_caretY = o.m_caretY;
0122         m_caretSize = o.m_caretSize;
0123     }
0124 }
0125 
0126 void Selection::init()
0127 {
0128     m_base = m_extent = m_start = m_end = emptyPosition();
0129     m_state = NONE;
0130     m_caretX = 0;
0131     m_caretY = 0;
0132     m_caretSize = 0;
0133     m_baseIsStart = true;
0134     m_needsCaretLayout = true;
0135     m_modifyBiasSet = false;
0136     m_affinity = DOWNSTREAM;
0137 }
0138 
0139 Selection &Selection::operator=(const Selection &o)
0140 {
0141     assignBaseAndExtent(o.base(), o.extent());
0142     assignStartAndEnd(o.start(), o.end());
0143 
0144     m_state = o.m_state;
0145     m_affinity = o.m_affinity;
0146 
0147     m_baseIsStart = o.m_baseIsStart;
0148     m_needsCaretLayout = o.m_needsCaretLayout;
0149     m_modifyBiasSet = o.m_modifyBiasSet;
0150 
0151     // Only copy the coordinates over if the other object
0152     // has had a layout, otherwise keep the current
0153     // coordinates. This prevents drawing artifacts from
0154     // remaining when the caret is painted and then moves,
0155     // and the old rectangle needs to be repainted.
0156     if (!m_needsCaretLayout) {
0157         m_caretX = o.m_caretX;
0158         m_caretY = o.m_caretY;
0159         m_caretSize = o.m_caretSize;
0160     }
0161 
0162     return *this;
0163 }
0164 
0165 void Selection::setAffinity(EAffinity affinity)
0166 {
0167     if (affinity == m_affinity) {
0168         return;
0169     }
0170 
0171     m_affinity = affinity;
0172     setNeedsLayout();
0173 }
0174 
0175 void Selection::moveTo(const Range &r)
0176 {
0177     Position start(r.startContainer().handle(), r.startOffset());
0178     Position end(r.endContainer().handle(), r.endOffset());
0179     moveTo(start, end);
0180 }
0181 
0182 void Selection::moveTo(const Selection &o)
0183 {
0184     moveTo(o.start(), o.end());
0185 }
0186 
0187 void Selection::moveTo(const Position &pos)
0188 {
0189     moveTo(pos, pos);
0190 }
0191 
0192 void Selection::moveTo(const Position &base, const Position &extent)
0193 {
0194 //   kdDebug(6200) << "Selection::moveTo: base(" << base.node() << "," << base.offset() << "), extent(" << extent.node() << "," << extent.offset() << ")";
0195 #ifdef DEBUG_CARET
0196     qCDebug(KHTML_LOG) << *this << base << extent;
0197 #endif
0198     assignBaseAndExtent(base, extent);
0199     validate();
0200 }
0201 
0202 bool Selection::modify(EAlter alter, EDirection dir, ETextGranularity granularity)
0203 {
0204     Position pos;
0205 
0206     switch (dir) {
0207     // EDIT FIXME: This needs to handle bidi
0208     case RIGHT:
0209     case FORWARD:
0210         if (alter == EXTEND) {
0211             if (!m_modifyBiasSet) {
0212                 m_modifyBiasSet = true;
0213                 assignBaseAndExtent(start(), end());
0214             }
0215             switch (granularity) {
0216             case CHARACTER:
0217                 pos = extent().nextCharacterPosition();
0218                 break;
0219             case WORD:
0220                 pos = extent().nextWordPosition();
0221                 break;
0222             case LINE:
0223                 pos = extent().nextLinePosition(xPosForVerticalArrowNavigation(EXTENT));
0224                 break;
0225             case PARAGRAPH:
0226                 // not implemented
0227                 break;
0228             }
0229         } else {
0230             m_modifyBiasSet = false;
0231             switch (granularity) {
0232             case CHARACTER:
0233                 pos = (state() == RANGE) ? end() : extent().nextCharacterPosition();
0234                 break;
0235             case WORD:
0236                 pos = extent().nextWordPosition();
0237                 break;
0238             case LINE:
0239                 pos = end().nextLinePosition(xPosForVerticalArrowNavigation(END, state() == RANGE));
0240                 break;
0241             case PARAGRAPH:
0242                 // not implemented
0243                 break;
0244             }
0245         }
0246         break;
0247     // EDIT FIXME: This needs to handle bidi
0248     case LEFT:
0249     case BACKWARD:
0250         if (alter == EXTEND) {
0251             if (!m_modifyBiasSet) {
0252                 m_modifyBiasSet = true;
0253                 assignBaseAndExtent(end(), start());
0254             }
0255             switch (granularity) {
0256             case CHARACTER:
0257                 pos = extent().previousCharacterPosition();
0258                 break;
0259             case WORD:
0260                 pos = extent().previousWordPosition();
0261                 break;
0262             case LINE:
0263                 pos = extent().previousLinePosition(xPosForVerticalArrowNavigation(EXTENT));
0264                 break;
0265             case PARAGRAPH:
0266                 // not implemented
0267                 break;
0268             }
0269         } else {
0270             m_modifyBiasSet = false;
0271             switch (granularity) {
0272             case CHARACTER:
0273                 pos = (state() == RANGE) ? start() : extent().previousCharacterPosition();
0274                 break;
0275             case WORD:
0276                 pos = extent().previousWordPosition();
0277                 break;
0278             case LINE:
0279                 pos = start().previousLinePosition(xPosForVerticalArrowNavigation(START, state() == RANGE));
0280                 break;
0281             case PARAGRAPH:
0282                 // not implemented
0283                 break;
0284             }
0285         }
0286         break;
0287     }
0288 
0289     if (pos.isEmpty()) {
0290         return false;
0291     }
0292 
0293     if (alter == MOVE) {
0294         moveTo(pos);
0295     } else { // alter == EXTEND
0296         setExtent(pos);
0297     }
0298 
0299     return true;
0300 }
0301 
0302 bool Selection::expandUsingGranularity(ETextGranularity granularity)
0303 {
0304     if (state() == NONE) {
0305         return false;
0306     }
0307 
0308     validate(granularity);
0309     return true;
0310 }
0311 
0312 int Selection::xPosForVerticalArrowNavigation(EPositionType type, bool recalc) const
0313 {
0314     int x = 0;
0315 
0316     if (state() == NONE) {
0317         return x;
0318     }
0319 
0320     Position pos;
0321     switch (type) {
0322     case START:
0323         pos = start();
0324         break;
0325     case END:
0326         pos = end();
0327         break;
0328     case BASE:
0329         pos = base();
0330         break;
0331     case EXTENT:
0332         pos = extent();
0333         break;
0334     case CARETPOS:
0335         pos = caretPos();
0336         break;
0337     }
0338 
0339     KHTMLPart *part = pos.node()->document()->part();
0340     if (!part) {
0341         return x;
0342     }
0343 
0344     if (recalc || part->d->editor_context.m_xPosForVerticalArrowNavigation == EditorContext::NoXPosForVerticalArrowNavigation
0345        ) {
0346         int y, w, h;
0347         if (pos.node()->renderer()) {
0348             pos.node()->renderer()->caretPos(pos.renderedOffset(), 0, x, y, w, h);
0349         }
0350         part->d->editor_context.m_xPosForVerticalArrowNavigation = x;
0351     } else {
0352         x = part->d->editor_context.m_xPosForVerticalArrowNavigation;
0353     }
0354 
0355     return x;
0356 }
0357 
0358 void Selection::clear()
0359 {
0360     assignBaseAndExtent(emptyPosition(), emptyPosition());
0361     validate();
0362 }
0363 
0364 void Selection::collapse()
0365 {
0366     moveTo(caretPos());
0367 }
0368 
0369 void Selection::setBase(const Position &pos)
0370 {
0371     assignBase(pos);
0372     validate();
0373 }
0374 
0375 void Selection::setExtent(const Position &pos)
0376 {
0377     assignExtent(pos);
0378     validate();
0379 }
0380 
0381 void Selection::setBaseAndExtent(const Position &base, const Position &extent)
0382 {
0383     assignBaseAndExtent(base, extent);
0384     validate();
0385 }
0386 
0387 void Selection::setStart(const Position &pos)
0388 {
0389     assignStart(pos);
0390     validate();
0391 }
0392 
0393 void Selection::setEnd(const Position &pos)
0394 {
0395     assignEnd(pos);
0396     validate();
0397 }
0398 
0399 void Selection::setStartAndEnd(const Position &start, const Position &end)
0400 {
0401     assignStartAndEnd(start, end);
0402     validate();
0403 }
0404 
0405 void Selection::setNeedsLayout(bool flag)
0406 {
0407     m_needsCaretLayout = flag;
0408 }
0409 
0410 void Selection::getRange(NodeImpl *&st, long &so, NodeImpl *&en, long &eo) const
0411 {
0412     if (isEmpty()) {
0413         st = en = nullptr; so = eo = 0;
0414         return;
0415     }
0416 
0417     // Make sure we have an updated layout since this function is called
0418     // in the course of running edit commands which modify the DOM.
0419     // Failing to call this can result in equivalentXXXPosition calls returning
0420     // incorrect results.
0421     start().node()->document()->updateLayout();
0422 
0423     Position s, e;
0424     if (state() == CARET) {
0425         // If the selection is a caret, move the range start upstream. This helps us match
0426         // the conventions of text editors tested, which make style determinations based
0427         // on the character before the caret, if any.
0428         s = start().equivalentUpstreamPosition().equivalentRangeCompliantPosition();
0429         e = s;
0430     } else {
0431         // If the selection is a range, select the minimum range that encompasses the selection.
0432         // Again, this is to match the conventions of text editors tested, which make style
0433         // determinations based on the first character of the selection.
0434         // For instance, this operation helps to make sure that the "X" selected below is the
0435         // only thing selected. The range should not be allowed to "leak" out to the end of the
0436         // previous text node, or to the beginning of the next text node, each of which has a
0437         // different style.
0438         //
0439         // On a treasure map, <b>X</b> marks the spot.
0440         //                       ^ selected
0441         //
0442         assert(state() == RANGE);
0443         s = start().equivalentDownstreamPosition();
0444         e = end().equivalentUpstreamPosition();
0445         if ((s.node() == e.node() && s.offset() > e.offset()) || !nodeIsBeforeNode(s.node(), e.node())) {
0446             // Make sure the start is before the end.
0447             // The end can wind up before the start if collapsed whitespace is the only thing selected.
0448             Position tmp = s;
0449             s = e;
0450             e = tmp;
0451         }
0452         s = s.equivalentRangeCompliantPosition();
0453         e = e.equivalentRangeCompliantPosition();
0454     }
0455 
0456     st = s.node();
0457     so = s.offset();
0458     en = e.node();
0459     eo = e.offset();
0460 }
0461 
0462 Range Selection::toRange() const
0463 {
0464     if (isEmpty()) {
0465         return Range();
0466     }
0467 
0468     NodeImpl *start, *end;
0469     long so, eo;
0470     getRange(start, so, end, eo);
0471     return Range(Node(start), so, Node(end), eo);
0472 }
0473 
0474 void Selection::layoutCaret()
0475 {
0476     if (isEmpty() || !caretPos().node()->renderer()) {
0477         m_caretX = m_caretY = m_caretSize = 0;
0478     } else {
0479         // EDIT FIXME: Enhance call to pass along selection
0480         // upstream/downstream affinity to get the right position.
0481         int w;
0482         int offset = RenderPosition::fromDOMPosition(caretPos()).renderedOffset();
0483 #ifdef DEBUG_CARET
0484         qCDebug(KHTML_LOG) << "[before caretPos()]" << m_caretX;
0485 #endif
0486         caretPos().node()->renderer()->caretPos(offset, true, m_caretX, m_caretY, w, m_caretSize);
0487 #ifdef DEBUG_CARET
0488         qCDebug(KHTML_LOG) << "[after caretPos()]" << m_caretX;
0489 #endif
0490     }
0491 
0492     m_needsCaretLayout = false;
0493 }
0494 
0495 QRect Selection::getRepaintRect() const
0496 {
0497     if (m_needsCaretLayout) {
0498         const_cast<Selection *>(this)->layoutCaret();
0499     }
0500 
0501     // EDIT FIXME: fudge a bit to make sure we don't leave behind artifacts
0502     return QRect(m_caretX - 1, m_caretY - 1, 3, m_caretSize + 2);
0503 }
0504 
0505 void Selection::needsCaretRepaint()
0506 {
0507     if (isEmpty()) {
0508         return;
0509     }
0510 
0511     if (!start().node()) {
0512         return;
0513     }
0514 
0515     if (!start().node()->document()) {
0516         return;
0517     }
0518 
0519     KHTMLView *v = caretPos().node()->document()->view();
0520     if (!v) {
0521         return;
0522     }
0523 
0524     // qCDebug(KHTML_LOG) << "[NeedsCaretLayout]" << m_needsCaretLayout;
0525     if (m_needsCaretLayout) {
0526         // repaint old position and calculate new position
0527         v->updateContents(getRepaintRect());
0528         layoutCaret();
0529 
0530         // EDIT FIXME: This is an unfortunate hack.
0531         // Basically, we can't trust this layout position since we
0532         // can't guarantee that the check to see if we are in unrendered
0533         // content will work at this point. We may have to wait for
0534         // a layout and re-render of the document to happen. So, resetting this
0535         // flag will cause another caret layout to happen the first time
0536         // that we try to paint the caret after this call. That one will work since
0537         // it happens after the document has accounted for any editing
0538         // changes which may have been done.
0539         // And, we need to leave this layout here so the caret moves right
0540         // away after clicking.
0541         m_needsCaretLayout = true;
0542     }
0543     v->updateContents(getRepaintRect());
0544 
0545 }
0546 
0547 void Selection::paintCaret(QPainter *p, const QRect &rect)
0548 {
0549     if (isEmpty()) {
0550         return;
0551     }
0552 
0553     if (m_state == NONE) {
0554         return;
0555     }
0556 
0557     if (m_needsCaretLayout) {
0558         Position pos = caretPos();
0559         if (!pos.inRenderedContent()) {
0560             // ### wrong wrong wrong wrong wrong. this will break quanta vpl
0561             moveToRenderedContent();
0562         }
0563         layoutCaret();
0564     }
0565 
0566     QRect caretRect(m_caretX, m_caretY, 1, m_caretSize);
0567     if (caretRect.intersects(rect)) {
0568         QPainter::CompositionMode oldop = p->compositionMode();
0569         QColor c = Qt::black;
0570         if (p->paintEngine() && p->paintEngine()->hasFeature(QPaintEngine::BlendModes)) {
0571             p->setCompositionMode(QPainter::CompositionMode_Difference);
0572             c =  Qt::white;
0573         } else {
0574             p->setCompositionMode(QPainter::CompositionMode_Xor);
0575         }
0576         p->fillRect(caretRect.left(), caretRect.top(), 1, caretRect.height(), c);
0577         p->setCompositionMode(oldop);
0578     }
0579 }
0580 
0581 void Selection::validate(ETextGranularity granularity)
0582 {
0583 #ifdef DEBUG_CARET
0584     qCDebug(KHTML_LOG) << *this << granularity;
0585 #endif
0586     // move the base and extent nodes to their equivalent leaf positions
0587     bool baseAndExtentEqual = base() == extent();
0588     if (base().notEmpty()) {
0589 #ifdef DEBUG_CARET
0590         qCDebug(KHTML_LOG) << "[base not empty]";
0591 #endif
0592         Position pos = base().equivalentLeafPosition();
0593         assignBase(pos);
0594         if (baseAndExtentEqual) {
0595             assignExtent(pos);
0596         }
0597     }
0598     if (extent().notEmpty() && !baseAndExtentEqual) {
0599         assignExtent(extent().equivalentLeafPosition());
0600     }
0601 
0602     // make sure we do not have a dangling start or end. In particular, if one
0603     // of base or extent is empty, we use the other one (which may be empty as
0604     // well) for everything, before getting into the code that computes
0605     // start + end from base + extent based on granularity.
0606     if (base().isEmpty()) {
0607         assignBaseAndExtent(extent(), extent());
0608         m_baseIsStart = true;
0609     } else if (extent().isEmpty()) {
0610         assignBaseAndExtent(base(), base());
0611         m_baseIsStart = true;
0612     } else {
0613         // adjust m_baseIsStart as needed
0614         if (base().node() == extent().node()) {
0615             if (base().offset() > extent().offset()) {
0616                 m_baseIsStart = false;
0617             } else {
0618                 m_baseIsStart = true;
0619             }
0620         } else if (nodeIsBeforeNode(base().node(), extent().node())) {
0621             m_baseIsStart = true;
0622         } else {
0623             m_baseIsStart = false;
0624         }
0625     }
0626 
0627     // calculate the correct start and end positions
0628     if (granularity == CHARACTER) {
0629 #ifdef DEBUG_CARET
0630         qCDebug(KHTML_LOG) << "[character:baseIsStart]" << m_baseIsStart << base() << extent();
0631 #endif
0632         if (m_baseIsStart) {
0633             assignStartAndEnd(base(), extent());
0634         } else {
0635             assignStartAndEnd(extent(), base());
0636         }
0637     } else if (granularity == WORD) {
0638         int baseStartOffset = base().offset();
0639         int baseEndOffset = base().offset();
0640         int extentStartOffset = extent().offset();
0641         int extentEndOffset = extent().offset();
0642 #ifdef DEBUG_CARET
0643         qCDebug(KHTML_LOG) << "WORD GRANULARITY:" << baseStartOffset << baseEndOffset << extentStartOffset << extentEndOffset;
0644 #endif
0645         if (base().notEmpty() && (base().node()->nodeType() == Node::TEXT_NODE || base().node()->nodeType() == Node::CDATA_SECTION_NODE)) {
0646             DOMString t = base().node()->nodeValue();
0647             QChar *chars = t.unicode();
0648             uint len = t.length();
0649 #ifdef DEBUG_CARET
0650             qCDebug(KHTML_LOG) << "text:" << QString::fromRawData(chars, len);
0651 #endif
0652             findWordBoundary(chars, len, base().offset(), &baseStartOffset, &baseEndOffset);
0653 #ifdef DEBUG_CARET
0654             qCDebug(KHTML_LOG) << "after find word boundary" << baseStartOffset << baseEndOffset;
0655 #endif
0656         }
0657         if (extent().notEmpty() && (extent().node()->nodeType() == Node::TEXT_NODE || extent().node()->nodeType() == Node::CDATA_SECTION_NODE)) {
0658             DOMString t = extent().node()->nodeValue();
0659             QChar *chars = t.unicode();
0660             uint len = t.length();
0661 #ifdef DEBUG_CARET
0662             qCDebug(KHTML_LOG) << "text:" << QString::fromRawData(chars, len);
0663 #endif
0664             findWordBoundary(chars, len, extent().offset(), &extentStartOffset, &extentEndOffset);
0665 #ifdef DEBUG_CARET
0666             qCDebug(KHTML_LOG) << "after find word boundary" << baseStartOffset << baseEndOffset;
0667 #endif
0668         }
0669 #ifdef DEBUG_CARET
0670         qCDebug(KHTML_LOG) << "is start:" << m_baseIsStart;
0671 #endif
0672         if (m_baseIsStart) {
0673             assignStart(Position(base().node(), baseStartOffset));
0674             assignEnd(Position(extent().node(), extentEndOffset));
0675         } else {
0676             assignStart(Position(extent().node(), extentStartOffset));
0677             assignEnd(Position(base().node(), baseEndOffset));
0678         }
0679     } else { // granularity == LINE
0680         Selection baseSelection = *this;
0681         Selection extentSelection = *this;
0682         if (base().notEmpty() && (base().node()->nodeType() == Node::TEXT_NODE || base().node()->nodeType() == Node::CDATA_SECTION_NODE)) {
0683             if (startAndEndLineNodesIncludingNode(base().node(), base().offset(), baseSelection)) {
0684                 assignStart(Position(baseSelection.base().node(), baseSelection.base().offset()));
0685                 assignEnd(Position(baseSelection.extent().node(), baseSelection.extent().offset()));
0686             }
0687         }
0688         if (extent().notEmpty() && (extent().node()->nodeType() == Node::TEXT_NODE || extent().node()->nodeType() == Node::CDATA_SECTION_NODE)) {
0689             if (startAndEndLineNodesIncludingNode(extent().node(), extent().offset(), extentSelection)) {
0690                 assignStart(Position(extentSelection.base().node(), extentSelection.base().offset()));
0691                 assignEnd(Position(extentSelection.extent().node(), extentSelection.extent().offset()));
0692             }
0693         }
0694         if (m_baseIsStart) {
0695             assignStart(baseSelection.start());
0696             assignEnd(extentSelection.end());
0697         } else {
0698             assignStart(extentSelection.start());
0699             assignEnd(baseSelection.end());
0700         }
0701     }
0702 
0703     // adjust the state
0704     if (start().isEmpty() && end().isEmpty()) {
0705         m_state = NONE;
0706     } else if (start() == end()) {
0707         m_state = CARET;
0708     } else {
0709         m_state = RANGE;
0710     }
0711 
0712     if (start().isEmpty()) {
0713         assert(m_state == NONE);
0714     }
0715 
0716     m_needsCaretLayout = true;
0717 
0718 #if EDIT_DEBUG
0719     debugPosition();
0720 #endif
0721 }
0722 
0723 bool Selection::moveToRenderedContent()
0724 {
0725     if (isEmpty()) {
0726         return false;
0727     }
0728 
0729     if (m_state != CARET) {
0730         return false;
0731     }
0732 
0733     Position pos = start();
0734     if (pos.inRenderedContent()) {
0735         return true;
0736     }
0737 
0738     // not currently rendered, try moving to prev
0739     Position prev = pos.previousCharacterPosition();
0740     if (prev != pos && prev.node()->inSameContainingBlockFlowElement(pos.node())) {
0741         moveTo(prev);
0742         return true;
0743     }
0744 
0745     // could not be moved to prev, try next
0746     Position next = pos.nextCharacterPosition();
0747     if (next != pos && next.node()->inSameContainingBlockFlowElement(pos.node())) {
0748         moveTo(next);
0749         return true;
0750     }
0751 
0752     return false;
0753 }
0754 
0755 bool Selection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) const
0756 {
0757     if (!n1 || !n2) {
0758         return true;
0759     }
0760 
0761     if (n1 == n2) {
0762         return true;
0763     }
0764 
0765     bool result = false;
0766     int n1Depth = 0;
0767     int n2Depth = 0;
0768 
0769     // First we find the depths of the two nodes in the tree (n1Depth, n2Depth)
0770     NodeImpl *n = n1;
0771     while (n->parentNode()) {
0772         n = n->parentNode();
0773         n1Depth++;
0774     }
0775     n = n2;
0776     while (n->parentNode()) {
0777         n = n->parentNode();
0778         n2Depth++;
0779     }
0780     // Climb up the tree with the deeper node, until both nodes have equal depth
0781     while (n2Depth > n1Depth) {
0782         n2 = n2->parentNode();
0783         n2Depth--;
0784     }
0785     while (n1Depth > n2Depth) {
0786         n1 = n1->parentNode();
0787         n1Depth--;
0788     }
0789     // Climb the tree with both n1 and n2 until they have the same parent
0790     while (n1->parentNode() != n2->parentNode()) {
0791         n1 = n1->parentNode();
0792         n2 = n2->parentNode();
0793     }
0794     // Iterate through the parent's children until n1 or n2 is found
0795     n = n1->parentNode() ? n1->parentNode()->firstChild() : n1->firstChild();
0796     while (n) {
0797         if (n == n1) {
0798             result = true;
0799             break;
0800         } else if (n == n2) {
0801             result = false;
0802             break;
0803         }
0804         n = n->nextSibling();
0805     }
0806     return result;
0807 }
0808 
0809 static bool firstRunAt(RenderObject *renderNode, int y, NodeImpl *&startNode, long &startOffset)
0810 {
0811     for (RenderObject *n = renderNode; n; n = n->nextSibling()) {
0812         if (n->isText()) {
0813             RenderText *textRenderer = static_cast<khtml::RenderText *>(n);
0814             for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
0815                 if (box->m_y == y) {
0816                     startNode = textRenderer->element();
0817                     startOffset = box->m_start;
0818                     return true;
0819                 }
0820             }
0821         }
0822 
0823         if (firstRunAt(n->firstChild(), y, startNode, startOffset)) {
0824             return true;
0825         }
0826     }
0827 
0828     return false;
0829 }
0830 
0831 static bool lastRunAt(RenderObject *renderNode, int y, NodeImpl *&endNode, long &endOffset)
0832 {
0833     RenderObject *n = renderNode;
0834     if (!n) {
0835         return false;
0836     }
0837     RenderObject *next;
0838     while ((next = n->nextSibling())) {
0839         n = next;
0840     }
0841 
0842     while (1) {
0843         if (lastRunAt(n->firstChild(), y, endNode, endOffset)) {
0844             return true;
0845         }
0846 
0847         if (n->isText()) {
0848             RenderText *textRenderer =  static_cast<khtml::RenderText *>(n);
0849             for (InlineTextBox *box = textRenderer->lastTextBox(); box; box = box->prevTextBox()) {
0850                 if (box->m_y == y) {
0851                     endNode = textRenderer->element();
0852                     endOffset = box->m_start + box->m_len;
0853                     return true;
0854                 }
0855             }
0856         }
0857 
0858         if (n == renderNode) {
0859             return false;
0860         }
0861 
0862         n = n->previousSibling();
0863     }
0864 }
0865 
0866 static bool startAndEndLineNodesIncludingNode(NodeImpl *node, int offset, Selection &selection)
0867 {
0868     if (node && node->renderer() && (node->nodeType() == Node::TEXT_NODE || node->nodeType() == Node::CDATA_SECTION_NODE)) {
0869         int pos;
0870         int selectionPointY;
0871         RenderPosition rp = RenderPosition::fromDOMPosition(Position(node, offset));
0872         pos = rp.renderedOffset();
0873         // RenderText *renderer = static_cast<RenderText *>(node->renderer());
0874         // const InlineTextBox * run = renderer->findInlineTextBox( offset, pos );
0875         const InlineTextBox *run = static_cast<InlineTextBox *>(node->renderer()->inlineBox(pos));
0876         DOMString t = node->nodeValue();
0877 
0878         if (!run) {
0879             return false;
0880         }
0881 
0882         selectionPointY = run->m_y;
0883 
0884         // Go up to first non-inline element.
0885         khtml::RenderObject *renderNode = node->renderer();
0886         while (renderNode && renderNode->isInline()) {
0887             renderNode = renderNode->parent();
0888         }
0889 
0890         if (renderNode) {
0891             renderNode = renderNode->firstChild();
0892         }
0893 
0894         NodeImpl *startNode = nullptr;
0895         NodeImpl *endNode = nullptr;
0896         long startOffset;
0897         long endOffset;
0898 
0899         // Look for all the first child in the block that is on the same line
0900         // as the selection point.
0901         if (!firstRunAt(renderNode, selectionPointY, startNode, startOffset)) {
0902             return false;
0903         }
0904 
0905         // Look for all the last child in the block that is on the same line
0906         // as the selection point.
0907         if (!lastRunAt(renderNode, selectionPointY, endNode, endOffset)) {
0908             return false;
0909         }
0910 
0911         selection.moveTo(RenderPosition(startNode, startOffset).position(), RenderPosition(endNode, endOffset).position());
0912 
0913         return true;
0914     }
0915     return false;
0916 }
0917 
0918 void Selection::debugRenderer(RenderObject *r, bool selected) const
0919 {
0920     if (r->node()->isElementNode()) {
0921         ElementImpl *element = static_cast<ElementImpl *>(r->node());
0922         fprintf(stderr, "%s%s\n", selected ? "==> " : "    ", element->tagName().string().toLatin1().data());
0923     } else if (r->isText()) {
0924         RenderText *textRenderer = static_cast<RenderText *>(r);
0925         if (textRenderer->stringLength() == 0 || !textRenderer->firstTextBox()) {
0926             fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : "    ");
0927             return;
0928         }
0929 
0930         static const int max = 36;
0931         QString text = DOMString(textRenderer->string()).string();
0932         int textLength = text.length();
0933         if (selected) {
0934             int offset = 0;
0935             if (r->node() == start().node()) {
0936                 offset = start().offset();
0937             } else if (r->node() == end().node()) {
0938                 offset = end().offset();
0939             }
0940 
0941             int pos;
0942             const InlineTextBox *box = textRenderer->findInlineTextBox(offset, pos);
0943             text = text.mid(box->m_start, box->m_len);
0944 
0945             QString show;
0946             int mid = max / 2;
0947             int caret = 0;
0948 
0949             // text is shorter than max
0950             if (textLength < max) {
0951                 show = text;
0952                 caret = pos;
0953             }
0954 
0955             // too few characters to left
0956             else if (pos - mid < 0) {
0957                 show = text.left(max - 3) + "...";
0958                 caret = pos;
0959             }
0960 
0961             // enough characters on each side
0962             else if (pos - mid >= 0 && pos + mid <= textLength) {
0963                 show = "..." + text.mid(pos - mid + 3, max - 6) + "...";
0964                 caret = mid;
0965             }
0966 
0967             // too few characters on right
0968             else {
0969                 show = "..." + text.right(max - 3);
0970                 caret = pos - (textLength - show.length());
0971             }
0972 
0973             show = show.replace("\n", " ");
0974             show = show.replace("\r", " ");
0975             fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.toLatin1().data(), pos);
0976             fprintf(stderr, "           ");
0977             for (int i = 0; i < caret; i++) {
0978                 fprintf(stderr, " ");
0979             }
0980             fprintf(stderr, "^\n");
0981         } else {
0982             if ((int)text.length() > max) {
0983                 text = text.left(max - 3) + "...";
0984             } else {
0985                 text = text.left(max);
0986             }
0987             fprintf(stderr, "    #text : \"%s\"\n", text.toLatin1().data());
0988         }
0989     }
0990 }
0991 
0992 void Selection::debugPosition() const
0993 {
0994     if (!start().node()) {
0995         return;
0996     }
0997 
0998     //static int context = 5;
0999 
1000     //RenderObject *r = 0;
1001 
1002     fprintf(stderr, "Selection =================\n");
1003 
1004     if (start() == end()) {
1005         Position pos = start();
1006         Position upstream = pos.equivalentUpstreamPosition();
1007         Position downstream = pos.equivalentDownstreamPosition();
1008         /*FIXME:use qCDebug(KHTML_LOG) fprintf(stderr, "upstream:   %s %p:%ld\n", getTagName(upstream.node()->id())
1009             , upstream.node(), upstream.offset());
1010         fprintf(stderr, "pos:        %s %p:%ld\n", getTagName(pos.node()->id())
1011             , pos.node(), pos.offset());
1012         fprintf(stderr, "downstream: %s %p:%ld\n", getTagName(downstream.node()->id())
1013             , downstream.node(), downstream.offset());*/
1014     } else {
1015         Position pos = start();
1016         Position upstream = pos.equivalentUpstreamPosition();
1017         Position downstream = pos.equivalentDownstreamPosition();
1018         /*FIXME: use qCDebug(KHTML_LOG) fprintf(stderr, "upstream:   %s %p:%ld\n", getTagName(upstream.node()->id())
1019             , upstream.node(), upstream.offset());
1020         fprintf(stderr, "start:      %s %p:%ld\n", getTagName(pos.node()->id())
1021             , pos.node(), pos.offset());
1022         fprintf(stderr, "downstream: %s %p:%ld\n", getTagName(downstream.node()->id())
1023             , downstream.node(), downstream.offset());
1024         fprintf(stderr, "-----------------------------------\n");*/
1025         pos = end();
1026         upstream = pos.equivalentUpstreamPosition();
1027         downstream = pos.equivalentDownstreamPosition();
1028         /*FIXME: use qCDebug(KHTML_LOG) fprintf(stderr, "upstream:   %s %p:%ld\n", getTagName(upstream.node()->id())
1029             , upstream.node(), upstream.offset());
1030         fprintf(stderr, "end:        %s %p:%ld\n", getTagName(pos.node()->id())
1031             , pos.node(), pos.offset());
1032         fprintf(stderr, "downstream: %s %p:%ld\n", getTagName(downstream.node()->id())
1033             , downstream.node(), downstream.offset());
1034         fprintf(stderr, "-----------------------------------\n");*/
1035     }
1036 
1037 #if 0
1038     int back = 0;
1039     r = start().node()->renderer();
1040     for (int i = 0; i < context; i++, back++) {
1041         if (r->previousRenderer()) {
1042             r = r->previousRenderer();
1043         } else {
1044             break;
1045         }
1046     }
1047     for (int i = 0; i < back; i++) {
1048         debugRenderer(r, false);
1049         r = r->nextRenderer();
1050     }
1051 
1052     fprintf(stderr, "\n");
1053 
1054     if (start().node() == end().node()) {
1055         debugRenderer(start().node()->renderer(), true);
1056     } else
1057         for (r = start().node()->renderer(); r && r != end().node()->renderer(); r = r->nextRenderer()) {
1058             debugRenderer(r, true);
1059         }
1060 
1061     fprintf(stderr, "\n");
1062 
1063     r = end().node()->renderer();
1064     for (int i = 0; i < context; i++) {
1065         if (r->nextRenderer()) {
1066             r = r->nextRenderer();
1067             debugRenderer(r, false);
1068         } else {
1069             break;
1070         }
1071     }
1072 #endif
1073 
1074     fprintf(stderr, "================================\n");
1075 }
1076 
1077 QDebug operator<<(QDebug stream, const Selection &selection)
1078 {
1079     stream << "Selection["
1080            << selection.base()
1081            << selection.extent()
1082            << selection.start()
1083            << selection.end()
1084            << selection.affinity() << "]";
1085     return stream;
1086 }
1087 
1088 } // namespace DOM