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