1 /**
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2000 Simon Hausmann <hausmann@kde.org>
4 * (C) 2000 Stefan Schimanski (1Stein@gmx.de)
5 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24 #include "config.h"
25 #include "RenderFrameSet.h"
26
27 #include "Document.h"
28 #include "EventHandler.h"
29 #include "EventNames.h"
30 #include "Frame.h"
31 #include "FrameView.h"
32 #include "GraphicsContext.h"
33 #include "HTMLFrameSetElement.h"
34 #include "HitTestRequest.h"
35 #include "HitTestResult.h"
36 #include "MouseEvent.h"
37 #include "PaintInfo.h"
38 #include "RenderFrame.h"
39 #include "RenderView.h"
40 #include "Settings.h"
41
42 namespace WebCore {
43
RenderFrameSet(HTMLFrameSetElement * frameSet)44 RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet)
45 : RenderBox(frameSet)
46 , m_isResizing(false)
47 , m_isChildResizing(false)
48 {
49 setInline(false);
50 }
51
~RenderFrameSet()52 RenderFrameSet::~RenderFrameSet()
53 {
54 }
55
GridAxis()56 RenderFrameSet::GridAxis::GridAxis()
57 : m_splitBeingResized(noSplit)
58 {
59 }
60
frameSet() const61 inline HTMLFrameSetElement* RenderFrameSet::frameSet() const
62 {
63 return static_cast<HTMLFrameSetElement*>(node());
64 }
65
borderStartEdgeColor()66 static Color borderStartEdgeColor()
67 {
68 return Color(170, 170, 170);
69 }
70
borderEndEdgeColor()71 static Color borderEndEdgeColor()
72 {
73 return Color::black;
74 }
75
borderFillColor()76 static Color borderFillColor()
77 {
78 return Color(208, 208, 208);
79 }
80
paintColumnBorder(const PaintInfo & paintInfo,const IntRect & borderRect)81 void RenderFrameSet::paintColumnBorder(const PaintInfo& paintInfo, const IntRect& borderRect)
82 {
83 if (!paintInfo.rect.intersects(borderRect))
84 return;
85
86 // FIXME: We should do something clever when borders from distinct framesets meet at a join.
87
88 // Fill first.
89 GraphicsContext* context = paintInfo.context;
90 ColorSpace colorSpace = style()->colorSpace();
91 context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->visitedDependentColor(CSSPropertyBorderLeftColor) : borderFillColor(), colorSpace);
92
93 // Now stroke the edges but only if we have enough room to paint both edges with a little
94 // bit of the fill color showing through.
95 if (borderRect.width() >= 3) {
96 context->fillRect(IntRect(borderRect.location(), IntSize(1, height())), borderStartEdgeColor(), colorSpace);
97 context->fillRect(IntRect(IntPoint(borderRect.maxX() - 1, borderRect.y()), IntSize(1, height())), borderEndEdgeColor(), colorSpace);
98 }
99 }
100
paintRowBorder(const PaintInfo & paintInfo,const IntRect & borderRect)101 void RenderFrameSet::paintRowBorder(const PaintInfo& paintInfo, const IntRect& borderRect)
102 {
103 if (!paintInfo.rect.intersects(borderRect))
104 return;
105
106 // FIXME: We should do something clever when borders from distinct framesets meet at a join.
107
108 // Fill first.
109 GraphicsContext* context = paintInfo.context;
110 ColorSpace colorSpace = style()->colorSpace();
111 context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->visitedDependentColor(CSSPropertyBorderLeftColor) : borderFillColor(), colorSpace);
112
113 // Now stroke the edges but only if we have enough room to paint both edges with a little
114 // bit of the fill color showing through.
115 if (borderRect.height() >= 3) {
116 context->fillRect(IntRect(borderRect.location(), IntSize(width(), 1)), borderStartEdgeColor(), colorSpace);
117 context->fillRect(IntRect(IntPoint(borderRect.x(), borderRect.maxY() - 1), IntSize(width(), 1)), borderEndEdgeColor(), colorSpace);
118 }
119 }
120
paint(PaintInfo & paintInfo,int tx,int ty)121 void RenderFrameSet::paint(PaintInfo& paintInfo, int tx, int ty)
122 {
123 if (paintInfo.phase != PaintPhaseForeground)
124 return;
125
126 RenderObject* child = firstChild();
127 if (!child)
128 return;
129
130 // Add in our offsets.
131 tx += x();
132 ty += y();
133
134 int rows = frameSet()->totalRows();
135 int cols = frameSet()->totalCols();
136 int borderThickness = frameSet()->border();
137
138 int yPos = 0;
139 for (int r = 0; r < rows; r++) {
140 int xPos = 0;
141 for (int c = 0; c < cols; c++) {
142 child->paint(paintInfo, tx, ty);
143 xPos += m_cols.m_sizes[c];
144 if (borderThickness && m_cols.m_allowBorder[c + 1]) {
145 paintColumnBorder(paintInfo, IntRect(tx + xPos, ty + yPos, borderThickness, height()));
146 xPos += borderThickness;
147 }
148 child = child->nextSibling();
149 if (!child)
150 return;
151 }
152 yPos += m_rows.m_sizes[r];
153 if (borderThickness && m_rows.m_allowBorder[r + 1]) {
154 paintRowBorder(paintInfo, IntRect(tx, ty + yPos, width(), borderThickness));
155 yPos += borderThickness;
156 }
157 }
158 }
159
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty,HitTestAction action)160 bool RenderFrameSet::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
161 int x, int y, int tx, int ty, HitTestAction action)
162 {
163 if (action != HitTestForeground)
164 return false;
165
166 bool inside = RenderBox::nodeAtPoint(request, result, x, y, tx, ty, action)
167 || m_isResizing;
168
169 if (inside && frameSet()->noResize()
170 && !request.readOnly() && !result.innerNode()) {
171 result.setInnerNode(node());
172 result.setInnerNonSharedNode(node());
173 }
174
175 return inside || m_isChildResizing;
176 }
177
resize(int size)178 void RenderFrameSet::GridAxis::resize(int size)
179 {
180 m_sizes.resize(size);
181 m_deltas.resize(size);
182 m_deltas.fill(0);
183
184 // To track edges for resizability and borders, we need to be (size + 1). This is because a parent frameset
185 // may ask us for information about our left/top/right/bottom edges in order to make its own decisions about
186 // what to do. We are capable of tainting that parent frameset's borders, so we have to cache this info.
187 m_preventResize.resize(size + 1);
188 m_allowBorder.resize(size + 1);
189 }
190
layOutAxis(GridAxis & axis,const Length * grid,int availableLen)191 void RenderFrameSet::layOutAxis(GridAxis& axis, const Length* grid, int availableLen)
192 {
193 availableLen = max(availableLen, 0);
194
195 int* gridLayout = axis.m_sizes.data();
196
197 if (!grid) {
198 gridLayout[0] = availableLen;
199 return;
200 }
201
202 int gridLen = axis.m_sizes.size();
203 ASSERT(gridLen);
204
205 int totalRelative = 0;
206 int totalFixed = 0;
207 int totalPercent = 0;
208 int countRelative = 0;
209 int countFixed = 0;
210 int countPercent = 0;
211
212 // First we need to investigate how many columns of each type we have and
213 // how much space these columns are going to require.
214 for (int i = 0; i < gridLen; ++i) {
215 // Count the total length of all of the fixed columns/rows -> totalFixed
216 // Count the number of columns/rows which are fixed -> countFixed
217 if (grid[i].isFixed()) {
218 gridLayout[i] = max(grid[i].value(), 0);
219 totalFixed += gridLayout[i];
220 countFixed++;
221 }
222
223 // Count the total percentage of all of the percentage columns/rows -> totalPercent
224 // Count the number of columns/rows which are percentages -> countPercent
225 if (grid[i].isPercent()) {
226 gridLayout[i] = max(grid[i].calcValue(availableLen), 0);
227 totalPercent += gridLayout[i];
228 countPercent++;
229 }
230
231 // Count the total relative of all the relative columns/rows -> totalRelative
232 // Count the number of columns/rows which are relative -> countRelative
233 if (grid[i].isRelative()) {
234 totalRelative += max(grid[i].value(), 1);
235 countRelative++;
236 }
237 }
238
239 int remainingLen = availableLen;
240
241 // Fixed columns/rows are our first priority. If there is not enough space to fit all fixed
242 // columns/rows we need to proportionally adjust their size.
243 if (totalFixed > remainingLen) {
244 int remainingFixed = remainingLen;
245
246 for (int i = 0; i < gridLen; ++i) {
247 if (grid[i].isFixed()) {
248 gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed;
249 remainingLen -= gridLayout[i];
250 }
251 }
252 } else
253 remainingLen -= totalFixed;
254
255 // Percentage columns/rows are our second priority. Divide the remaining space proportionally
256 // over all percentage columns/rows. IMPORTANT: the size of each column/row is not relative
257 // to 100%, but to the total percentage. For example, if there are three columns, each of 75%,
258 // and the available space is 300px, each column will become 100px in width.
259 if (totalPercent > remainingLen) {
260 int remainingPercent = remainingLen;
261
262 for (int i = 0; i < gridLen; ++i) {
263 if (grid[i].isPercent()) {
264 gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent;
265 remainingLen -= gridLayout[i];
266 }
267 }
268 } else
269 remainingLen -= totalPercent;
270
271 // Relative columns/rows are our last priority. Divide the remaining space proportionally
272 // over all relative columns/rows. IMPORTANT: the relative value of 0* is treated as 1*.
273 if (countRelative) {
274 int lastRelative = 0;
275 int remainingRelative = remainingLen;
276
277 for (int i = 0; i < gridLen; ++i) {
278 if (grid[i].isRelative()) {
279 gridLayout[i] = (max(grid[i].value(), 1) * remainingRelative) / totalRelative;
280 remainingLen -= gridLayout[i];
281 lastRelative = i;
282 }
283 }
284
285 // If we could not evenly distribute the available space of all of the relative
286 // columns/rows, the remainder will be added to the last column/row.
287 // For example: if we have a space of 100px and three columns (*,*,*), the remainder will
288 // be 1px and will be added to the last column: 33px, 33px, 34px.
289 if (remainingLen) {
290 gridLayout[lastRelative] += remainingLen;
291 remainingLen = 0;
292 }
293 }
294
295 // If we still have some left over space we need to divide it over the already existing
296 // columns/rows
297 if (remainingLen) {
298 // Our first priority is to spread if over the percentage columns. The remaining
299 // space is spread evenly, for example: if we have a space of 100px, the columns
300 // definition of 25%,25% used to result in two columns of 25px. After this the
301 // columns will each be 50px in width.
302 if (countPercent && totalPercent) {
303 int remainingPercent = remainingLen;
304 int changePercent = 0;
305
306 for (int i = 0; i < gridLen; ++i) {
307 if (grid[i].isPercent()) {
308 changePercent = (remainingPercent * gridLayout[i]) / totalPercent;
309 gridLayout[i] += changePercent;
310 remainingLen -= changePercent;
311 }
312 }
313 } else if (totalFixed) {
314 // Our last priority is to spread the remaining space over the fixed columns.
315 // For example if we have 100px of space and two column of each 40px, both
316 // columns will become exactly 50px.
317 int remainingFixed = remainingLen;
318 int changeFixed = 0;
319
320 for (int i = 0; i < gridLen; ++i) {
321 if (grid[i].isFixed()) {
322 changeFixed = (remainingFixed * gridLayout[i]) / totalFixed;
323 gridLayout[i] += changeFixed;
324 remainingLen -= changeFixed;
325 }
326 }
327 }
328 }
329
330 // If we still have some left over space we probably ended up with a remainder of
331 // a division. We cannot spread it evenly anymore. If we have any percentage
332 // columns/rows simply spread the remainder equally over all available percentage columns,
333 // regardless of their size.
334 if (remainingLen && countPercent) {
335 int remainingPercent = remainingLen;
336 int changePercent = 0;
337
338 for (int i = 0; i < gridLen; ++i) {
339 if (grid[i].isPercent()) {
340 changePercent = remainingPercent / countPercent;
341 gridLayout[i] += changePercent;
342 remainingLen -= changePercent;
343 }
344 }
345 }
346
347 // If we don't have any percentage columns/rows we only have fixed columns. Spread
348 // the remainder equally over all fixed columns/rows.
349 else if (remainingLen && countFixed) {
350 int remainingFixed = remainingLen;
351 int changeFixed = 0;
352
353 for (int i = 0; i < gridLen; ++i) {
354 if (grid[i].isFixed()) {
355 changeFixed = remainingFixed / countFixed;
356 gridLayout[i] += changeFixed;
357 remainingLen -= changeFixed;
358 }
359 }
360 }
361
362 // Still some left over. Add it to the last column, because it is impossible
363 // spread it evenly or equally.
364 if (remainingLen)
365 gridLayout[gridLen - 1] += remainingLen;
366
367 // now we have the final layout, distribute the delta over it
368 bool worked = true;
369 int* gridDelta = axis.m_deltas.data();
370 for (int i = 0; i < gridLen; ++i) {
371 if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0)
372 worked = false;
373 gridLayout[i] += gridDelta[i];
374 }
375 // if the deltas broke something, undo them
376 if (!worked) {
377 for (int i = 0; i < gridLen; ++i)
378 gridLayout[i] -= gridDelta[i];
379 axis.m_deltas.fill(0);
380 }
381 }
382
fillFromEdgeInfo(const FrameEdgeInfo & edgeInfo,int r,int c)383 void RenderFrameSet::fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c)
384 {
385 if (edgeInfo.allowBorder(LeftFrameEdge))
386 m_cols.m_allowBorder[c] = true;
387 if (edgeInfo.allowBorder(RightFrameEdge))
388 m_cols.m_allowBorder[c + 1] = true;
389 if (edgeInfo.preventResize(LeftFrameEdge))
390 m_cols.m_preventResize[c] = true;
391 if (edgeInfo.preventResize(RightFrameEdge))
392 m_cols.m_preventResize[c + 1] = true;
393
394 if (edgeInfo.allowBorder(TopFrameEdge))
395 m_rows.m_allowBorder[r] = true;
396 if (edgeInfo.allowBorder(BottomFrameEdge))
397 m_rows.m_allowBorder[r + 1] = true;
398 if (edgeInfo.preventResize(TopFrameEdge))
399 m_rows.m_preventResize[r] = true;
400 if (edgeInfo.preventResize(BottomFrameEdge))
401 m_rows.m_preventResize[r + 1] = true;
402 }
403
computeEdgeInfo()404 void RenderFrameSet::computeEdgeInfo()
405 {
406 m_rows.m_preventResize.fill(frameSet()->noResize());
407 m_rows.m_allowBorder.fill(false);
408 m_cols.m_preventResize.fill(frameSet()->noResize());
409 m_cols.m_allowBorder.fill(false);
410
411 RenderObject* child = firstChild();
412 if (!child)
413 return;
414
415 int rows = frameSet()->totalRows();
416 int cols = frameSet()->totalCols();
417 for (int r = 0; r < rows; ++r) {
418 for (int c = 0; c < cols; ++c) {
419 FrameEdgeInfo edgeInfo;
420 if (child->isFrameSet())
421 edgeInfo = toRenderFrameSet(child)->edgeInfo();
422 else
423 edgeInfo = toRenderFrame(child)->edgeInfo();
424 fillFromEdgeInfo(edgeInfo, r, c);
425 child = child->nextSibling();
426 if (!child)
427 return;
428 }
429 }
430 }
431
edgeInfo() const432 FrameEdgeInfo RenderFrameSet::edgeInfo() const
433 {
434 FrameEdgeInfo result(frameSet()->noResize(), true);
435
436 int rows = frameSet()->totalRows();
437 int cols = frameSet()->totalCols();
438 if (rows && cols) {
439 result.setPreventResize(LeftFrameEdge, m_cols.m_preventResize[0]);
440 result.setAllowBorder(LeftFrameEdge, m_cols.m_allowBorder[0]);
441 result.setPreventResize(RightFrameEdge, m_cols.m_preventResize[cols]);
442 result.setAllowBorder(RightFrameEdge, m_cols.m_allowBorder[cols]);
443 result.setPreventResize(TopFrameEdge, m_rows.m_preventResize[0]);
444 result.setAllowBorder(TopFrameEdge, m_rows.m_allowBorder[0]);
445 result.setPreventResize(BottomFrameEdge, m_rows.m_preventResize[rows]);
446 result.setAllowBorder(BottomFrameEdge, m_rows.m_allowBorder[rows]);
447 }
448
449 return result;
450 }
451
layout()452 void RenderFrameSet::layout()
453 {
454 ASSERT(needsLayout());
455
456 bool doFullRepaint = selfNeedsLayout() && checkForRepaintDuringLayout();
457 IntRect oldBounds;
458 if (doFullRepaint)
459 oldBounds = absoluteClippedOverflowRect();
460
461 if (!parent()->isFrameSet() && !document()->printing()) {
462 setWidth(view()->viewWidth());
463 setHeight(view()->viewHeight());
464 }
465
466 size_t cols = frameSet()->totalCols();
467 size_t rows = frameSet()->totalRows();
468
469 if (m_rows.m_sizes.size() != rows || m_cols.m_sizes.size() != cols) {
470 m_rows.resize(rows);
471 m_cols.resize(cols);
472 }
473
474 int borderThickness = frameSet()->border();
475 layOutAxis(m_rows, frameSet()->rowLengths(), height() - (rows - 1) * borderThickness);
476 layOutAxis(m_cols, frameSet()->colLengths(), width() - (cols - 1) * borderThickness);
477
478 if (flattenFrameSet())
479 positionFramesWithFlattening();
480 else
481 positionFrames();
482
483 RenderBox::layout();
484
485 computeEdgeInfo();
486
487 if (doFullRepaint) {
488 view()->repaintViewRectangle(oldBounds);
489 IntRect newBounds = absoluteClippedOverflowRect();
490 if (newBounds != oldBounds)
491 view()->repaintViewRectangle(newBounds);
492 }
493
494 setNeedsLayout(false);
495 }
496
positionFrames()497 void RenderFrameSet::positionFrames()
498 {
499 RenderBox* child = firstChildBox();
500 if (!child)
501 return;
502
503 int rows = frameSet()->totalRows();
504 int cols = frameSet()->totalCols();
505
506 int yPos = 0;
507 int borderThickness = frameSet()->border();
508 for (int r = 0; r < rows; r++) {
509 int xPos = 0;
510 int height = m_rows.m_sizes[r];
511 for (int c = 0; c < cols; c++) {
512 child->setLocation(xPos, yPos);
513 int width = m_cols.m_sizes[c];
514
515 // has to be resized and itself resize its contents
516 if (width != child->width() || height != child->height()) {
517 child->setWidth(width);
518 child->setHeight(height);
519 child->setNeedsLayout(true);
520 child->layout();
521 }
522
523 xPos += width + borderThickness;
524
525 child = child->nextSiblingBox();
526 if (!child)
527 return;
528 }
529 yPos += height + borderThickness;
530 }
531
532 // all the remaining frames are hidden to avoid ugly spurious unflowed frames
533 for (; child; child = child->nextSiblingBox()) {
534 child->setWidth(0);
535 child->setHeight(0);
536 child->setNeedsLayout(false);
537 }
538 }
539
positionFramesWithFlattening()540 void RenderFrameSet::positionFramesWithFlattening()
541 {
542 RenderBox* child = firstChildBox();
543 if (!child)
544 return;
545
546 int rows = frameSet()->totalRows();
547 int cols = frameSet()->totalCols();
548
549 int borderThickness = frameSet()->border();
550 bool repaintNeeded = false;
551
552 // calculate frameset height based on actual content height to eliminate scrolling
553 bool out = false;
554 for (int r = 0; r < rows && !out; r++) {
555 int extra = 0;
556 int height = m_rows.m_sizes[r];
557
558 for (int c = 0; c < cols; c++) {
559 IntRect oldFrameRect = child->frameRect();
560
561 int width = m_cols.m_sizes[c];
562
563 bool fixedWidth = frameSet()->colLengths() && frameSet()->colLengths()[c].isFixed();
564 bool fixedHeight = frameSet()->rowLengths() && frameSet()->rowLengths()[r].isFixed();
565
566 // has to be resized and itself resize its contents
567 if (!fixedWidth)
568 child->setWidth(width ? width + extra / (cols - c) : 0);
569 else
570 child->setWidth(width);
571 child->setHeight(height);
572
573 child->setNeedsLayout(true);
574
575 if (child->isFrameSet())
576 toRenderFrameSet(child)->layout();
577 else
578 toRenderFrame(child)->layoutWithFlattening(fixedWidth, fixedHeight);
579
580 if (child->height() > m_rows.m_sizes[r])
581 m_rows.m_sizes[r] = child->height();
582 if (child->width() > m_cols.m_sizes[c])
583 m_cols.m_sizes[c] = child->width();
584
585 if (child->frameRect() != oldFrameRect)
586 repaintNeeded = true;
587
588 // difference between calculated frame width and the width it actually decides to have
589 extra += width - m_cols.m_sizes[c];
590
591 child = child->nextSiblingBox();
592 if (!child) {
593 out = true;
594 break;
595 }
596 }
597 }
598
599 int xPos = 0;
600 int yPos = 0;
601 out = false;
602 child = firstChildBox();
603 for (int r = 0; r < rows && !out; r++) {
604 xPos = 0;
605 for (int c = 0; c < cols; c++) {
606 // ensure the rows and columns are filled
607 IntRect oldRect = child->frameRect();
608
609 child->setLocation(xPos, yPos);
610 child->setHeight(m_rows.m_sizes[r]);
611 child->setWidth(m_cols.m_sizes[c]);
612
613 if (child->frameRect() != oldRect) {
614 repaintNeeded = true;
615
616 // update to final size
617 child->setNeedsLayout(true);
618 if (child->isFrameSet())
619 toRenderFrameSet(child)->layout();
620 else
621 toRenderFrame(child)->layoutWithFlattening(true, true);
622 }
623
624 xPos += m_cols.m_sizes[c] + borderThickness;
625 child = child->nextSiblingBox();
626 if (!child) {
627 out = true;
628 break;
629 }
630 }
631 yPos += m_rows.m_sizes[r] + borderThickness;
632 }
633
634 setWidth(xPos - borderThickness);
635 setHeight(yPos - borderThickness);
636
637 if (repaintNeeded)
638 repaint();
639
640 // all the remaining frames are hidden to avoid ugly spurious unflowed frames
641 for (; child; child = child->nextSiblingBox()) {
642 child->setWidth(0);
643 child->setHeight(0);
644 child->setNeedsLayout(false);
645 }
646 }
647
flattenFrameSet() const648 bool RenderFrameSet::flattenFrameSet() const
649 {
650 return frame() && frame()->settings()->frameFlatteningEnabled();
651 }
652
startResizing(GridAxis & axis,int position)653 void RenderFrameSet::startResizing(GridAxis& axis, int position)
654 {
655 int split = hitTestSplit(axis, position);
656 if (split == noSplit || !axis.m_allowBorder[split] || axis.m_preventResize[split]) {
657 axis.m_splitBeingResized = noSplit;
658 return;
659 }
660 axis.m_splitBeingResized = split;
661 axis.m_splitResizeOffset = position - splitPosition(axis, split);
662 }
663
continueResizing(GridAxis & axis,int position)664 void RenderFrameSet::continueResizing(GridAxis& axis, int position)
665 {
666 if (needsLayout())
667 return;
668 if (axis.m_splitBeingResized == noSplit)
669 return;
670 int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized);
671 int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset;
672 if (delta == 0)
673 return;
674 axis.m_deltas[axis.m_splitBeingResized - 1] += delta;
675 axis.m_deltas[axis.m_splitBeingResized] -= delta;
676 setNeedsLayout(true);
677 }
678
userResize(MouseEvent * evt)679 bool RenderFrameSet::userResize(MouseEvent* evt)
680 {
681 if (flattenFrameSet())
682 return false;
683
684 if (!m_isResizing) {
685 if (needsLayout())
686 return false;
687 if (evt->type() == eventNames().mousedownEvent && evt->button() == LeftButton) {
688 FloatPoint pos = localToAbsolute();
689 startResizing(m_cols, evt->absoluteLocation().x() - pos.x());
690 startResizing(m_rows, evt->absoluteLocation().y() - pos.y());
691 if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) {
692 setIsResizing(true);
693 return true;
694 }
695 }
696 } else {
697 if (evt->type() == eventNames().mousemoveEvent || (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton)) {
698 FloatPoint pos = localToAbsolute();
699 continueResizing(m_cols, evt->absoluteLocation().x() - pos.x());
700 continueResizing(m_rows, evt->absoluteLocation().y() - pos.y());
701 if (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton) {
702 setIsResizing(false);
703 return true;
704 }
705 }
706 }
707
708 return false;
709 }
710
setIsResizing(bool isResizing)711 void RenderFrameSet::setIsResizing(bool isResizing)
712 {
713 m_isResizing = isResizing;
714 for (RenderObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
715 if (ancestor->isFrameSet())
716 toRenderFrameSet(ancestor)->m_isChildResizing = isResizing;
717 }
718 if (Frame* frame = this->frame())
719 frame->eventHandler()->setResizingFrameSet(isResizing ? frameSet() : 0);
720 }
721
isResizingRow() const722 bool RenderFrameSet::isResizingRow() const
723 {
724 return m_isResizing && m_rows.m_splitBeingResized != noSplit;
725 }
726
isResizingColumn() const727 bool RenderFrameSet::isResizingColumn() const
728 {
729 return m_isResizing && m_cols.m_splitBeingResized != noSplit;
730 }
731
canResizeRow(const IntPoint & p) const732 bool RenderFrameSet::canResizeRow(const IntPoint& p) const
733 {
734 int r = hitTestSplit(m_rows, p.y());
735 return r != noSplit && m_rows.m_allowBorder[r] && !m_rows.m_preventResize[r];
736 }
737
canResizeColumn(const IntPoint & p) const738 bool RenderFrameSet::canResizeColumn(const IntPoint& p) const
739 {
740 int c = hitTestSplit(m_cols, p.x());
741 return c != noSplit && m_cols.m_allowBorder[c] && !m_cols.m_preventResize[c];
742 }
743
splitPosition(const GridAxis & axis,int split) const744 int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const
745 {
746 if (needsLayout())
747 return 0;
748
749 int borderThickness = frameSet()->border();
750
751 int size = axis.m_sizes.size();
752 if (!size)
753 return 0;
754
755 int position = 0;
756 for (int i = 0; i < split && i < size; ++i)
757 position += axis.m_sizes[i] + borderThickness;
758 return position - borderThickness;
759 }
760
hitTestSplit(const GridAxis & axis,int position) const761 int RenderFrameSet::hitTestSplit(const GridAxis& axis, int position) const
762 {
763 if (needsLayout())
764 return noSplit;
765
766 int borderThickness = frameSet()->border();
767 if (borderThickness <= 0)
768 return noSplit;
769
770 size_t size = axis.m_sizes.size();
771 if (!size)
772 return noSplit;
773
774 int splitPosition = axis.m_sizes[0];
775 for (size_t i = 1; i < size; ++i) {
776 if (position >= splitPosition && position < splitPosition + borderThickness)
777 return i;
778 splitPosition += borderThickness + axis.m_sizes[i];
779 }
780 return noSplit;
781 }
782
isChildAllowed(RenderObject * child,RenderStyle *) const783 bool RenderFrameSet::isChildAllowed(RenderObject* child, RenderStyle*) const
784 {
785 return child->isFrame() || child->isFrameSet();
786 }
787
788 } // namespace WebCore
789