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