LayoutTests:
[WebKit-https.git] / WebCore / editing / SelectionController.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 "config.h"
27 #include "SelectionController.h"
28
29 #include "Document.h"
30 #include "Element.h"
31 #include "EventNames.h"
32 #include "Frame.h"
33 #include "GraphicsContext.h"
34 #include "RenderView.h"
35 #include "TextIterator.h"
36 #include "htmlediting.h"
37 #include "visible_units.h"
38
39 #define EDIT_DEBUG 0
40
41 namespace WebCore {
42
43 using namespace EventNames;
44
45 SelectionController::SelectionController()
46     : m_needsLayout(true)
47 {
48     setSelection(Selection());
49 }
50
51 SelectionController::SelectionController(const Selection &sel)
52     : m_needsLayout(true)
53     , m_modifyBiasSet(false)
54 {
55     setSelection(sel);
56 }
57
58 SelectionController::SelectionController(const Position &pos, EAffinity affinity)
59     : m_needsLayout(true)
60     , m_modifyBiasSet(false)
61 {
62     setSelection(Selection(pos, pos, affinity));
63 }
64
65 SelectionController::SelectionController(const Range *r, EAffinity affinity)
66     : m_needsLayout(true)
67     , m_modifyBiasSet(false)
68 {
69     setSelection(Selection(startPosition(r), endPosition(r), affinity));
70 }
71
72 SelectionController::SelectionController(const Position &base, const Position &extent, EAffinity affinity)
73     : m_needsLayout(true)
74     , m_modifyBiasSet(false)
75 {
76     setSelection(Selection(base, extent, affinity));
77 }
78
79 SelectionController::SelectionController(const VisiblePosition &visiblePos)
80     : m_needsLayout(true)
81     , m_modifyBiasSet(false)
82 {
83     setSelection(Selection(visiblePos.deepEquivalent(), visiblePos.deepEquivalent(), visiblePos.affinity()));
84 }
85
86 SelectionController::SelectionController(const VisiblePosition &base, const VisiblePosition &extent)
87     : m_needsLayout(true)
88     , m_modifyBiasSet(false)
89 {
90     setSelection(Selection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()));
91 }
92
93 SelectionController::SelectionController(const SelectionController &o)
94     : m_needsLayout(o.m_needsLayout)
95     , m_modifyBiasSet(o.m_modifyBiasSet)
96 {
97     setSelection(o.m_sel); 
98     // Only copy the coordinates over if the other object
99     // has had a layout, otherwise keep the current
100     // coordinates. This prevents drawing artifacts from
101     // remaining when the caret is painted and then moves,
102     // and the old rectangle needs to be repainted.
103     if (!m_needsLayout) {
104         m_caretRect = o.m_caretRect;
105         m_caretPositionOnLayout = o.m_caretPositionOnLayout;
106     }
107 }
108
109 SelectionController &SelectionController::operator=(const SelectionController &o)
110 {
111     setSelection(o.m_sel);
112
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_caretPositionOnLayout = o.m_caretPositionOnLayout;
124     }
125     
126     return *this;
127 }
128
129 void SelectionController::moveTo(const VisiblePosition &pos)
130 {
131     setSelection(Selection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()));
132     m_needsLayout = true;
133 }
134
135 void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent)
136 {
137     setSelection(Selection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()));
138     m_needsLayout = true;
139 }
140
141 void SelectionController::moveTo(const SelectionController &o)
142 {
143     setSelection(o.m_sel);
144     m_needsLayout = true;
145 }
146
147 void SelectionController::moveTo(const Position &pos, EAffinity affinity)
148 {
149     setSelection(Selection(pos, affinity));
150     m_needsLayout = true;
151 }
152
153 void SelectionController::moveTo(const Range *r, EAffinity affinity)
154 {
155     setSelection(Selection(startPosition(r), endPosition(r), affinity));
156     m_needsLayout = true;
157 }
158
159 void SelectionController::moveTo(const Position &base, const Position &extent, EAffinity affinity)
160 {
161     setSelection(Selection(base, extent, affinity));
162     m_needsLayout = true;
163 }
164
165 void SelectionController::setSelection(const Selection& newSelection)
166 {
167     m_sel = newSelection;
168 }
169
170 void SelectionController::nodeWillBeRemoved(Node *node)
171 {
172     if (isNone())
173         return;
174     
175     Node* base = m_sel.base().node();
176     Node* extent = m_sel.extent().node();
177     Node* start = m_sel.start().node();
178     Node* end = m_sel.end().node();
179     
180     bool baseRemoved = node == base || (base && base->isAncestor(node));
181     bool extentRemoved = node == extent || (extent && extent->isAncestor(node));
182     bool startRemoved = node == start || (start && start->isAncestor(node));
183     bool endRemoved = node == end || (end && end->isAncestor(node));
184     
185     bool clearSelection = false;
186     if (startRemoved || endRemoved) {
187         // FIXME: When endpoints are removed, we should just alter the selection, instead of blowing it away.
188         clearSelection = true;
189     // FIXME: This could be more efficient if we had an isNodeInRange function on Ranges.
190     } else if (Range::compareBoundaryPoints(m_sel.start(), Position(node, 0)) == -1 &&
191                Range::compareBoundaryPoints(m_sel.end(), Position(node, 0)) == 1) {
192         // If we did nothing here, when this node's renderer was destroyed, the rect that it 
193         // occupied would be invalidated, but, selection gaps that change as a result of 
194         // the removal wouldn't be invalidated.
195         // FIXME: Don't do so much unnecessary invalidation.
196         clearSelection = true;
197     } else if (baseRemoved || extentRemoved) {
198         if (m_sel.isBaseFirst()) {
199             m_sel.setBase(m_sel.start());
200             m_sel.setExtent(m_sel.end());
201         } else {
202             m_sel.setBase(m_sel.start());
203             m_sel.setExtent(m_sel.end());
204         }
205     }
206
207     if (clearSelection) {
208         // FIXME (6498): This doesn't notify the editing delegate of a selection change.
209         RefPtr<Document> document = start->document();
210         document->updateRendering();
211         if (RenderView* view = static_cast<RenderView*>(document->renderer()))
212             view->clearSelection();
213         setSelection(Selection());
214     }
215 }
216
217 void SelectionController::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_sel.setBase(m_sel.start());
231                         m_sel.setExtent(m_sel.end());
232                         break;
233                     case LEFT:
234                     case BACKWARD:
235                         m_sel.setBase(m_sel.end());
236                         m_sel.setExtent(m_sel.start());
237                         break;
238                 }
239             }
240             break;
241     }
242 }
243
244 VisiblePosition SelectionController::modifyExtendingRightForward(TextGranularity granularity)
245 {
246     VisiblePosition pos(m_sel.extent(), m_sel.affinity());
247     switch (granularity) {
248         case CharacterGranularity:
249             if (isLastVisiblePositionBeforeTableElement(pos.deepEquivalent()))
250                 pos = VisiblePosition(positionAfterFollowingTableElement(pos.deepEquivalent()), VP_DEFAULT_AFFINITY);
251             else
252                 pos = pos.next();
253             break;
254         case WordGranularity:
255             if (isLastVisiblePositionBeforeTableElement(pos.deepEquivalent()))
256                 pos = VisiblePosition(positionAfterFollowingTableElement(pos.deepEquivalent()), VP_DEFAULT_AFFINITY);
257             else
258                 pos = nextWordPosition(pos);
259             break;
260         case SentenceGranularity:
261             pos = nextSentencePosition(pos);
262             break;
263         case LineGranularity:
264             pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
265             break;
266         case ParagraphGranularity:
267             pos = nextParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
268             break;
269         case SentenceBoundary:
270             pos = endOfSentence(VisiblePosition(m_sel.end(), m_sel.affinity()));
271             break;
272         case LineBoundary:
273             pos = endOfLine(VisiblePosition(m_sel.end(), m_sel.affinity()));
274             break;
275         case ParagraphBoundary:
276             pos = endOfParagraph(VisiblePosition(m_sel.end(), m_sel.affinity()));
277             break;
278         case DocumentBoundary:
279             pos = endOfDocument(pos);
280             break;
281     }
282     
283     return pos;
284 }
285
286 VisiblePosition SelectionController::modifyMovingRightForward(TextGranularity granularity)
287 {
288     VisiblePosition pos;
289     // FIXME: Stay in editable content for the less common granularities.
290     switch (granularity) {
291         case CharacterGranularity:
292             if (isRange()) 
293                 pos = VisiblePosition(m_sel.end(), m_sel.affinity());
294             else
295                 pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).next(true);
296             break;
297         case WordGranularity:
298             pos = nextWordPosition(VisiblePosition(m_sel.extent(), m_sel.affinity()));
299             break;
300         case SentenceGranularity:
301             pos = nextSentencePosition(VisiblePosition(m_sel.extent(), m_sel.affinity()));
302             break;
303         case LineGranularity: {
304             // down-arrowing from a range selection that ends at the start of a line needs
305             // to leave the selection at that line start (no need to call nextLinePosition!)
306             pos = VisiblePosition(m_sel.end(), m_sel.affinity());
307             if (!isRange() || !isStartOfLine(pos))
308                 pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(END, isRange()));
309             break;
310         }
311         case ParagraphGranularity:
312             pos = nextParagraphPosition(VisiblePosition(m_sel.end(), m_sel.affinity()), xPosForVerticalArrowNavigation(END, isRange()));
313             break;
314         case SentenceBoundary:
315             pos = endOfSentence(VisiblePosition(m_sel.end(), m_sel.affinity()));
316             break;
317         case LineBoundary:
318             pos = endOfLine(VisiblePosition(m_sel.end(), m_sel.affinity()));
319             break;
320         case ParagraphBoundary:
321             pos = endOfParagraph(VisiblePosition(m_sel.end(), m_sel.affinity()));
322             break;
323         case DocumentBoundary:
324             pos = endOfDocument(VisiblePosition(m_sel.end(), m_sel.affinity()));
325             break;
326     }
327     return pos;
328 }
329
330 VisiblePosition SelectionController::modifyExtendingLeftBackward(TextGranularity granularity)
331 {
332     VisiblePosition pos(m_sel.extent(), m_sel.affinity());
333         
334     // Extending a selection backward by word or character from just after a table selects
335     // the table.  This "makes sense" from the user perspective, esp. when deleting.
336     // It was done here instead of in VisiblePosition because we want VPs to iterate
337     // over everything.
338     switch (granularity) {
339         case CharacterGranularity:
340             if (isFirstVisiblePositionAfterTableElement(pos.deepEquivalent()))
341                 pos = VisiblePosition(positionBeforePrecedingTableElement(pos.deepEquivalent()), VP_DEFAULT_AFFINITY);
342             else
343                 pos = pos.previous();
344             break;
345         case WordGranularity:
346             if (isFirstVisiblePositionAfterTableElement(pos.deepEquivalent()))
347                 pos = VisiblePosition(positionBeforePrecedingTableElement(pos.deepEquivalent()), VP_DEFAULT_AFFINITY);
348             else
349                 pos = previousWordPosition(pos);
350             break;
351         case SentenceGranularity:
352             pos = previousSentencePosition(pos);
353             break;
354         case LineGranularity:
355             pos = previousLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
356             break;
357         case ParagraphGranularity:
358             pos = previousParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
359             break;
360         case SentenceBoundary:
361             pos = startOfSentence(VisiblePosition(m_sel.start(), m_sel.affinity()));
362             break;
363         case LineBoundary:
364             pos = startOfLine(VisiblePosition(m_sel.start(), m_sel.affinity()));
365             break;
366         case ParagraphBoundary:
367             pos = startOfParagraph(VisiblePosition(m_sel.start(), m_sel.affinity()));
368             break;
369         case DocumentBoundary:
370             pos = startOfDocument(pos);
371             break;
372     }
373     return pos;
374 }
375
376 VisiblePosition SelectionController::modifyMovingLeftBackward(TextGranularity granularity)
377 {
378     VisiblePosition pos;
379     // FIXME: Stay in editable content for the less common granularities.
380     switch (granularity) {
381         case CharacterGranularity:
382             if (isRange()) 
383                 pos = VisiblePosition(m_sel.start(), m_sel.affinity());
384             else
385                 pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).previous(true);
386             break;
387         case WordGranularity:
388             pos = previousWordPosition(VisiblePosition(m_sel.extent(), m_sel.affinity()));
389             break;
390         case SentenceGranularity:
391             pos = previousSentencePosition(VisiblePosition(m_sel.extent(), m_sel.affinity()));
392             break;
393         case LineGranularity:
394             pos = previousLinePosition(VisiblePosition(m_sel.start(), m_sel.affinity()), xPosForVerticalArrowNavigation(START, isRange()));
395             break;
396         case ParagraphGranularity:
397             pos = previousParagraphPosition(VisiblePosition(m_sel.start(), m_sel.affinity()), xPosForVerticalArrowNavigation(START, isRange()));
398             break;
399         case SentenceBoundary:
400             pos = startOfSentence(VisiblePosition(m_sel.start(), m_sel.affinity()));
401             break;
402         case LineBoundary:
403             pos = startOfLine(VisiblePosition(m_sel.start(), m_sel.affinity()));
404             break;
405         case ParagraphBoundary:
406             pos = startOfParagraph(VisiblePosition(m_sel.start(), m_sel.affinity()));
407             break;
408         case DocumentBoundary:
409             pos = startOfDocument(VisiblePosition(m_sel.start(), m_sel.affinity()));
410             break;
411     }
412     return pos;
413 }
414
415 bool SelectionController::modify(const String &alterString, const String &directionString, const String &granularityString)
416 {
417     String alterStringLower = alterString.lower();
418     EAlter alter;
419     if (alterStringLower == "extend")
420         alter = EXTEND;
421     else if (alterStringLower == "move")
422         alter = MOVE;
423     else 
424         return false;
425     
426     String directionStringLower = directionString.lower();
427     EDirection direction;
428     if (directionStringLower == "forward")
429         direction = FORWARD;
430     else if (directionStringLower == "backward")
431         direction = BACKWARD;
432     else if (directionStringLower == "left")
433         direction = LEFT;
434     else if (directionStringLower == "right")
435         direction = RIGHT;
436     else
437         return false;
438         
439     String granularityStringLower = granularityString.lower();
440     TextGranularity granularity;
441     if (granularityStringLower == "character")
442         granularity = CharacterGranularity;
443     else if (granularityStringLower == "word")
444         granularity = WordGranularity;
445     else if (granularityStringLower == "sentence")
446         granularity = SentenceGranularity;
447     else if (granularityStringLower == "line")
448         granularity = LineGranularity;
449     else if (granularityStringLower == "paragraph")
450         granularity = ParagraphGranularity;
451     else
452         return false;
453                 
454     return modify(alter, direction, granularity);
455 }
456
457 bool SelectionController::modify(EAlter alter, EDirection dir, TextGranularity granularity)
458 {
459     if (frame())
460         frame()->setSelectionGranularity(granularity);
461     
462     setModifyBias(alter, dir);
463
464     VisiblePosition pos;
465     switch (dir) {
466         // EDIT FIXME: These need to handle bidi
467         case RIGHT:
468         case FORWARD:
469             if (alter == EXTEND)
470                 pos = modifyExtendingRightForward(granularity);
471             else
472                 pos = modifyMovingRightForward(granularity);
473             break;
474         case LEFT:
475         case BACKWARD:
476             if (alter == EXTEND)
477                 pos = modifyExtendingLeftBackward(granularity);
478             else
479                 pos = modifyMovingLeftBackward(granularity);
480             break;
481     }
482
483     if (pos.isNull())
484         return false;
485
486     switch (alter) {
487         case MOVE:
488             moveTo(pos);
489             break;
490         case EXTEND:
491             setExtent(pos);
492             break;
493     }
494
495     setNeedsLayout();
496
497     return true;
498 }
499
500 // FIXME: Maybe baseline would be better?
501 static bool caretY(const VisiblePosition &c, int &y)
502 {
503     Position p = c.deepEquivalent();
504     Node *n = p.node();
505     if (!n)
506         return false;
507     RenderObject *r = p.node()->renderer();
508     if (!r)
509         return false;
510     IntRect rect = r->caretRect(p.offset());
511     if (rect.isEmpty())
512         return false;
513     y = rect.y() + rect.height() / 2;
514     return true;
515 }
516
517 bool SelectionController::modify(EAlter alter, int verticalDistance)
518 {
519     if (verticalDistance == 0)
520         return false;
521
522     bool up = verticalDistance < 0;
523     if (up)
524         verticalDistance = -verticalDistance;
525
526     setModifyBias(alter, up ? BACKWARD : FORWARD);
527
528     VisiblePosition pos;
529     int xPos = 0;
530     switch (alter) {
531         case MOVE:
532             pos = VisiblePosition(up ? m_sel.start() : m_sel.end(), m_sel.affinity());
533             xPos = xPosForVerticalArrowNavigation(up ? START : END, isRange());
534             m_sel.setAffinity(up ? UPSTREAM : DOWNSTREAM);
535             break;
536         case EXTEND:
537             pos = VisiblePosition(m_sel.extent(), m_sel.affinity());
538             xPos = xPosForVerticalArrowNavigation(EXTENT);
539             m_sel.setAffinity(DOWNSTREAM);
540             break;
541     }
542
543     int startY;
544     if (!caretY(pos, startY))
545         return false;
546     if (up)
547         startY = -startY;
548     int lastY = startY;
549
550     VisiblePosition result;
551     VisiblePosition next;
552     for (VisiblePosition p = pos; ; p = next) {
553         next = (up ? previousLinePosition : nextLinePosition)(p, xPos);
554         if (next.isNull() || next == p)
555             break;
556         int nextY;
557         if (!caretY(next, nextY))
558             break;
559         if (up)
560             nextY = -nextY;
561         if (nextY - startY > verticalDistance)
562             break;
563         if (nextY >= lastY) {
564             lastY = nextY;
565             result = next;
566         }
567     }
568
569     if (result.isNull())
570         return false;
571
572     switch (alter) {
573         case MOVE:
574             moveTo(result);
575             break;
576         case EXTEND:
577             setExtent(result);
578             break;
579     }
580
581     return true;
582 }
583
584 bool SelectionController::expandUsingGranularity(TextGranularity granularity)
585 {
586     if (isNone())
587         return false;
588
589     m_sel.expandUsingGranularity(granularity);
590     m_needsLayout = true;
591     return true;
592 }
593
594 int SelectionController::xPosForVerticalArrowNavigation(EPositionType type, bool recalc) const
595 {
596     int x = 0;
597
598     if (isNone())
599         return x;
600
601     Position pos;
602     switch (type) {
603         case START:
604             pos = m_sel.start();
605             break;
606         case END:
607             pos = m_sel.end();
608             break;
609         case BASE:
610             pos = m_sel.base();
611             break;
612         case EXTENT:
613             pos = m_sel.extent();
614             break;
615     }
616
617     Frame *frame = pos.node()->document()->frame();
618     if (!frame)
619         return x;
620         
621     if (recalc || frame->xPosForVerticalArrowNavigation() == Frame::NoXPosForVerticalArrowNavigation) {
622         pos = VisiblePosition(pos, m_sel.affinity()).deepEquivalent();
623         x = pos.node()->renderer()->caretRect(pos.offset(), m_sel.affinity()).x();
624         frame->setXPosForVerticalArrowNavigation(x);
625     }
626     else {
627         x = frame->xPosForVerticalArrowNavigation();
628     }
629     return x;
630 }
631
632 void SelectionController::clear()
633 {
634     setSelection(Selection());
635     m_needsLayout = true;
636 }
637
638 void SelectionController::setBase(const VisiblePosition &pos)
639 {
640     setSelection(Selection(pos.deepEquivalent(), m_sel.extent(), pos.affinity()));
641     m_needsLayout = true;
642 }
643
644 void SelectionController::setExtent(const VisiblePosition &pos)
645 {
646     setSelection(Selection(m_sel.base(), pos.deepEquivalent(), pos.affinity()));
647     m_needsLayout = true;
648 }
649
650 void SelectionController::setBase(const Position &pos, EAffinity affinity)
651 {
652     setSelection(Selection(pos, m_sel.extent(), affinity));
653     m_needsLayout = true;
654 }
655
656 void SelectionController::setExtent(const Position &pos, EAffinity affinity)
657 {
658     setSelection(Selection(m_sel.base(), pos, affinity));
659     m_needsLayout = true;
660 }
661
662 void SelectionController::setNeedsLayout(bool flag)
663 {
664     m_needsLayout = flag;
665 }
666
667 String SelectionController::type() const
668 {
669     if (isNone())
670         return "None";
671     else if (isCaret())
672         return "Caret";
673     else
674         return "Range";
675 }
676
677 String SelectionController::toString() const
678 {
679     return String(plainText(m_sel.toRange().get()));
680 }
681
682 PassRefPtr<Range> SelectionController::getRangeAt(int index) const
683 {
684     return index == 0 ? m_sel.toRange() : 0;
685 }
686
687 Frame *SelectionController::frame() const
688 {
689     return !isNone() ? m_sel.start().node()->document()->frame() : 0;
690 }
691
692 void SelectionController::setBaseAndExtent(Node *baseNode, int baseOffset, Node *extentNode, int extentOffset)
693 {
694     VisiblePosition visibleBase = VisiblePosition(baseNode, baseOffset, DOWNSTREAM);
695     VisiblePosition visibleExtent = VisiblePosition(extentNode, extentOffset, DOWNSTREAM);
696     
697     moveTo(visibleBase, visibleExtent);
698 }
699
700 void SelectionController::setPosition(Node *node, int offset)
701 {
702     moveTo(VisiblePosition(node, offset, DOWNSTREAM));
703 }
704
705 void SelectionController::collapse(Node *node, int offset)
706 {
707     moveTo(VisiblePosition(node, offset, DOWNSTREAM));
708 }
709
710 void SelectionController::collapseToEnd()
711 {
712     moveTo(VisiblePosition(m_sel.end(), DOWNSTREAM));
713 }
714
715 void SelectionController::collapseToStart()
716 {
717     moveTo(VisiblePosition(m_sel.start(), DOWNSTREAM));
718 }
719
720 void SelectionController::empty()
721 {
722     moveTo(SelectionController());
723 }
724
725 void SelectionController::extend(Node *node, int offset)
726 {
727     moveTo(VisiblePosition(node, offset, DOWNSTREAM));
728 }
729
730 void SelectionController::layout()
731 {
732     if (isNone() || !m_sel.start().node()->inDocument() || !m_sel.end().node()->inDocument()) {
733         m_caretRect = IntRect();
734         m_caretPositionOnLayout = IntPoint();
735         return;
736     }
737
738     m_sel.start().node()->document()->updateRendering();
739     
740     m_caretRect = IntRect();
741     m_caretPositionOnLayout = IntPoint();
742         
743     if (isCaret()) {
744         Position pos = m_sel.start();
745         pos = VisiblePosition(m_sel.start(), m_sel.affinity()).deepEquivalent();
746         if (pos.isNotNull()) {
747             ASSERT(pos.node()->renderer());
748             m_caretRect = pos.node()->renderer()->caretRect(pos.offset(), m_sel.affinity());
749             
750             int x, y;
751             pos.node()->renderer()->absolutePositionForContent(x, y);
752             m_caretPositionOnLayout = IntPoint(x, y);
753         }
754     }
755
756     m_needsLayout = false;
757 }
758
759 IntRect SelectionController::caretRect() const
760 {
761     if (m_needsLayout)
762         const_cast<SelectionController *>(this)->layout();
763     
764     IntRect caret = m_caretRect;
765
766     if (m_sel.start().node() && m_sel.start().node()->renderer()) {
767         int x, y;
768         m_sel.start().node()->renderer()->absolutePositionForContent(x, y);
769         caret.move(IntPoint(x, y) - m_caretPositionOnLayout);
770     }
771
772     return caret;
773 }
774
775 IntRect SelectionController::caretRepaintRect() const
776 {
777     // FIXME: Add one pixel of slop on each side to make sure we don't leave behind artifacts.
778     IntRect r = caretRect();
779     if (r.isEmpty())
780         return IntRect();
781     return IntRect(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2);
782 }
783
784 void SelectionController::needsCaretRepaint()
785 {
786     if (!isCaret())
787         return;
788
789     FrameView *v = m_sel.start().node()->document()->view();
790     if (!v)
791         return;
792
793     if (m_needsLayout) {
794         // repaint old position and calculate new position
795         v->updateContents(caretRepaintRect(), false);
796         layout();
797         
798         // EDIT FIXME: This is an unfortunate hack.
799         // Basically, we can't trust this layout position since we 
800         // can't guarantee that the check to see if we are in unrendered 
801         // content will work at this point. We may have to wait for
802         // a layout and re-render of the document to happen. So, resetting this
803         // flag will cause another caret layout to happen the first time
804         // that we try to paint the caret after this call. That one will work since
805         // it happens after the document has accounted for any editing
806         // changes which may have been done.
807         // And, we need to leave this layout here so the caret moves right 
808         // away after clicking.
809         m_needsLayout = true;
810     }
811     v->updateContents(caretRepaintRect(), false);
812 }
813
814 void SelectionController::paintCaret(GraphicsContext *p, const IntRect &rect)
815 {
816     if (! m_sel.isCaret())
817         return;
818
819     if (m_needsLayout)
820         layout();
821         
822     IntRect caret = intersection(caretRect(), rect);
823     if (!caret.isEmpty())
824         p->fillRect(caret, Color::black);
825 }
826
827 void SelectionController::debugRenderer(RenderObject *r, bool selected) const
828 {
829     if (r->node()->isElementNode()) {
830         Element *element = static_cast<Element *>(r->node());
831         fprintf(stderr, "%s%s\n", selected ? "==> " : "    ", element->localName().deprecatedString().latin1());
832     }
833     else if (r->isText()) {
834         RenderText *textRenderer = static_cast<RenderText *>(r);
835         if (textRenderer->stringLength() == 0 || !textRenderer->firstTextBox()) {
836             fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : "    ");
837             return;
838         }
839         
840         static const int max = 36;
841         DeprecatedString text = String(textRenderer->string()).deprecatedString();
842         int textLength = text.length();
843         if (selected) {
844             int offset = 0;
845             if (r->node() == m_sel.start().node())
846                 offset = m_sel.start().offset();
847             else if (r->node() == m_sel.end().node())
848                 offset = m_sel.end().offset();
849                 
850             int pos;
851             InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
852             text = text.mid(box->m_start, box->m_len);
853             
854             DeprecatedString show;
855             int mid = max / 2;
856             int caret = 0;
857             
858             // text is shorter than max
859             if (textLength < max) {
860                 show = text;
861                 caret = pos;
862             }
863             
864             // too few characters to left
865             else if (pos - mid < 0) {
866                 show = text.left(max - 3) + "...";
867                 caret = pos;
868             }
869             
870             // enough characters on each side
871             else if (pos - mid >= 0 && pos + mid <= textLength) {
872                 show = "..." + text.mid(pos - mid + 3, max - 6) + "...";
873                 caret = mid;
874             }
875             
876             // too few characters on right
877             else {
878                 show = "..." + text.right(max - 3);
879                 caret = pos - (textLength - show.length());
880             }
881             
882             show.replace('\n', ' ');
883             show.replace('\r', ' ');
884             fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.latin1(), pos);
885             fprintf(stderr, "           ");
886             for (int i = 0; i < caret; i++)
887                 fprintf(stderr, " ");
888             fprintf(stderr, "^\n");
889         }
890         else {
891             if ((int)text.length() > max)
892                 text = text.left(max - 3) + "...";
893             else
894                 text = text.left(max);
895             fprintf(stderr, "    #text : \"%s\"\n", text.latin1());
896         }
897     }
898 }
899
900 #ifndef NDEBUG
901
902 void SelectionController::formatForDebugger(char* buffer, unsigned length) const
903 {
904     m_sel.formatForDebugger(buffer, length);
905 }
906
907 void SelectionController::showTreeForThis() const
908 {
909     m_sel.showTreeForThis();
910 }
911
912 #endif
913
914 }
915
916 #ifndef NDEBUG
917
918 void showTree(const WebCore::SelectionController& sel)
919 {
920     sel.showTreeForThis();
921 }
922
923 void showTree(const WebCore::SelectionController* sel)
924 {
925     if (sel)
926         sel->showTreeForThis();
927 }
928
929 #endif