Re-applied the fix for bug 8835 (REGRESSION: Line moves but selection
[WebKit-https.git] / WebCore / rendering / RootInlineBox.cpp
1 /**
2 * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 2003, 2006 Apple Computer, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 #include "config.h"
22 #include "RootInlineBox.h"
23
24 #include "EllipsisBox.h"
25 #include "RenderBlock.h"
26
27 using namespace std;
28
29 namespace WebCore {
30
31 void RootInlineBox::destroy(RenderArena* arena)
32 {
33     detachEllipsisBox(arena);
34     InlineFlowBox::destroy(arena);
35 }
36
37 void RootInlineBox::detachEllipsisBox(RenderArena* arena)
38 {
39     if (m_ellipsisBox) {
40         m_ellipsisBox->destroy(arena);
41         m_ellipsisBox = 0;
42     }
43 }
44
45 void RootInlineBox::clearTruncation()
46 {
47     if (m_ellipsisBox) {
48         detachEllipsisBox(m_object->renderArena());
49         InlineFlowBox::clearTruncation();
50     }
51 }
52
53 bool RootInlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth)
54 {
55     // First sanity-check the unoverflowed width of the whole line to see if there is sufficient room.
56     int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge;
57     if (width() - delta < ellipsisWidth)
58         return false;
59
60     // Next iterate over all the line boxes on the line.  If we find a replaced element that intersects
61     // then we refuse to accommodate the ellipsis.  Otherwise we're ok.
62     return InlineFlowBox::canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth);
63 }
64
65 void RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr,  bool ltr, int blockEdge, int ellipsisWidth,
66                                   InlineBox* markupBox)
67 {
68     // Create an ellipsis box.
69     m_ellipsisBox = new (m_object->renderArena()) EllipsisBox(m_object, ellipsisStr, this, 
70                                                               ellipsisWidth - (markupBox ? markupBox->width() : 0),
71                                                               yPos(), height(), baseline(), !prevRootBox(),
72                                                               markupBox);
73
74     if (ltr && (xPos() + width() + ellipsisWidth) <= blockEdge) {
75         m_ellipsisBox->m_x = xPos() + width();
76         return;
77     }
78
79     // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL)
80     // of that glyph.  Mark all of the objects that intersect the ellipsis box as not painting (as being
81     // truncated).
82     bool foundBox = false;
83     m_ellipsisBox->m_x = placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox);
84 }
85
86 int RootInlineBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox)
87 {
88     int result = InlineFlowBox::placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox);
89     if (result == -1)
90         result = ltr ? blockEdge - ellipsisWidth : blockEdge;
91     return result;
92 }
93
94 void RootInlineBox::paintEllipsisBox(RenderObject::PaintInfo& i, int _tx, int _ty) const
95 {
96     if (m_ellipsisBox && object()->shouldPaintWithinRoot(i) && object()->style()->visibility() == VISIBLE &&
97         i.phase == PaintPhaseForeground)
98         m_ellipsisBox->paint(i, _tx, _ty);
99 }
100
101 void RootInlineBox::paint(RenderObject::PaintInfo& i, int tx, int ty)
102 {
103     InlineFlowBox::paint(i, tx, ty);
104     paintEllipsisBox(i, tx, ty);
105 }
106
107 bool RootInlineBox::nodeAtPoint(RenderObject::NodeInfo& i, int x, int y, int tx, int ty)
108 {
109     if (m_ellipsisBox && object()->style()->visibility() == VISIBLE) {
110         if (m_ellipsisBox->nodeAtPoint(i, x, y, tx, ty)) {
111             object()->setInnerNode(i);
112             return true;
113         }
114     }
115     return InlineFlowBox::nodeAtPoint(i, x, y, tx, ty);
116 }
117
118 void RootInlineBox::adjustPosition(int dx, int dy)
119 {
120     InlineFlowBox::adjustPosition(dx, dy);
121     m_topOverflow += dy;
122     m_bottomOverflow += dy;
123     m_blockHeight += dy;
124     m_selectionTop += dy;
125     m_selectionBottom += dy;
126 }
127
128 void RootInlineBox::childRemoved(InlineBox* box)
129 {
130     if (box->object() == m_lineBreakObj)
131         setLineBreakInfo(0, 0, 0, 0);
132
133     RootInlineBox* prev = prevRootBox();
134     if (prev && prev->lineBreakObj() == box->object()) {
135         prev->setLineBreakInfo(0, 0, 0, 0);
136         prev->markDirty();
137     }
138 }
139
140 GapRects RootInlineBox::fillLineSelectionGap(int selTop, int selHeight, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, 
141                                              const RenderObject::PaintInfo* i)
142 {
143     GapRects result;
144     RenderObject::SelectionState lineState = selectionState();
145
146     bool leftGap, rightGap;
147     block()->getHorizontalSelectionGapInfo(lineState, leftGap, rightGap);
148
149     InlineBox* firstBox = firstSelectedBox();
150     InlineBox* lastBox = lastSelectedBox();
151     if (leftGap)
152         result.uniteLeft(block()->fillLeftSelectionGap(firstBox->parent()->object(), 
153                                                        firstBox->xPos(), selTop, selHeight, 
154                                                        rootBlock, blockX, blockY, tx, ty, i));
155     if (rightGap)
156         result.uniteRight(block()->fillRightSelectionGap(lastBox->parent()->object(), 
157                                                          lastBox->xPos() + lastBox->width(), selTop, selHeight, 
158                                                          rootBlock, blockX, blockY, tx, ty, i));
159
160     if (firstBox && firstBox != lastBox) {
161         // Now fill in any gaps on the line that occurred between two selected elements.
162         int lastX = firstBox->xPos() + firstBox->width();
163         for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) {
164             if (box->selectionState() != RenderObject::SelectionNone) {
165                 result.uniteCenter(block()->fillHorizontalSelectionGap(box->parent()->object(),
166                                                                        lastX + tx, selTop + ty,
167                                                                        box->xPos() - lastX, selHeight, i));
168                 lastX = box->xPos() + box->width();
169             }
170             if (box == lastBox)
171                 break;
172         }
173     }
174       
175     return result;
176 }
177
178 void RootInlineBox::setHasSelectedChildren(bool b)
179 {
180     if (m_hasSelectedChildren == b)
181         return;
182     m_hasSelectedChildren = b;
183 }
184
185 RenderObject::SelectionState RootInlineBox::selectionState()
186 {
187     // Walk over all of the selected boxes.
188     RenderObject::SelectionState state = RenderObject::SelectionNone;
189     for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) {
190         RenderObject::SelectionState boxState = box->selectionState();
191         if ((boxState == RenderObject::SelectionStart && state == RenderObject::SelectionEnd) ||
192             (boxState == RenderObject::SelectionEnd && state == RenderObject::SelectionStart))
193             state = RenderObject::SelectionBoth;
194         else if (state == RenderObject::SelectionNone ||
195                  ((boxState == RenderObject::SelectionStart || boxState == RenderObject::SelectionEnd) &&
196                   (state == RenderObject::SelectionNone || state == RenderObject::SelectionInside)))
197             state = boxState;
198         if (state == RenderObject::SelectionBoth)
199             break;
200     }
201     
202     return state;
203 }
204
205 InlineBox* RootInlineBox::firstSelectedBox()
206 {
207     for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild())
208         if (box->selectionState() != RenderObject::SelectionNone)
209             return box;
210     return 0;
211 }
212
213 InlineBox* RootInlineBox::lastSelectedBox()
214 {
215     for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild())
216         if (box->selectionState() != RenderObject::SelectionNone)
217             return box;
218     return 0;
219 }
220
221 int RootInlineBox::selectionTop()
222 {
223     if (!prevRootBox())
224         return m_selectionTop;
225     
226     int prevBottom = prevRootBox()->selectionBottom();
227     if (prevBottom < m_selectionTop && block()->containsFloats()) {
228         // This line has actually been moved further down, probably from a large line-height, but possibly because the
229         // line was forced to clear floats.  If so, let's check the offsets, and only be willing to use the previous
230         // line's bottom overflow if the offsets are greater on both sides.
231         int prevLeft = block()->leftOffset(prevBottom);
232         int prevRight = block()->rightOffset(prevBottom);
233         int newLeft = block()->leftOffset(m_selectionTop);
234         int newRight = block()->rightOffset(m_selectionTop);
235         if (prevLeft > newLeft || prevRight < newRight)
236             return m_selectionTop;
237     }
238     
239     return prevBottom;
240 }
241
242 RenderBlock* RootInlineBox::block() const
243 {
244     return static_cast<RenderBlock*>(m_object);
245 }
246
247 InlineBox* RootInlineBox::closestLeafChildForXPos(int _x, int _tx)
248 {
249     InlineBox *firstLeaf = firstLeafChildAfterBox();
250     InlineBox *lastLeaf = lastLeafChildBeforeBox();
251     if (firstLeaf == lastLeaf)
252         return firstLeaf;
253     
254     // Avoid returning a list marker when possible.
255     if (_x <= _tx + firstLeaf->m_x && !firstLeaf->object()->isListMarker())
256         // The x coordinate is less or equal to left edge of the firstLeaf.
257         // Return it.
258         return firstLeaf;
259     
260     if (_x >= _tx + lastLeaf->m_x + lastLeaf->m_width && !lastLeaf->object()->isListMarker())
261         // The x coordinate is greater or equal to right edge of the lastLeaf.
262         // Return it.
263         return lastLeaf;
264
265     for (InlineBox *leaf = firstLeaf; leaf && leaf != lastLeaf; leaf = leaf->nextLeafChild()) {
266         if (!leaf->object()->isListMarker()) {
267             int leafX = _tx + leaf->m_x;
268             if (_x < leafX + leaf->m_width)
269                 // The x coordinate is less than the right edge of the box.
270                 // Return it.
271                 return leaf;
272         }
273     }
274
275     return lastLeaf;
276 }
277
278 void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, BidiStatus* status, BidiContext* context)
279 {
280     m_lineBreakObj = obj;
281     m_lineBreakPos = breakPos;
282     m_lineBreakContext = context;
283     if (status)
284         m_lineBreakBidiStatus = *status;
285 }
286
287 }