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