Reviewed by Dave Harrison.
[WebKit-https.git] / WebCore / khtml / editing / selection.cpp
1 /*
2  * Copyright (C) 2004 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 "selection.h"
27
28 #include <qevent.h>
29 #include <qpainter.h>
30 #include <qrect.h>
31
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"
47
48 #if APPLE_CHANGES
49 #include "KWQAssertions.h"
50 #else
51 #define ASSERT(assertion) assert(assertion)
52 #endif
53
54 #define EDIT_DEBUG 0
55
56 using DOM::DOMString;
57 using DOM::ElementImpl;
58 using DOM::Node;
59 using DOM::NodeImpl;
60 using DOM::Position;
61 using DOM::Range;
62 using DOM::RangeImpl;
63 using DOM::StayInBlock;
64 using DOM::DoNotStayInBlock;
65
66 namespace khtml {
67
68 Selection::Selection()
69 {
70     init(DOWNSTREAM);
71 }
72
73 Selection::Selection(const Position &pos, EAffinity affinity)
74     : m_base(pos), m_extent(pos)
75 {
76     init(affinity);
77     validate();
78 }
79
80 Selection::Selection(const Range &r, EAffinity baseAffinity, EAffinity extentAffinity)
81     : m_base(startPosition(r)), m_extent(endPosition(r))
82 {
83     init(baseAffinity);
84     validate();
85 }
86
87 Selection::Selection(const Position &base, EAffinity baseAffinity, const Position &extent, EAffinity extentAffinity)
88     : m_base(base), m_extent(extent)
89 {
90     init(baseAffinity);
91     validate();
92 }
93
94 Selection::Selection(const VisiblePosition &visiblePos)
95     : m_base(visiblePos.position()), m_extent(visiblePos.position())
96 {
97     init(visiblePos.affinity());
98     validate();
99 }
100
101 Selection::Selection(const VisiblePosition &base, const VisiblePosition &extent)
102     : m_base(base.position()), m_extent(extent.position())
103 {
104     init(base.affinity());
105     validate();
106 }
107
108 Selection::Selection(const Selection &o)
109     : m_base(o.m_base), m_extent(o.m_extent)
110     , m_start(o.m_start), m_end(o.m_end)
111     , m_state(o.m_state), m_affinity(o.m_affinity)
112     , m_baseIsStart(o.m_baseIsStart)
113     , m_needsLayout(o.m_needsLayout)
114     , m_modifyBiasSet(o.m_modifyBiasSet)
115 {
116     // Only copy the coordinates over if the other object
117     // has had a layout, otherwise keep the current
118     // coordinates. This prevents drawing artifacts from
119     // remaining when the caret is painted and then moves,
120     // and the old rectangle needs to be repainted.
121     if (!m_needsLayout) {
122         m_caretRect = o.m_caretRect;
123         m_expectedVisibleRect = o.m_expectedVisibleRect;
124     }
125 }
126
127 void Selection::init(EAffinity affinity)
128 {
129     // FIXME: set extentAffinity
130     m_state = NONE; 
131     m_baseIsStart = true;
132     m_affinity = affinity;
133     m_needsLayout = true;
134     m_modifyBiasSet = false;
135 }
136
137 Selection &Selection::operator=(const Selection &o)
138 {
139     m_base = o.m_base;
140     m_extent = o.m_extent;
141     m_start = o.m_start;
142     m_end = o.m_end;
143
144     m_state = o.m_state;
145     m_affinity = o.m_affinity;
146
147     m_baseIsStart = o.m_baseIsStart;
148     m_needsLayout = o.m_needsLayout;
149     m_modifyBiasSet = o.m_modifyBiasSet;
150     
151     // Only copy the coordinates over if the other object
152     // has had a layout, otherwise keep the current
153     // coordinates. This prevents drawing artifacts from
154     // remaining when the caret is painted and then moves,
155     // and the old rectangle needs to be repainted.
156     if (!m_needsLayout) {
157         m_caretRect = o.m_caretRect;
158         m_expectedVisibleRect = o.m_expectedVisibleRect;
159     }
160     
161     return *this;
162 }
163
164 void Selection::moveTo(const VisiblePosition &pos)
165 {
166     // FIXME: use extentAffinity
167     m_affinity = pos.affinity();
168     m_base = pos.deepEquivalent();
169     m_extent = pos.deepEquivalent();
170     validate();
171 }
172
173 void Selection::moveTo(const VisiblePosition &base, const VisiblePosition &extent)
174 {
175     // FIXME: use extentAffinity
176     m_affinity = base.affinity();
177     m_base = base.deepEquivalent();
178     m_extent = extent.deepEquivalent();
179     validate();
180 }
181
182 void Selection::moveTo(const Selection &o)
183 {
184     // FIXME: copy extentAffinity
185     m_affinity = o.m_affinity;
186     m_base = o.m_start;
187     m_extent = o.m_end;
188     validate();
189 }
190
191 void Selection::moveTo(const Position &pos, EAffinity affinity)
192 {
193     // FIXME: use extentAffinity
194     m_affinity = affinity;
195     m_base = pos;
196     m_extent = pos;
197     validate();
198 }
199
200 void Selection::moveTo(const Range &r, EAffinity baseAffinity, EAffinity extentAffinity)
201 {
202     // FIXME: use extentAffinity
203     m_affinity = baseAffinity;
204     m_base = startPosition(r);
205     m_extent = endPosition(r);
206     validate();
207 }
208
209 void Selection::moveTo(const Position &base, EAffinity baseAffinity, const Position &extent, EAffinity extentAffinity)
210 {
211     // FIXME: use extentAffinity
212     m_affinity = baseAffinity;
213     m_base = base;
214     m_extent = extent;
215     validate();
216 }
217
218 void Selection::setModifyBias(EAlter alter, EDirection direction)
219 {
220     switch (alter) {
221         case MOVE:
222             m_modifyBiasSet = false;
223             break;
224         case EXTEND:
225             if (!m_modifyBiasSet) {
226                 m_modifyBiasSet = true;
227                 switch (direction) {
228                     // FIXME: right for bidi?
229                     case RIGHT:
230                     case FORWARD:
231                         m_base = m_start;
232                         m_extent = m_end;
233                         break;
234                     case LEFT:
235                     case BACKWARD:
236                         m_base = m_end;
237                         m_extent = m_start;
238                         break;
239                 }
240             }
241             break;
242     }
243 }
244
245 VisiblePosition Selection::modifyExtendingRightForward(ETextGranularity granularity)
246 {
247     VisiblePosition pos(m_extent, m_affinity);
248     switch (granularity) {
249         case CHARACTER:
250             pos = pos.next();
251             break;
252         case WORD:
253             pos = nextWordPosition(pos);
254             break;
255         case PARAGRAPH:
256             pos = nextParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
257             break;
258         case LINE:
259             pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
260             break;
261         case LINE_BOUNDARY:
262             pos = endOfLine(VisiblePosition(m_end, m_affinity));
263             break;
264         case PARAGRAPH_BOUNDARY:
265             pos = endOfParagraph(VisiblePosition(m_end, m_affinity));
266             break;
267         case DOCUMENT_BOUNDARY:
268             pos = endOfDocument(pos);
269             break;
270     }
271     
272     return pos;
273 }
274
275 VisiblePosition Selection::modifyMovingRightForward(ETextGranularity granularity)
276 {
277     VisiblePosition pos;
278     switch (granularity) {
279         case CHARACTER:
280             if (isRange()) 
281                 pos = VisiblePosition(m_end, m_affinity);
282             else
283                 pos = VisiblePosition(m_extent, m_affinity).next();
284             break;
285         case WORD:
286             pos = nextWordPosition(VisiblePosition(m_extent, m_affinity));
287             break;
288         case PARAGRAPH:
289             pos = nextParagraphPosition(VisiblePosition(m_end, m_affinity), xPosForVerticalArrowNavigation(END, isRange()));
290             break;
291         case LINE: {
292             // down-arrowing from a range selection that ends at the start of a line needs
293             // to leave the selection at that line start (no need to call nextLinePosition!)
294             pos = VisiblePosition(m_end, m_affinity);
295             if (!isRange() || !isStartOfLine(pos))
296                 pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(END, isRange()));
297             break;
298         }
299         case LINE_BOUNDARY:
300             pos = endOfLine(VisiblePosition(m_end, m_affinity));
301             break;
302         case PARAGRAPH_BOUNDARY:
303             pos = endOfParagraph(VisiblePosition(m_end, m_affinity));
304             break;
305         case DOCUMENT_BOUNDARY:
306             pos = endOfDocument(VisiblePosition(m_end, m_affinity));
307             break;
308     }
309     return pos;
310 }
311
312 VisiblePosition Selection::modifyExtendingLeftBackward(ETextGranularity granularity)
313 {
314     VisiblePosition pos(m_extent, m_affinity);
315     switch (granularity) {
316         case CHARACTER:
317             pos = pos.previous();
318             break;
319         case WORD:
320             pos = previousWordPosition(pos);
321             break;
322         case PARAGRAPH:
323             pos = previousParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
324             break;
325         case LINE:
326             pos = previousLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
327             break;
328         case LINE_BOUNDARY:
329             pos = startOfLine(VisiblePosition(m_start, m_affinity));
330             break;
331         case PARAGRAPH_BOUNDARY:
332             pos = startOfParagraph(VisiblePosition(m_start, m_affinity));
333             break;
334         case DOCUMENT_BOUNDARY:
335             pos = startOfDocument(pos);
336             break;
337     }
338     return pos;
339 }
340
341 VisiblePosition Selection::modifyMovingLeftBackward(ETextGranularity granularity)
342 {
343     VisiblePosition pos;
344     switch (granularity) {
345         case CHARACTER:
346             if (isRange()) 
347                 pos = VisiblePosition(m_start, m_affinity);
348             else
349                 pos = VisiblePosition(m_extent, m_affinity).previous();
350             break;
351         case WORD:
352             pos = previousWordPosition(VisiblePosition(m_extent, m_affinity));
353             break;
354         case PARAGRAPH:
355             pos = previousParagraphPosition(VisiblePosition(m_start, m_affinity), xPosForVerticalArrowNavigation(START, isRange()));
356             break;
357         case LINE:
358             pos = previousLinePosition(VisiblePosition(m_start, m_affinity), xPosForVerticalArrowNavigation(START, isRange()));
359             break;
360         case LINE_BOUNDARY:
361             pos = startOfLine(VisiblePosition(m_start, m_affinity));
362             break;
363         case PARAGRAPH_BOUNDARY:
364             pos = startOfParagraph(VisiblePosition(m_start, m_affinity));
365             break;
366         case DOCUMENT_BOUNDARY:
367             pos = startOfDocument(VisiblePosition(m_start, m_affinity));
368             break;
369     }
370     return pos;
371 }
372
373 bool Selection::modify(EAlter alter, EDirection dir, ETextGranularity granularity)
374 {
375     setModifyBias(alter, dir);
376
377     VisiblePosition pos;
378     switch (dir) {
379         // EDIT FIXME: These need to handle bidi
380         case RIGHT:
381         case FORWARD:
382             if (alter == EXTEND)
383                 pos = modifyExtendingRightForward(granularity);
384             else
385                 pos = modifyMovingRightForward(granularity);
386             break;
387         case LEFT:
388         case BACKWARD:
389             if (alter == EXTEND)
390                 pos = modifyExtendingLeftBackward(granularity);
391             else
392                 pos = modifyMovingLeftBackward(granularity);
393             break;
394     }
395
396     if (pos.isNull())
397         return false;
398
399     switch (alter) {
400         case MOVE:
401             moveTo(pos);
402             break;
403         case EXTEND:
404             setExtent(pos);
405             break;
406     }
407
408     setNeedsLayout();
409
410     return true;
411 }
412
413 // FIXME: Maybe baseline would be better?
414 static bool caretY(const VisiblePosition &c, int &y)
415 {
416     Position p = c.deepEquivalent();
417     NodeImpl *n = p.node();
418     if (!n)
419         return false;
420     RenderObject *r = p.node()->renderer();
421     if (!r)
422         return false;
423     QRect rect = r->caretRect(p.offset());
424     if (rect.isEmpty())
425         return false;
426     y = rect.y() + rect.height() / 2;
427     return true;
428 }
429
430 bool Selection::modify(EAlter alter, int verticalDistance)
431 {
432     if (verticalDistance == 0)
433         return false;
434
435     bool up = verticalDistance < 0;
436     if (up)
437         verticalDistance = -verticalDistance;
438
439     // can dump this UPSTREAM when we have m_extentAffinity
440     m_affinity = UPSTREAM;
441     setModifyBias(alter, up ? BACKWARD : FORWARD);
442
443     VisiblePosition pos;
444     int xPos = 0;
445     switch (alter) {
446         case MOVE:
447             pos = VisiblePosition(up ? m_start : m_end, m_affinity);
448             xPos = xPosForVerticalArrowNavigation(up ? START : END, isRange());
449             break;
450         case EXTEND:
451             pos = VisiblePosition(m_extent, m_affinity);
452             xPos = xPosForVerticalArrowNavigation(EXTENT);
453             break;
454     }
455
456     int startY;
457     if (!caretY(pos, startY))
458         return false;
459     if (up)
460         startY = -startY;
461     int lastY = startY;
462
463     VisiblePosition result;
464     VisiblePosition next;
465     for (VisiblePosition p = pos; ; p = next) {
466         next = (up ? previousLinePosition : nextLinePosition)(p, xPos);
467         if (next.isNull() || next == p)
468             break;
469         int nextY;
470         if (!caretY(next, nextY))
471             break;
472         if (up)
473             nextY = -nextY;
474         if (nextY - startY > verticalDistance)
475             break;
476         if (nextY >= lastY) {
477             lastY = nextY;
478             result = next;
479         }
480     }
481
482     if (result.isNull())
483         return false;
484
485     switch (alter) {
486         case MOVE:
487             moveTo(result);
488             break;
489         case EXTEND:
490             setExtent(result);
491             break;
492     }
493
494     return true;
495 }
496
497 bool Selection::expandUsingGranularity(ETextGranularity granularity)
498 {
499     if (isNone())
500         return false;
501     validate(granularity);
502     return true;
503 }
504
505 int Selection::xPosForVerticalArrowNavigation(EPositionType type, bool recalc) const
506 {
507     int x = 0;
508
509     if (isNone())
510         return x;
511
512     Position pos;
513     switch (type) {
514         case START:
515             pos = m_start;
516             break;
517         case END:
518             pos = m_end;
519             break;
520         case BASE:
521             pos = m_base;
522             break;
523         case EXTENT:
524             pos = m_extent;
525             break;
526     }
527
528     KHTMLPart *part = pos.node()->getDocument()->part();
529     if (!part)
530         return x;
531         
532     if (recalc || part->xPosForVerticalArrowNavigation() == KHTMLPart::NoXPosForVerticalArrowNavigation) {
533         switch (m_affinity) {
534             case DOWNSTREAM:
535                 pos = VisiblePosition(pos, m_affinity).downstreamDeepEquivalent();
536                 break;
537             case UPSTREAM:
538                 pos = VisiblePosition(pos, m_affinity).deepEquivalent();
539                 break;
540         }
541         x = pos.node()->renderer()->caretRect(pos.offset(), m_affinity).x();
542         part->setXPosForVerticalArrowNavigation(x);
543     }
544     else {
545         x = part->xPosForVerticalArrowNavigation();
546     }
547     return x;
548 }
549
550 void Selection::clear()
551 {
552     m_affinity = SEL_DEFAULT_AFFINITY;
553     m_base.clear();
554     m_extent.clear();
555     validate();
556 }
557
558 void Selection::setBase(const VisiblePosition &pos)
559 {
560     m_affinity = pos.affinity();
561     m_base = pos.deepEquivalent();
562     validate();
563 }
564
565 void Selection::setExtent(const VisiblePosition &pos)
566 {
567     // FIXME: Support extentAffinity
568     m_extent = pos.deepEquivalent();
569     validate();
570 }
571
572 void Selection::setBaseAndExtent(const VisiblePosition &base, const VisiblePosition &extent)
573 {
574     // FIXME: Support extentAffinity
575     m_affinity = base.affinity();
576     m_base = base.deepEquivalent();
577     m_extent = extent.deepEquivalent();
578     validate();
579 }
580
581
582 void Selection::setBase(const Position &pos, EAffinity baseAffinity)
583 {
584     m_affinity = baseAffinity;
585     m_base = pos;
586     validate();
587 }
588
589 void Selection::setExtent(const Position &pos, EAffinity extentAffinity)
590 {
591     // FIXME: Support extentAffinity for real
592     m_affinity = extentAffinity;
593     m_extent = pos;
594     validate();
595 }
596
597 void Selection::setBaseAndExtent(const Position &base, EAffinity baseAffinity, const Position &extent, EAffinity extentAffinity)
598 {
599     // FIXME: extentAffinity
600     m_affinity = baseAffinity;
601     m_base = base;
602     m_extent = extent;
603     validate();
604 }
605
606 void Selection::setNeedsLayout(bool flag)
607 {
608     m_needsLayout = flag;
609 }
610
611 Range Selection::toRange() const
612 {
613     if (isNone())
614         return Range();
615
616     // Make sure we have an updated layout since this function is called
617     // in the course of running edit commands which modify the DOM.
618     // Failing to call this can result in equivalentXXXPosition calls returning
619     // incorrect results.
620     m_start.node()->getDocument()->updateLayout();
621
622     Position s, e;
623     if (isCaret()) {
624         // If the selection is a caret, move the range start upstream. This helps us match
625         // the conventions of text editors tested, which make style determinations based
626         // on the character before the caret, if any. 
627         s = m_start.upstream(StayInBlock).equivalentRangeCompliantPosition();
628         e = s;
629     }
630     else {
631         // If the selection is a range, select the minimum range that encompasses the selection.
632         // Again, this is to match the conventions of text editors tested, which make style 
633         // determinations based on the first character of the selection. 
634         // For instance, this operation helps to make sure that the "X" selected below is the 
635         // only thing selected. The range should not be allowed to "leak" out to the end of the 
636         // previous text node, or to the beginning of the next text node, each of which has a 
637         // different style.
638         // 
639         // On a treasure map, <b>X</b> marks the spot.
640         //                       ^ selected
641         //
642         ASSERT(isRange());
643         s = m_start.downstream(StayInBlock);
644         e = m_end.upstream(StayInBlock);
645         if (RangeImpl::compareBoundaryPoints(s.node(), s.offset(), e.node(), e.offset()) > 0) {
646             // Make sure the start is before the end.
647             // The end can wind up before the start if collapsed whitespace is the only thing selected.
648             Position tmp = s;
649             s = e;
650             e = tmp;
651         }
652         s = s.equivalentRangeCompliantPosition();
653         e = e.equivalentRangeCompliantPosition();
654     }
655
656     // Use this roundabout way of creating the Range in order to have defined behavior
657     // when there is a DOM exception.
658     int exceptionCode = 0;
659     Range result(s.node()->getDocument());
660     RangeImpl *handle = result.handle();
661     ASSERT(handle);
662     handle->setStart(s.node(), s.offset(), exceptionCode);
663     if (exceptionCode) {
664         ERROR("Exception setting Range start from Selection: %d", exceptionCode);
665         return Range();
666     }
667     handle->setEnd(e.node(), e.offset(), exceptionCode);
668     if (exceptionCode) {
669         ERROR("Exception setting Range end from Selection: %d", exceptionCode);
670         return Range();
671     }
672     return result;
673 }
674
675 void Selection::layout()
676 {
677     if (isNone() || !m_start.node()->inDocument() || !m_end.node()->inDocument()) {
678         m_caretRect = QRect();
679         m_expectedVisibleRect = QRect();
680         return;
681     }
682
683     m_start.node()->getDocument()->updateRendering();
684         
685     if (isCaret()) {
686         Position pos = m_start;
687         switch (m_affinity) {
688             case DOWNSTREAM:
689                 pos = VisiblePosition(m_start, m_affinity).downstreamDeepEquivalent();
690                 break;
691             case UPSTREAM:
692                 pos = VisiblePosition(m_start, m_affinity).deepEquivalent();
693                 break;
694         }
695         if (pos.isNotNull()) {
696             ASSERT(pos.node()->renderer());
697             m_caretRect = pos.node()->renderer()->caretRect(pos.offset(), m_affinity);
698             m_expectedVisibleRect = m_caretRect;
699         }
700         else {
701             m_caretRect = QRect();
702             m_expectedVisibleRect = QRect();
703         }
704     }
705     else {
706         // Calculate which position to use based on whether the base is the start.
707         // We want the position, start or end, that was calculated using the extent. 
708         // This makes the selection follow the extent position while scrolling as a 
709         // result of arrow navigation. 
710         //
711         // Note: no need to get additional help from VisiblePosition. The m_start and
712         // m_end positions should already be visible, and we're only interested in 
713         // a rectangle for m_expectedVisibleRect, hence affinity is not a factor
714         // like it is when drawing a caret.
715         //
716         Position pos = m_baseIsStart ? m_end : m_start;
717         ASSERT(pos.node()->renderer()); 
718         m_expectedVisibleRect = pos.node()->renderer()->caretRect(pos.offset(), m_affinity);
719         m_caretRect = QRect();
720     }
721
722     m_needsLayout = false;
723 }
724
725 QRect Selection::caretRect() const
726 {
727     if (m_needsLayout) {
728         const_cast<Selection *>(this)->layout();
729     }
730
731     return m_caretRect;
732 }
733
734 QRect Selection::expectedVisibleRect() const
735 {
736     if (m_needsLayout) {
737         const_cast<Selection *>(this)->layout();
738     }
739
740     return m_expectedVisibleRect;
741 }
742
743 QRect Selection::caretRepaintRect() const
744 {
745     // FIXME: Add one pixel of slop on each side to make sure we don't leave behind artifacts.
746     QRect r = caretRect();
747     if (r.isEmpty())
748         return QRect();
749     return QRect(r.left() - 1, r.top() - 1, r.width() + 2, r.height() + 2);
750 }
751
752 void Selection::needsCaretRepaint()
753 {
754     if (!isCaret())
755         return;
756
757     if (!m_start.node()->getDocument())
758         return;
759
760     KHTMLView *v = m_start.node()->getDocument()->view();
761     if (!v)
762         return;
763
764     if (m_needsLayout) {
765         // repaint old position and calculate new position
766         v->updateContents(caretRepaintRect(), false);
767         layout();
768         
769         // EDIT FIXME: This is an unfortunate hack.
770         // Basically, we can't trust this layout position since we 
771         // can't guarantee that the check to see if we are in unrendered 
772         // content will work at this point. We may have to wait for
773         // a layout and re-render of the document to happen. So, resetting this
774         // flag will cause another caret layout to happen the first time
775         // that we try to paint the caret after this call. That one will work since
776         // it happens after the document has accounted for any editing
777         // changes which may have been done.
778         // And, we need to leave this layout here so the caret moves right 
779         // away after clicking.
780         m_needsLayout = true;
781     }
782     v->updateContents(caretRepaintRect(), false);
783 }
784
785 void Selection::paintCaret(QPainter *p, const QRect &rect)
786 {
787     if (m_state != CARET)
788         return;
789
790     if (m_needsLayout)
791         layout();
792
793     if (m_caretRect.isValid())
794         p->fillRect(m_caretRect & rect, QBrush());
795 }
796
797 void Selection::validate(ETextGranularity granularity)
798 {
799     // Move the selection to rendered positions, if possible.
800     Position originalBase(m_base);
801     bool baseAndExtentEqual = m_base == m_extent;
802     bool updatedLayout = false;
803     if (m_base.isNotNull()) {
804         m_base.node()->getDocument()->updateLayout();
805         updatedLayout = true;
806         m_base = VisiblePosition(m_base, m_affinity).deepEquivalent();
807         if (baseAndExtentEqual)
808             m_extent = m_base;
809     }
810     if (m_extent.isNotNull() && !baseAndExtentEqual) {
811         if (!updatedLayout)
812             m_extent.node()->getDocument()->updateLayout();
813         m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent();
814     }
815
816     // Make sure we do not have a dangling start or end
817     if (m_base.isNull() && m_extent.isNull()) {
818         // Move the position to the enclosingBlockFlowElement of the original base, if possible.
819         // This has the effect of flashing the caret somewhere when a rendered position for
820         // the base and extent cannot be found.
821         if (originalBase.isNotNull()) {
822             Position pos(originalBase.node()->enclosingBlockFlowElement(), 0);
823             m_base = pos;
824             m_extent = pos;
825         }
826         else {
827             // We have no position to work with. See if the BODY element of the page
828             // is contentEditable. If it is, put the caret there.
829             //NodeImpl *node = document()
830             m_start.clear();
831             m_end.clear();
832         }
833         m_baseIsStart = true;
834     }
835     else if (m_base.isNull()) {
836         m_base = m_extent;
837         m_baseIsStart = true;
838     }
839     else if (m_extent.isNull()) {
840         m_extent = m_base;
841         m_baseIsStart = true;
842     }
843     else {
844         m_baseIsStart = RangeImpl::compareBoundaryPoints(m_base.node(), m_base.offset(), m_extent.node(), m_extent.offset()) <= 0;
845     }
846
847     m_start.clear();
848     m_end.clear();
849
850     // calculate the correct start and end positions
851     switch (granularity) {
852         case CHARACTER:
853             if (m_baseIsStart) {
854                 m_start = m_base;
855                 m_end = m_extent;
856             } else {
857                 m_start = m_extent;
858                 m_end = m_base;
859             }
860             break;
861         case WORD: {
862             // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary).
863             // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in
864             // the document, select that last word (LeftWordIfOnBoundary).
865             // Edge case: If the caret is after the last word in a paragraph, select from the the end of the
866             // last word to the line break (also RightWordIfOnBoundary);
867             VisiblePosition start = m_baseIsStart ? VisiblePosition(m_base, m_affinity)   : VisiblePosition(m_extent, m_affinity);
868             VisiblePosition end   = m_baseIsStart ? VisiblePosition(m_extent, m_affinity) : VisiblePosition(m_base, m_affinity);
869             EWordSide side = RightWordIfOnBoundary;
870             if (isEndOfDocument(start) || (isEndOfLine(start) && !isStartOfLine(start) && !isEndOfParagraph(start)))
871                 side = LeftWordIfOnBoundary;
872             m_start = startOfWord(start, side).deepEquivalent();
873             side = RightWordIfOnBoundary;
874             if (isEndOfDocument(end) || (isEndOfLine(end) && !isStartOfLine(end) && !isEndOfParagraph(end)))
875                 side = LeftWordIfOnBoundary;
876             m_end = endOfWord(end, side).deepEquivalent();
877             
878             break;
879             }
880         case LINE:
881         case LINE_BOUNDARY:
882             if (m_baseIsStart) {
883                 m_start = startOfLine(VisiblePosition(m_base, m_affinity)).deepEquivalent();
884                 m_end = endOfLine(VisiblePosition(m_extent, m_affinity), IncludeLineBreak).deepEquivalent();
885             } else {
886                 m_start = startOfLine(VisiblePosition(m_extent, m_affinity)).deepEquivalent();
887                 m_end = endOfLine(VisiblePosition(m_base, m_affinity), IncludeLineBreak).deepEquivalent();
888             }
889             break;
890         case PARAGRAPH:
891             if (m_baseIsStart) {
892                 VisiblePosition pos(m_base, m_affinity);
893                 if (isStartOfLine(pos) && isEndOfDocument(pos))
894                     pos = pos.previous();
895                 m_start = startOfParagraph(pos).deepEquivalent();
896                 m_end = endOfParagraph(VisiblePosition(m_extent, m_affinity), IncludeLineBreak).deepEquivalent();
897             } else {
898                 m_start = startOfParagraph(VisiblePosition(m_extent, m_affinity)).deepEquivalent();
899                 m_end = endOfParagraph(VisiblePosition(m_base, m_affinity), IncludeLineBreak).deepEquivalent();
900             }
901             break;
902         case DOCUMENT_BOUNDARY:
903             m_start = startOfDocument(VisiblePosition(m_base, m_affinity)).deepEquivalent();
904             m_end = endOfDocument(VisiblePosition(m_base, m_affinity)).deepEquivalent();
905             break;
906         case PARAGRAPH_BOUNDARY:
907             if (m_baseIsStart) {
908                 m_start = startOfParagraph(VisiblePosition(m_base, m_affinity)).deepEquivalent();
909                 m_end = endOfParagraph(VisiblePosition(m_extent, m_affinity)).deepEquivalent();
910             } else {
911                 m_start = startOfParagraph(VisiblePosition(m_extent, m_affinity)).deepEquivalent();
912                 m_end = endOfParagraph(VisiblePosition(m_base, m_affinity)).deepEquivalent();
913             }
914             break;
915     }
916
917     // adjust the state
918     if (m_start.isNull()) {
919         ASSERT(m_end.isNull());
920         m_state = NONE;
921     }
922     else if (m_start == m_end || m_start.upstream(StayInBlock) == m_end.upstream(StayInBlock)) {
923         m_state = CARET;
924     }
925     else {
926         m_state = RANGE;
927         // "Constrain" the selection to be the smallest equivalent range of nodes.
928         // This is a somewhat arbitrary choice, but experience shows that it is
929         // useful to make to make the selection "canonical" (if only for
930         // purposes of comparing selections). This is an ideal point of the code
931         // to do this operation, since all selection changes that result in a RANGE 
932         // come through here before anyone uses it.
933         m_start = m_start.downstream(StayInBlock);
934         m_end = m_end.upstream(StayInBlock);
935     }
936
937     m_needsLayout = true;
938     
939 #if EDIT_DEBUG
940     debugPosition();
941 #endif
942 }
943
944 void Selection::debugRenderer(RenderObject *r, bool selected) const
945 {
946     if (r->node()->isElementNode()) {
947         ElementImpl *element = static_cast<ElementImpl *>(r->node());
948         fprintf(stderr, "%s%s\n", selected ? "==> " : "    ", element->tagName().string().latin1());
949     }
950     else if (r->isText()) {
951         RenderText *textRenderer = static_cast<RenderText *>(r);
952         if (textRenderer->stringLength() == 0 || !textRenderer->firstTextBox()) {
953             fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : "    ");
954             return;
955         }
956         
957         static const int max = 36;
958         QString text = DOMString(textRenderer->string()).string();
959         int textLength = text.length();
960         if (selected) {
961             int offset = 0;
962             if (r->node() == m_start.node())
963                 offset = m_start.offset();
964             else if (r->node() == m_end.node())
965                 offset = m_end.offset();
966                 
967             int pos;
968             InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
969             text = text.mid(box->m_start, box->m_len);
970             
971             QString show;
972             int mid = max / 2;
973             int caret = 0;
974             
975             // text is shorter than max
976             if (textLength < max) {
977                 show = text;
978                 caret = pos;
979             }
980             
981             // too few characters to left
982             else if (pos - mid < 0) {
983                 show = text.left(max - 3) + "...";
984                 caret = pos;
985             }
986             
987             // enough characters on each side
988             else if (pos - mid >= 0 && pos + mid <= textLength) {
989                 show = "..." + text.mid(pos - mid + 3, max - 6) + "...";
990                 caret = mid;
991             }
992             
993             // too few characters on right
994             else {
995                 show = "..." + text.right(max - 3);
996                 caret = pos - (textLength - show.length());
997             }
998             
999             show.replace('\n', ' ');
1000             show.replace('\r', ' ');
1001             fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.latin1(), pos);
1002             fprintf(stderr, "           ");
1003             for (int i = 0; i < caret; i++)
1004                 fprintf(stderr, " ");
1005             fprintf(stderr, "^\n");
1006         }
1007         else {
1008             if ((int)text.length() > max)
1009                 text = text.left(max - 3) + "...";
1010             else
1011                 text = text.left(max);
1012             fprintf(stderr, "    #text : \"%s\"\n", text.latin1());
1013         }
1014     }
1015 }
1016
1017 void Selection::debugPosition() const
1018 {
1019     if (!m_start.node())
1020         return;
1021
1022     //static int context = 5;
1023     
1024     //RenderObject *r = 0;
1025
1026     fprintf(stderr, "Selection =================\n");
1027
1028     if (m_start == m_end) {
1029         Position pos = m_start;
1030         Position upstream = pos.upstream(DoNotStayInBlock);
1031         Position downstream = pos.downstream(DoNotStayInBlock);
1032         fprintf(stderr, "upstream:   %s %p:%ld\n", upstream.node()->nodeName().string().latin1(), upstream.node(), upstream.offset());
1033         fprintf(stderr, "pos:        %s %p:%ld\n", pos.node()->nodeName().string().latin1(), pos.node(), pos.offset());
1034         fprintf(stderr, "downstream: %s %p:%ld\n", downstream.node()->nodeName().string().latin1(), downstream.node(), downstream.offset());
1035     }
1036     else {
1037         Position pos = m_start;
1038         Position upstream = pos.upstream(DoNotStayInBlock);
1039         Position downstream = pos.downstream(DoNotStayInBlock);
1040         fprintf(stderr, "upstream:   %s %p:%ld\n", upstream.node()->nodeName().string().latin1(), upstream.node(), upstream.offset());
1041         fprintf(stderr, "start:      %s %p:%ld\n", pos.node()->nodeName().string().latin1(), pos.node(), pos.offset());
1042         fprintf(stderr, "downstream: %s %p:%ld\n", downstream.node()->nodeName().string().latin1(), downstream.node(), downstream.offset());
1043         fprintf(stderr, "-----------------------------------\n");
1044         pos = m_end;
1045         upstream = pos.upstream(DoNotStayInBlock);
1046         downstream = pos.downstream(DoNotStayInBlock);
1047         fprintf(stderr, "upstream:   %s %p:%ld\n", upstream.node()->nodeName().string().latin1(), upstream.node(), upstream.offset());
1048         fprintf(stderr, "end:        %s %p:%ld\n", pos.node()->nodeName().string().latin1(), pos.node(), pos.offset());
1049         fprintf(stderr, "downstream: %s %p:%ld\n", downstream.node()->nodeName().string().latin1(), downstream.node(), downstream.offset());
1050         fprintf(stderr, "-----------------------------------\n");
1051     }
1052           
1053 #if 0
1054     int back = 0;
1055     r = m_start.node()->renderer();
1056     for (int i = 0; i < context; i++, back++) {
1057         if (r->previousRenderer())
1058             r = r->previousRenderer();
1059         else
1060             break;
1061     }
1062     for (int i = 0; i < back; i++) {
1063         debugRenderer(r, false);
1064         r = r->nextRenderer();
1065     }
1066
1067
1068     fprintf(stderr, "\n");
1069
1070     if (m_start.node() == m_end.node())
1071         debugRenderer(m_start.node()->renderer(), true);
1072     else
1073         for (r = m_start.node()->renderer(); r && r != m_end.node()->renderer(); r = r->nextRenderer())
1074             debugRenderer(r, true);
1075     
1076     fprintf(stderr, "\n");
1077     
1078     r = m_end.node()->renderer();
1079     for (int i = 0; i < context; i++) {
1080         if (r->nextRenderer()) {
1081             r = r->nextRenderer();
1082             debugRenderer(r, false);
1083         }
1084         else
1085             break;
1086     }
1087 #endif
1088
1089     fprintf(stderr, "================================\n");
1090 }
1091
1092 #ifndef NDEBUG
1093 #define FormatBufferSize 1024
1094 void Selection::formatForDebugger(char *buffer, unsigned length) const
1095 {
1096     DOMString result;
1097     DOMString s;
1098     
1099     if (isNone()) {
1100         result = "<none>";
1101     }
1102     else {
1103         char s[FormatBufferSize];
1104         result += "from ";
1105         m_start.formatForDebugger(s, FormatBufferSize);
1106         result += s;
1107         result += " to ";
1108         m_end.formatForDebugger(s, FormatBufferSize);
1109         result += s;
1110     }
1111           
1112     strncpy(buffer, result.string().latin1(), length - 1);
1113 }
1114 #undef FormatBufferSize
1115 #endif
1116
1117 } // namespace DOM