9fabb37081766da046b87d9d7bc8691cb1e734c9
[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 }
125
126 void RootInlineBox::childRemoved(InlineBox* box)
127 {
128     if (box->object() == m_lineBreakObj)
129         setLineBreakInfo(0, 0, 0, 0);
130
131     RootInlineBox* prev = prevRootBox();
132     if (prev && prev->lineBreakObj() == box->object()) {
133         prev->setLineBreakInfo(0, 0, 0, 0);
134         prev->markDirty();
135     }
136 }
137
138 GapRects RootInlineBox::fillLineSelectionGap(int selTop, int selHeight, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, 
139                                              const RenderObject::PaintInfo* i)
140 {
141     GapRects result;
142     RenderObject::SelectionState lineState = selectionState();
143
144     bool leftGap, rightGap;
145     block()->getHorizontalSelectionGapInfo(lineState, leftGap, rightGap);
146
147     InlineBox* firstBox = firstSelectedBox();
148     InlineBox* lastBox = lastSelectedBox();
149     if (leftGap)
150         result.uniteLeft(block()->fillLeftSelectionGap(firstBox->parent()->object(), 
151                                                        firstBox->xPos(), selTop, selHeight, 
152                                                        rootBlock, blockX, blockY, tx, ty, i));
153     if (rightGap)
154         result.uniteRight(block()->fillRightSelectionGap(lastBox->parent()->object(), 
155                                                          lastBox->xPos() + lastBox->width(), selTop, selHeight, 
156                                                          rootBlock, blockX, blockY, tx, ty, i));
157
158     if (firstBox && firstBox != lastBox) {
159         // Now fill in any gaps on the line that occurred between two selected elements.
160         int lastX = firstBox->xPos() + firstBox->width();
161         for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) {
162             if (box->selectionState() != RenderObject::SelectionNone) {
163                 result.uniteCenter(block()->fillHorizontalSelectionGap(box->parent()->object(),
164                                                                        lastX + tx, selTop + ty,
165                                                                        box->xPos() - lastX, selHeight, i));
166                 lastX = box->xPos() + box->width();
167             }
168             if (box == lastBox)
169                 break;
170         }
171     }
172       
173     return result;
174 }
175
176 void RootInlineBox::setHasSelectedChildren(bool b)
177 {
178     if (m_hasSelectedChildren == b)
179         return;
180     m_hasSelectedChildren = b;
181 }
182
183 RenderObject::SelectionState RootInlineBox::selectionState()
184 {
185     // Walk over all of the selected boxes.
186     RenderObject::SelectionState state = RenderObject::SelectionNone;
187     for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) {
188         RenderObject::SelectionState boxState = box->selectionState();
189         if ((boxState == RenderObject::SelectionStart && state == RenderObject::SelectionEnd) ||
190             (boxState == RenderObject::SelectionEnd && state == RenderObject::SelectionStart))
191             state = RenderObject::SelectionBoth;
192         else if (state == RenderObject::SelectionNone ||
193                  ((boxState == RenderObject::SelectionStart || boxState == RenderObject::SelectionEnd) &&
194                   (state == RenderObject::SelectionNone || state == RenderObject::SelectionInside)))
195             state = boxState;
196         if (state == RenderObject::SelectionBoth)
197             break;
198     }
199     
200     return state;
201 }
202
203 InlineBox* RootInlineBox::firstSelectedBox()
204 {
205     for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild())
206         if (box->selectionState() != RenderObject::SelectionNone)
207             return box;
208     return 0;
209 }
210
211 InlineBox* RootInlineBox::lastSelectedBox()
212 {
213     for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild())
214         if (box->selectionState() != RenderObject::SelectionNone)
215             return box;
216     return 0;
217 }
218
219 int RootInlineBox::selectionTop()
220 {
221     if (!prevRootBox())
222         return m_selectionTop;
223     
224     int prevBottom = prevRootBox()->selectionBottom();
225     if (prevBottom < m_selectionTop && block()->containsFloats()) {
226         // This line has actually been moved further down, probably from a large line-height, but possibly because the
227         // line was forced to clear floats.  If so, let's check the offsets, and only be willing to use the previous
228         // line's bottom overflow if the offsets are greater on both sides.
229         int prevLeft = block()->leftOffset(prevBottom);
230         int prevRight = block()->rightOffset(prevBottom);
231         int newLeft = block()->leftOffset(m_selectionTop);
232         int newRight = block()->rightOffset(m_selectionTop);
233         if (prevLeft > newLeft || prevRight < newRight)
234             return m_selectionTop;
235     }
236     
237     return prevBottom;
238 }
239
240 RenderBlock* RootInlineBox::block() const
241 {
242     return static_cast<RenderBlock*>(m_object);
243 }
244
245 InlineBox* RootInlineBox::closestLeafChildForXPos(int _x, int _tx)
246 {
247     InlineBox *firstLeaf = firstLeafChildAfterBox();
248     InlineBox *lastLeaf = lastLeafChildBeforeBox();
249     if (firstLeaf == lastLeaf)
250         return firstLeaf;
251     
252     // Avoid returning a list marker when possible.
253     if (_x <= _tx + firstLeaf->m_x && !firstLeaf->object()->isListMarker())
254         // The x coordinate is less or equal to left edge of the firstLeaf.
255         // Return it.
256         return firstLeaf;
257     
258     if (_x >= _tx + lastLeaf->m_x + lastLeaf->m_width && !lastLeaf->object()->isListMarker())
259         // The x coordinate is greater or equal to right edge of the lastLeaf.
260         // Return it.
261         return lastLeaf;
262
263     for (InlineBox *leaf = firstLeaf; leaf && leaf != lastLeaf; leaf = leaf->nextLeafChild()) {
264         if (!leaf->object()->isListMarker()) {
265             int leafX = _tx + leaf->m_x;
266             if (_x < leafX + leaf->m_width)
267                 // The x coordinate is less than the right edge of the box.
268                 // Return it.
269                 return leaf;
270         }
271     }
272
273     return lastLeaf;
274 }
275
276 void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, BidiStatus* status, BidiContext* context)
277 {
278     m_lineBreakObj = obj;
279     m_lineBreakPos = breakPos;
280     m_lineBreakContext = context;
281     if (status)
282         m_lineBreakBidiStatus = *status;
283 }
284
285 }