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