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