82dc803fb146b473f5905dc09c98da9254edb2cc
[WebKit-https.git] / WebCore / khtml / editing / SelectionController.cpp
1 /*
2  * Copyright (C) 2003 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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. 
24  */
25   
26 #include "khtml_selection.h"
27
28 #include "htmltags.h"
29 #include "khtml_part.h"
30 #include "khtmlview.h"
31 #include "qevent.h"
32 #include "qpainter.h"
33 #include "qrect.h"
34 #include "dom/dom2_range.h"
35 #include "dom/dom_node.h"
36 #include "dom/dom_position.h"
37 #include "dom/dom_string.h"
38 #include "rendering/render_object.h"
39 #include "rendering/render_style.h"
40 #include "rendering/render_text.h"
41 #include "xml/dom_docimpl.h"
42 #include "xml/dom_edititerator.h"
43 #include "xml/dom_elementimpl.h"
44 #include "xml/dom_nodeimpl.h"
45 #include "xml/dom_textimpl.h"
46
47 #if APPLE_CHANGES
48 #include <KWQAssertions.h>
49 #include <KWQTextUtilities.h>
50 #include <CoreServices/CoreServices.h>
51
52 #define EDIT_DEBUG 0
53 #endif
54
55 using DOM::DocumentImpl;
56 using DOM::DOMPosition;
57 using DOM::DOMString;
58 using DOM::EditIterator;
59 using DOM::ElementImpl;
60 using DOM::Node;
61 using DOM::NodeImpl;
62 using DOM::Range;
63 using DOM::TextImpl;
64 using khtml::InlineTextBox;
65 using khtml::RenderObject;
66 using khtml::RenderText;
67
68 #if APPLE_CHANGES
69 static bool firstRunAt(RenderObject *renderNode, int y, NodeImpl *&startNode, long &startOffset);
70 static bool lastRunAt(RenderObject *renderNode, int y, NodeImpl *&endNode, long &endOffset);
71 static bool startAndEndLineNodesIncludingNode(DOM::NodeImpl *node, int offset, KHTMLSelection &selection);
72 #endif
73
74
75 KHTMLSelection::KHTMLSelection()
76 {
77     init();
78 }
79
80 KHTMLSelection::KHTMLSelection(NodeImpl *node, long offset)
81 {
82     init();
83
84         setBaseNode(node);
85         setExtentNode(node);
86         setBaseOffset(offset);
87         setExtentOffset(offset);
88
89     validate();
90 }
91
92 KHTMLSelection::KHTMLSelection(const DOMPosition &pos)
93 {
94     init();
95
96         setBaseNode(pos.node());
97         setExtentNode(pos.node());
98         setBaseOffset(pos.offset());
99         setExtentOffset(pos.offset());
100
101     validate();
102 }
103
104 KHTMLSelection::KHTMLSelection(const DOMPosition &base, const DOMPosition &extent)
105 {
106     init();
107
108         setBaseNode(base.node());
109         setExtentNode(extent.node());
110         setBaseOffset(base.offset());
111         setExtentOffset(extent.offset());
112
113     validate();
114 }
115
116 KHTMLSelection::KHTMLSelection(NodeImpl *baseNode, long baseOffset, NodeImpl *endNode, long endOffset)
117 {
118     init();
119
120         setBaseNode(baseNode);
121         setExtentNode(endNode);
122         setBaseOffset(baseOffset);
123         setExtentOffset(endOffset);
124
125     validate();
126 }
127
128 KHTMLSelection::KHTMLSelection(const KHTMLSelection &o)
129 {
130     init();
131     
132         setBaseNode(o.baseNode());
133         setExtentNode(o.extentNode());
134         setBaseOffset(o.baseOffset());
135         setExtentOffset(o.extentOffset());
136
137         setStartNode(o.startNode());
138         setEndNode(o.endNode());
139         setStartOffset(o.startOffset());
140         setEndOffset(o.endOffset());
141
142     m_state = o.m_state;
143
144     m_baseIsStart = o.m_baseIsStart;
145     m_needsCaretLayout = o.m_needsCaretLayout;
146     m_modifyBiasSet = o.m_modifyBiasSet;
147     
148     // Only copy the coordinates over if the other object
149     // has had a layout, otherwise keep the current
150     // coordinates. This prevents drawing artifacts from
151     // remaining when the caret is painted and then moves,
152     // and the old rectangle needs to be repainted.
153     if (!m_needsCaretLayout) {
154         m_caretX = o.m_caretX;
155         m_caretY = o.m_caretY;
156         m_caretSize = o.m_caretSize;
157     }
158 }
159
160 void KHTMLSelection::init()
161 {
162     m_baseNode = 0;
163     m_baseOffset = 0;
164     m_extentNode = 0; 
165     m_extentOffset = 0;
166     m_startNode = 0;
167     m_startOffset = 0;
168     m_endNode = 0;
169     m_endOffset = 0;
170     m_state = NONE; 
171     m_caretX = 0;
172     m_caretY = 0;
173     m_caretSize = 0;
174     m_baseIsStart = true;
175     m_needsCaretLayout = true;
176     m_modifyBiasSet = false;
177 }
178
179 KHTMLSelection::~KHTMLSelection()
180 {
181     if (m_baseNode)
182         m_baseNode->deref();
183     if (m_extentNode)
184         m_extentNode->deref();
185     if (m_startNode)
186         m_startNode->deref();
187     if (m_endNode)
188         m_endNode->deref();
189 }
190
191 KHTMLSelection &KHTMLSelection::operator=(const KHTMLSelection &o)
192 {
193         setBaseNode(o.baseNode());
194         setExtentNode(o.extentNode());
195         setBaseOffset(o.baseOffset());
196         setExtentOffset(o.extentOffset());
197
198         setStartNode(o.startNode());
199         setEndNode(o.endNode());
200         setStartOffset(o.startOffset());
201         setEndOffset(o.endOffset());
202
203     m_state = o.m_state;
204
205     m_baseIsStart = o.m_baseIsStart;
206     m_needsCaretLayout = o.m_needsCaretLayout;
207     m_modifyBiasSet = o.m_modifyBiasSet;
208     
209     // Only copy the coordinates over if the other object
210     // has had a layout, otherwise keep the current
211     // coordinates. This prevents drawing artifacts from
212     // remaining when the caret is painted and then moves,
213     // and the old rectangle needs to be repainted.
214     if (!m_needsCaretLayout) {
215         m_caretX = o.m_caretX;
216         m_caretY = o.m_caretY;
217         m_caretSize = o.m_caretSize;
218     }
219     
220     return *this;
221 }
222
223 void KHTMLSelection::moveTo(DOM::NodeImpl *node, long offset)
224 {
225     moveTo(node, offset, node, offset);
226 }
227
228 void KHTMLSelection::moveTo(const DOM::Range &r)
229 {
230         moveTo(r.startContainer().handle(), r.startOffset(), 
231                 r.endContainer().handle(), r.endOffset());
232 }
233
234 void KHTMLSelection::moveTo(const DOM::DOMPosition &pos)
235 {
236         moveTo(pos.node(), pos.offset());
237 }
238
239 void KHTMLSelection::moveTo(const KHTMLSelection &o)
240 {
241         moveTo(o.baseNode(), o.baseOffset(), o.extentNode(), o.extentOffset());
242 }
243
244 void KHTMLSelection::moveTo(DOM::NodeImpl *baseNode, long baseOffset, DOM::NodeImpl *extentNode, long extentOffset)
245 {
246         setBaseNode(baseNode);
247         setExtentNode(extentNode);
248         setBaseOffset(baseOffset);
249         setExtentOffset(extentOffset);
250         validate();
251 }
252
253 bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity granularity)
254 {
255     DOMPosition pos;
256     
257     switch (dir) {
258         // EDIT FIXME: This needs to handle bidi
259         case RIGHT:
260         case FORWARD:
261             if (alter == EXTEND) {
262                 if (!m_modifyBiasSet) {
263                     m_modifyBiasSet = true;
264                     setBaseNode(startNode());
265                     setBaseOffset(startOffset());
266                     setExtentNode(endNode());
267                     setExtentOffset(endOffset());
268                 }
269                 if (granularity == CHARACTER)
270                     pos = extentPosition().nextCharacterPosition();
271                 else if (granularity == WORD)
272                     pos = extentPosition().nextWordPosition();
273             }
274             else {
275                 m_modifyBiasSet = false;
276                 if (state() == RANGE) {
277                     if (granularity == CHARACTER)
278                         pos = endPosition();
279                     else if (granularity == WORD)
280                         pos = extentPosition().nextWordPosition();
281                 }
282                 else {
283                     if (granularity == CHARACTER)
284                         pos = extentPosition().nextCharacterPosition();
285                     else if (granularity == WORD)
286                         pos = extentPosition().nextWordPosition();
287                 }
288             }
289             break;
290         // EDIT FIXME: This needs to handle bidi
291         case LEFT:
292         case BACKWARD:
293             if (alter == EXTEND) {
294                 if (!m_modifyBiasSet) {
295                     m_modifyBiasSet = true;
296                     setBaseNode(endNode());
297                     setBaseOffset(endOffset());
298                     setExtentNode(startNode());
299                     setExtentOffset(startOffset());
300                 }
301                 if (granularity == CHARACTER)
302                     pos = extentPosition().previousCharacterPosition();
303                 else if (granularity == WORD)
304                     pos = extentPosition().previousWordPosition();
305             }
306             else {
307                 m_modifyBiasSet = false;
308                 if (state() == RANGE) {
309                     if (granularity == CHARACTER)
310                         pos = startPosition();
311                     else if (granularity == WORD)
312                         pos = extentPosition().previousWordPosition();
313                 }
314                 else {
315                     if (granularity == CHARACTER)
316                         pos = extentPosition().previousCharacterPosition();
317                     else if (granularity == WORD)
318                         pos = extentPosition().previousWordPosition();
319                 }
320             }
321             break;
322         case UP:
323             if (alter == EXTEND) {
324                 if (!m_modifyBiasSet) {
325                     m_modifyBiasSet = true;
326                     setBaseNode(endNode());
327                     setBaseOffset(endOffset());
328                     setExtentNode(startNode());
329                     setExtentOffset(startOffset());
330                 }
331                 pos = extentPosition().previousLinePosition(xPosForVerticalArrowNavigation(EXTENT));
332             }
333             else {
334                 m_modifyBiasSet = false;
335                 pos = startPosition().previousLinePosition(xPosForVerticalArrowNavigation(START, state()==RANGE));
336             }
337             break;
338         case DOWN:
339             if (alter == EXTEND) {
340                 if (!m_modifyBiasSet) {
341                     m_modifyBiasSet = true;
342                     setBaseNode(startNode());
343                     setBaseOffset(startOffset());
344                     setExtentNode(endNode());
345                     setExtentOffset(endOffset());
346                 }
347                 pos = extentPosition().nextLinePosition(xPosForVerticalArrowNavigation(EXTENT));
348             }
349             else {
350                 m_modifyBiasSet = false;
351                 pos = endPosition().nextLinePosition(xPosForVerticalArrowNavigation(END, state()==RANGE));
352             }
353             break;
354     }
355     
356     if (pos.isEmpty())
357         return false;
358     
359     if (alter == MOVE)
360         moveTo(pos.node(), pos.offset());
361     else // alter == EXTEND
362         setExtent(pos.node(), pos.offset());
363     
364     return true;
365 }
366
367 bool KHTMLSelection::expandUsingGranularity(ETextGranularity granularity)
368 {
369     if (state() == NONE)
370         return false;
371         
372     validate(granularity);
373     return true;
374 }
375
376 int KHTMLSelection::xPosForVerticalArrowNavigation(EPositionType type, bool recalc) const
377 {
378     int x = 0;
379
380     if (state() == NONE)
381         return x;
382
383     DOMPosition pos;
384     switch (type) {
385         case START:
386             pos = startPosition();
387             break;
388         case END:
389             pos = endPosition();
390             break;
391         case BASE:
392             pos = basePosition();
393             break;
394         case EXTENT:
395             pos = extentPosition();
396             break;
397     }
398
399     KHTMLPart *part = pos.node()->getDocument()->part();
400     if (!part)
401         return x;
402         
403     if (recalc || part->xPosForVerticalArrowNavigation() == KHTMLPart::NoXPosForVerticalArrowNavigation) {
404         int y, w, h;
405         pos.node()->renderer()->caretPos(pos.offset(), true, x, y, w, h);
406         part->setXPosForVerticalArrowNavigation(x);
407     }
408     else {
409         x = part->xPosForVerticalArrowNavigation();
410     }
411
412     return x;
413 }
414
415 void KHTMLSelection::clear()
416 {
417         setBaseNode(0);
418         setExtentNode(0);
419         setBaseOffset(0);
420         setExtentOffset(0);
421         validate();
422 }
423
424 void KHTMLSelection::setBase(DOM::NodeImpl *node, long offset)
425 {
426         setBaseNode(node);
427         setBaseOffset(offset);
428         validate();
429 }
430
431 void KHTMLSelection::setExtent(DOM::NodeImpl *node, long offset)
432 {
433         setExtentNode(node);
434         setExtentOffset(offset);
435         validate();
436 }
437
438 void KHTMLSelection::setNeedsLayout(bool flag)
439 {
440     m_needsCaretLayout = flag;
441 }
442
443 Range KHTMLSelection::toRange() const
444 {
445     if (isEmpty())
446         return Range();
447
448     return Range(Node(startNode()), startOffset(), Node(endNode()), endOffset());
449 }
450
451 void KHTMLSelection::layoutCaret()
452 {
453     if (isEmpty() || !startNode()->renderer()) {
454         m_caretX = m_caretY = m_caretSize = 0;
455     }
456     else {
457         int w;
458         startNode()->renderer()->caretPos(startOffset(), true, m_caretX, m_caretY, w, m_caretSize);
459     }
460
461     m_needsCaretLayout = false;
462 }
463
464 QRect KHTMLSelection::getRepaintRect()
465 {
466     // EDIT FIXME: fudge a bit to make sure we don't leave behind artifacts
467     return QRect(m_caretX - 1, m_caretY - 1, 3, m_caretSize + 2);
468 }
469
470 void KHTMLSelection::needsCaretRepaint()
471 {
472     if (isEmpty())
473         return;
474
475     if (!startNode()->getDocument())
476         return;
477
478     KHTMLView *v = startNode()->getDocument()->view();
479     if (!v)
480         return;
481
482     if (m_needsCaretLayout) {
483         // repaint old position and calculate new position
484         v->updateContents(getRepaintRect(), false);
485         layoutCaret();
486         
487         // EDIT FIXME: This is an unfortunate hack.
488         // Basically, we can't trust this layout position since we 
489         // can't guarantee that the check to see if we are in unrendered 
490         // content will work at this point. We may have to wait for
491         // a layout and re-render of the document to happen. So, resetting this
492         // flag will cause another caret layout to happen the first time
493         // that we try to paint the caret after this call. That one will work since
494         // it happens after the document has accounted for any editing
495         // changes which may have been done.
496         // And, we need to leave this layout here so the caret moves right 
497         // away after clicking.
498         m_needsCaretLayout = true;
499     }
500     v->updateContents(getRepaintRect(), false);
501 }
502
503 void KHTMLSelection::paintCaret(QPainter *p, const QRect &rect)
504 {
505     if (isEmpty())
506         return;
507
508     if (m_state != CARET)
509         return;
510
511     if (m_needsCaretLayout) {
512         DOMPosition pos = DOMPosition(startNode(), startOffset());
513         if (!pos.inRenderedContent()) {
514             moveToRenderedContent();
515         }
516         layoutCaret();
517     }
518
519     QRect caretRect(m_caretX, m_caretY, 1, m_caretSize);
520     if (caretRect.intersects(rect)) {
521         QPen pen = p->pen();
522         pen.setStyle(Qt::SolidLine);
523         pen.setColor(Qt::black);
524         pen.setWidth(1);
525         p->setPen(pen);
526         p->drawLine(caretRect.left(), caretRect.top(), caretRect.left(), caretRect.bottom());
527     }
528 }
529
530 void KHTMLSelection::setBaseNode(DOM::NodeImpl *node)
531 {
532         if (m_baseNode == node)
533                 return;
534
535         if (m_baseNode)
536                 m_baseNode->deref();
537         
538         m_baseNode = node;
539         
540         if (m_baseNode)
541                 m_baseNode->ref();
542 }
543
544 void KHTMLSelection::setBaseOffset(long offset)
545 {
546         m_baseOffset = offset;
547 }
548
549 void KHTMLSelection::setExtentNode(DOM::NodeImpl *node)
550 {
551         if (m_extentNode == node)
552                 return;
553
554         if (m_extentNode)
555                 m_extentNode->deref();
556         
557         m_extentNode = node;
558         
559         if (m_extentNode)
560                 m_extentNode->ref();
561 }
562         
563 void KHTMLSelection::setExtentOffset(long offset)
564 {
565         m_extentOffset = offset;
566 }
567
568 void KHTMLSelection::setStartNode(DOM::NodeImpl *node)
569 {
570         if (m_startNode == node)
571                 return;
572
573         if (m_startNode)
574                 m_startNode->deref();
575         
576         m_startNode = node;
577         
578         if (m_startNode)
579                 m_startNode->ref();
580 }
581
582 void KHTMLSelection::setStartOffset(long offset)
583 {
584         m_startOffset = offset;
585 }
586
587 void KHTMLSelection::setEndNode(DOM::NodeImpl *node)
588 {
589         if (m_endNode == node)
590                 return;
591
592         if (m_endNode)
593                 m_endNode->deref();
594         
595         m_endNode = node;
596         
597         if (m_endNode)
598                 m_endNode->ref();
599 }
600         
601 void KHTMLSelection::setEndOffset(long offset)
602 {
603         m_endOffset = offset;
604 }
605
606 void KHTMLSelection::validate(ETextGranularity granularity)
607 {
608     // move the base and extent nodes to their equivalent leaf positions
609     bool baseAndExtentEqual = m_baseNode == m_extentNode && m_baseOffset == m_extentOffset;
610     if (m_baseNode) {
611         DOMPosition pos = basePosition().equivalentLeafPosition();
612         m_baseNode = pos.node();
613         m_baseOffset = pos.offset();
614         if (baseAndExtentEqual) {
615             m_extentNode = pos.node();
616             m_extentOffset = pos.offset();
617         }
618     }
619     if (m_extentNode && !baseAndExtentEqual) {
620         DOMPosition pos = extentPosition().equivalentLeafPosition();
621         m_extentNode = pos.node();
622         m_extentOffset = pos.offset();
623     }
624
625     // make sure we do not have a dangling start or end
626         if (!m_baseNode && !m_extentNode) {
627         setBaseOffset(0);
628         setExtentOffset(0);
629         m_baseIsStart = true;
630     }
631         else if (!m_baseNode) {
632                 setBaseNode(m_extentNode);
633                 setBaseOffset(m_extentOffset);
634         m_baseIsStart = true;
635         }
636         else if (!m_extentNode) {
637                 setExtentNode(m_baseNode);
638                 setExtentOffset(m_baseOffset);
639         m_baseIsStart = true;
640         }
641     else {
642         // adjust m_baseIsStart as needed
643         if (m_baseNode == m_extentNode) {
644             if (m_baseOffset > m_extentOffset)
645                 m_baseIsStart = false;
646             else 
647                 m_baseIsStart = true;
648         }
649         else if (nodeIsBeforeNode(m_baseNode, m_extentNode))
650             m_baseIsStart = true;
651         else
652             m_baseIsStart = false;
653     }
654
655     // calculate the correct start and end positions
656 #if !APPLE_CHANGES
657     if (m_baseIsStart) {
658         setStartNode(m_baseNode);
659         setStartOffset(m_baseOffset);
660         setEndNode(m_extentNode);
661         setEndOffset(m_extentOffset);
662     }
663     else {
664         setStartNode(m_extentNode);
665         setStartOffset(m_extentOffset);
666         setEndNode(m_baseNode);
667         setEndOffset(m_baseOffset);
668     }
669 #else
670     if (granularity == CHARACTER) {
671         if (m_baseIsStart) {
672             setStartNode(m_baseNode);
673             setStartOffset(m_baseOffset);
674             setEndNode(m_extentNode);
675             setEndOffset(m_extentOffset);
676         }
677         else {
678             setStartNode(m_extentNode);
679             setStartOffset(m_extentOffset);
680             setEndNode(m_baseNode);
681             setEndOffset(m_baseOffset);
682         }
683     }
684     else if (granularity == WORD) {
685         int baseStartOffset = m_baseOffset;
686         int baseEndOffset = m_baseOffset;
687         int extentStartOffset = m_extentOffset;
688         int extentEndOffset = m_extentOffset;
689         if (m_baseNode && (m_baseNode->nodeType() == Node::TEXT_NODE || m_baseNode->nodeType() == Node::CDATA_SECTION_NODE)) {
690             DOMString t = m_baseNode->nodeValue();
691             QChar *chars = t.unicode();
692             uint len = t.length();
693             KWQFindWordBoundary(chars, len, m_baseOffset, &baseStartOffset, &baseEndOffset);
694         }
695         if (m_extentNode && (m_extentNode->nodeType() == Node::TEXT_NODE || m_extentNode->nodeType() == Node::CDATA_SECTION_NODE)) {
696             DOMString t = m_extentNode->nodeValue();
697             QChar *chars = t.unicode();
698             uint len = t.length();
699             KWQFindWordBoundary(chars, len, m_extentOffset, &extentStartOffset, &extentEndOffset);
700         }
701         if (m_baseIsStart) {
702             setStartNode(m_baseNode);
703             setStartOffset(baseStartOffset);
704             setEndNode(m_extentNode);
705             setEndOffset(extentEndOffset);
706         }
707         else {
708             setStartNode(m_extentNode);
709             setStartOffset(extentStartOffset);
710             setEndNode(m_baseNode);
711             setEndOffset(baseEndOffset);
712         }
713     }
714     else {  // granularity == LINE
715         KHTMLSelection baseSelection = *this;
716         KHTMLSelection extentSelection = *this;
717         if (m_baseNode && (m_baseNode->nodeType() == Node::TEXT_NODE || m_baseNode->nodeType() == Node::CDATA_SECTION_NODE)) {
718             if (startAndEndLineNodesIncludingNode(m_baseNode, m_baseOffset, baseSelection)) {
719                 setStartNode(baseSelection.baseNode());
720                 setStartOffset(baseSelection.baseOffset());
721                 setEndNode(baseSelection.extentNode());
722                 setEndOffset(baseSelection.extentOffset());
723             }
724         }
725         if (m_extentNode && (m_extentNode->nodeType() == Node::TEXT_NODE || m_extentNode->nodeType() == Node::CDATA_SECTION_NODE)) {
726             if (startAndEndLineNodesIncludingNode(m_extentNode, m_extentOffset, extentSelection)) {
727                 setStartNode(extentSelection.baseNode());
728                 setStartOffset(extentSelection.baseOffset());
729                 setEndNode(extentSelection.extentNode());
730                 setEndOffset(extentSelection.extentOffset());
731             }
732         }
733         if (m_baseIsStart) {
734             setStartNode(baseSelection.startNode());
735             setStartOffset(baseSelection.startOffset());
736             setEndNode(extentSelection.endNode());
737             setEndOffset(extentSelection.endOffset());
738         }
739         else {
740             setStartNode(extentSelection.startNode());
741             setStartOffset(extentSelection.startOffset());
742             setEndNode(baseSelection.endNode());
743             setEndOffset(baseSelection.endOffset());
744         }
745     }
746 #endif  // APPLE_CHANGES
747
748         // adjust the state
749         if (!m_startNode && !m_endNode)
750                 m_state = NONE;
751         else if (m_startNode == m_endNode && m_startOffset == m_endOffset)
752                 m_state = CARET;
753         else
754                 m_state = RANGE;
755
756     m_needsCaretLayout = true;
757     
758 #if EDIT_DEBUG
759     debugPosition();
760 #endif
761 }
762
763 bool KHTMLSelection::moveToRenderedContent()
764 {
765     if (isEmpty())
766         return false;
767         
768     if (m_state != CARET)
769         return false;
770
771     DOMPosition pos = DOMPosition(startNode(), startOffset());
772     if (pos.inRenderedContent())
773         return true;
774         
775     // not currently rendered, try moving to prev
776     DOMPosition prev = pos.previousCharacterPosition();
777     if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node())) {
778         moveTo(prev);
779         return true;
780     }
781
782     // could not be moved to prev, try next
783     DOMPosition next = pos.nextCharacterPosition();
784     if (next != pos && next.node()->inSameContainingEditableBlock(pos.node())) {
785         moveTo(next);
786         return true;
787     }
788     
789     return false;
790 }
791
792 DOMPosition KHTMLSelection::basePosition() const
793 {
794     return DOMPosition(baseNode(), baseOffset());
795 }
796
797 DOMPosition KHTMLSelection::extentPosition() const
798 {
799     return DOMPosition(extentNode(), extentOffset());
800 }
801
802 DOMPosition KHTMLSelection::startPosition() const
803 {
804     return DOMPosition(startNode(), startOffset());
805 }
806
807 DOMPosition KHTMLSelection::endPosition() const
808 {
809     return DOMPosition(endNode(), endOffset());
810 }
811
812 bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
813 {
814         if (!n1 || !n2) 
815                 return true;
816  
817         if (n1 == n2)
818                 return true;
819  
820         bool result = false;
821     int n1Depth = 0;
822     int n2Depth = 0;
823
824     // First we find the depths of the two nodes in the tree (n1Depth, n2Depth)
825     DOM::NodeImpl *n = n1;
826     while (n->parentNode()) {
827         n = n->parentNode();
828         n1Depth++;
829     }
830     n = n2;
831     while (n->parentNode()) {
832         n = n->parentNode();
833         n2Depth++;
834     }
835     // Climb up the tree with the deeper node, until both nodes have equal depth
836     while (n2Depth > n1Depth) {
837         n2 = n2->parentNode();
838         n2Depth--;
839     }
840     while (n1Depth > n2Depth) {
841         n1 = n1->parentNode();
842         n1Depth--;
843     }
844     // Climb the tree with both n1 and n2 until they have the same parent
845     while (n1->parentNode() != n2->parentNode()) {
846         n1 = n1->parentNode();
847         n2 = n2->parentNode();
848     }
849     // Iterate through the parent's children until n1 or n2 is found
850     n = n1->parentNode() ? n1->parentNode()->firstChild() : n1->firstChild();
851     while (n) {
852         if (n == n1) {
853             result = true;
854             break;
855         }
856         else if (n == n2) {
857             result = false;
858             break;
859         }
860         n = n->nextSibling();
861     }
862         return result;
863 }
864
865 #if APPLE_CHANGES
866
867 static bool firstRunAt(RenderObject *renderNode, int y, NodeImpl *&startNode, long &startOffset)
868 {
869     for (RenderObject *n = renderNode; n; n = n->nextSibling()) {
870         if (n->isText()) {
871             RenderText *textRenderer = static_cast<khtml::RenderText *>(n);
872             for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
873                 if (box->m_y == y) {
874                     startNode = textRenderer->element();
875                     startOffset = box->m_start;
876                     return true;
877                 }
878             }
879         }
880         
881         if (firstRunAt(n->firstChild(), y, startNode, startOffset)) {
882             return true;
883         }
884     }
885     
886     return false;
887 }
888
889 static bool lastRunAt(RenderObject *renderNode, int y, NodeImpl *&endNode, long &endOffset)
890 {
891     RenderObject *n = renderNode;
892     if (!n) {
893         return false;
894     }
895     RenderObject *next;
896     while ((next = n->nextSibling())) {
897         n = next;
898     }
899     
900     while (1) {
901         if (lastRunAt(n->firstChild(), y, endNode, endOffset)) {
902             return true;
903         }
904     
905         if (n->isText()) {
906             RenderText *textRenderer =  static_cast<khtml::RenderText *>(n);
907             for (InlineTextBox* box = textRenderer->lastTextBox(); box; box = box->prevTextBox()) {
908                 if (box->m_y == y) {
909                     endNode = textRenderer->element();
910                     endOffset = box->m_start + box->m_len;
911                     return true;
912                 }
913             }
914         }
915         
916         if (n == renderNode) {
917             return false;
918         }
919         
920         n = n->previousSibling();
921     }
922 }
923
924 static bool startAndEndLineNodesIncludingNode(DOM::NodeImpl *node, int offset, KHTMLSelection &selection)
925 {
926     if (node && (node->nodeType() == Node::TEXT_NODE || node->nodeType() == Node::CDATA_SECTION_NODE)) {
927         int pos;
928         int selectionPointY;
929         RenderText *renderer = static_cast<RenderText *>(node->renderer());
930         InlineTextBox * run = renderer->findNextInlineTextBox( offset, pos );
931         DOMString t = node->nodeValue();
932         
933         if (!run)
934             return false;
935             
936         selectionPointY = run->m_y;
937         
938         // Go up to first non-inline element.
939         khtml::RenderObject *renderNode = renderer;
940         while (renderNode && renderNode->isInline())
941             renderNode = renderNode->parent();
942         
943         renderNode = renderNode->firstChild();
944         
945         DOM::NodeImpl *startNode = 0;
946         DOM::NodeImpl *endNode = 0;
947         long startOffset;
948         long endOffset;
949         
950         // Look for all the first child in the block that is on the same line
951         // as the selection point.
952         if (!firstRunAt (renderNode, selectionPointY, startNode, startOffset))
953             return false;
954     
955         // Look for all the last child in the block that is on the same line
956         // as the selection point.
957         if (!lastRunAt (renderNode, selectionPointY, endNode, endOffset))
958             return false;
959         
960         selection.moveTo(startNode, startOffset, endNode, endOffset);
961         
962         return true;
963     }
964     return false;
965 }
966
967 void KHTMLSelection::debugRenderer(RenderObject *r, bool selected) const
968 {
969     if (r->node()->isElementNode()) {
970         ElementImpl *element = static_cast<ElementImpl *>(r->node());
971         fprintf(stderr, "%s%s\n", selected ? "==> " : "    ", element->tagName().string().latin1());
972     }
973     else if (r->isText()) {
974         RenderText *textRenderer = static_cast<RenderText *>(r);
975         if (textRenderer->stringLength() == 0 || !textRenderer->firstTextBox()) {
976             fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : "    ");
977             return;
978         }
979         
980         static const int max = 36;
981         QString text = DOMString(textRenderer->string()).string();
982         int textLength = text.length();
983         if (selected) {
984             int offset = 0;
985             if (r->node() == startNode())
986                 offset = startOffset();
987             else if (r->node() == endNode())
988                 offset = endOffset();
989                 
990             int pos;
991             InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
992             text = text.mid(box->m_start, box->m_len);
993             
994             QString show;
995             int mid = max / 2;
996             int caret = 0;
997             
998             // text is shorter than max
999             if (textLength < max) {
1000                 show = text;
1001                 caret = pos;
1002             }
1003             
1004             // too few characters to left
1005             else if (pos - mid < 0) {
1006                 show = text.left(max - 3) + "...";
1007                 caret = pos;
1008             }
1009             
1010             // enough characters on each side
1011             else if (pos - mid >= 0 && pos + mid <= textLength) {
1012                 show = "..." + text.mid(pos - mid + 3, max - 6) + "...";
1013                 caret = mid;
1014             }
1015             
1016             // too few characters on right
1017             else {
1018                 show = "..." + text.right(max - 3);
1019                 caret = pos - (textLength - show.length());
1020             }
1021             
1022             show = show.replace("\n", " ");
1023             show = show.replace("\r", " ");
1024             fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.latin1(), pos);
1025             fprintf(stderr, "           ");
1026             for (int i = 0; i < caret; i++)
1027                 fprintf(stderr, " ");
1028             fprintf(stderr, "^\n");
1029         }
1030         else {
1031             if ((int)text.length() > max)
1032                 text = text.left(max - 3) + "...";
1033             else
1034                 text = text.left(max);
1035             fprintf(stderr, "    #text : \"%s\"\n", text.latin1());
1036         }
1037     }
1038 }
1039
1040 void KHTMLSelection::debugPosition() const
1041 {
1042     if (!startNode())
1043         return;
1044
1045     //static int context = 5;
1046     
1047     //RenderObject *r = 0;
1048
1049     fprintf(stderr, "KHTMLSelection =================\n");
1050
1051     if (startPosition() == endPosition()) {
1052         DOMPosition pos = startPosition();
1053         DOMPosition upstream = pos.equivalentUpstreamPosition();
1054         DOMPosition downstream = pos.equivalentDownstreamPosition();
1055         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
1056         fprintf(stderr, "pos:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
1057         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
1058     }
1059     else {
1060         DOMPosition pos = startPosition();
1061         DOMPosition upstream = pos.equivalentUpstreamPosition();
1062         DOMPosition downstream = pos.equivalentDownstreamPosition();
1063         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
1064         fprintf(stderr, "start:      %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
1065         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
1066         fprintf(stderr, "-----------------------------------\n");
1067         pos = endPosition();
1068         upstream = pos.equivalentUpstreamPosition();
1069         downstream = pos.equivalentDownstreamPosition();
1070         fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
1071         fprintf(stderr, "end:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
1072         fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
1073         fprintf(stderr, "-----------------------------------\n");
1074     }
1075           
1076 #if 0
1077     int back = 0;
1078     r = startNode()->renderer();
1079     for (int i = 0; i < context; i++, back++) {
1080         if (r->previousRenderer())
1081             r = r->previousRenderer();
1082         else
1083             break;
1084     }
1085     for (int i = 0; i < back; i++) {
1086         debugRenderer(r, false);
1087         r = r->nextRenderer();
1088     }
1089
1090
1091     fprintf(stderr, "\n");
1092
1093     if (startNode() == endNode())
1094         debugRenderer(startNode()->renderer(), true);
1095     else
1096         for (r = startNode()->renderer(); r && r != endNode()->renderer(); r = r->nextRenderer())
1097             debugRenderer(r, true);
1098     
1099     fprintf(stderr, "\n");
1100     
1101     r = endNode()->renderer();
1102     for (int i = 0; i < context; i++) {
1103         if (r->nextRenderer()) {
1104             r = r->nextRenderer();
1105             debugRenderer(r, false);
1106         }
1107         else
1108             break;
1109     }
1110 #endif
1111
1112     fprintf(stderr, "================================\n");
1113 }
1114
1115 #endif