RenderFrameSet.cpp [plain text]
#include "config.h"
#include "RenderFrameSet.h"
#include "Cursor.h"
#include "EventNames.h"
#include "Frame.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "HTMLFrameSetElement.h"
#include "HTMLNames.h"
#include "TextStream.h"
#include "MouseEvent.h"
#include "RenderFrame.h"
#include "RenderView.h"
#include "Settings.h"
namespace WebCore {
using namespace EventNames;
using namespace HTMLNames;
inline HTMLFrameSetElement* RenderFrameSet::frameSet() const
{
return static_cast<HTMLFrameSetElement*>(node());
}
RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet)
: RenderContainer(frameSet)
, m_hSplitVar(0)
, m_vSplitVar(0)
, m_hSplit(-1)
, m_vSplit(-1)
, m_resizing(false)
, m_clientResizing(false)
{
setInline(false);
for (int k = 0; k < 2; ++k) {
m_gridLen[k] = -1;
m_gridDelta[k] = 0;
m_gridLayout[k] = 0;
}
}
RenderFrameSet::~RenderFrameSet()
{
for (int k = 0; k < 2; ++k) {
if (m_gridLayout[k])
delete [] m_gridLayout[k];
if (m_gridDelta[k])
delete [] m_gridDelta[k];
}
if (m_hSplitVar)
delete [] m_hSplitVar;
if (m_vSplitVar)
delete [] m_vSplitVar;
}
bool RenderFrameSet::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
HitTestAction hitTestAction)
{
if (hitTestAction != HitTestForeground)
return false;
bool inside = RenderContainer::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction) ||
m_resizing || canResize(_x, _y);
if (inside && element() && !element()->noResize() && !info.readonly() && !info.innerNode()) {
info.setInnerNode(element());
info.setInnerNonSharedNode(element());
}
return inside || m_clientResizing;
}
void RenderFrameSet::layout()
{
ASSERT(needsLayout());
ASSERT(minMaxKnown());
if (!parent()->isFrameSet()) {
FrameView* frameView = view()->frameView();
m_width = frameView->visibleWidth();
m_height = frameView->visibleHeight();
if (flattenFrameset()) {
calcMinMaxWidth();
m_width = max(m_width, m_minWidth);
if (!frameView->frame()->ownerElement())
m_height = max(m_height, 600);
}
}
int remainingLen[2];
remainingLen[1] = m_width - (element()->totalCols()-1)*element()->border();
if (remainingLen[1] < 0)
remainingLen[1] = 0;
remainingLen[0] = m_height - (element()->totalRows()-1)*element()->border();
if (remainingLen[0] < 0)
remainingLen[0] = 0;
int availableLen[2];
availableLen[0] = remainingLen[0];
availableLen[1] = remainingLen[1];
if (m_gridLen[0] != element()->totalRows() || m_gridLen[1] != element()->totalCols()) {
m_gridLen[0] = element()->totalRows();
m_gridLen[1] = element()->totalCols();
for (int k = 0; k < 2; ++k) {
if (m_gridDelta[k]) delete [] m_gridDelta[k];
m_gridDelta[k] = new int[m_gridLen[k]];
if (m_gridLayout[k]) delete [] m_gridLayout[k];
m_gridLayout[k] = new int[m_gridLen[k]];
for (int i = 0; i < m_gridLen[k]; ++i)
m_gridDelta[k][i] = 0;
}
}
for (int k = 0; k < 2; ++k) {
int totalRelative = 0;
int totalFixed = 0;
int totalPercent = 0;
int countRelative = 0;
int countFixed = 0;
int countPercent = 0;
int gridLen = m_gridLen[k];
int* gridDelta = m_gridDelta[k];
Length* grid = k ? element()->m_cols : element()->m_rows;
int* gridLayout = m_gridLayout[k];
if (grid) {
assert(gridLen);
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isFixed()) {
gridLayout[i] = max(grid[i].value(), 0);
totalFixed += gridLayout[i];
countFixed++;
}
if (grid[i].isPercent()) {
gridLayout[i] = max(grid[i].calcValue(availableLen[k]), 0);
totalPercent += gridLayout[i];
countPercent++;
}
if (grid[i].isRelative()) {
totalRelative += max(grid[i].value(), 1);
countRelative++;
}
}
if (totalFixed > remainingLen[k]) {
int remainingFixed = remainingLen[k];
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isFixed()) {
gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed;
remainingLen[k] -= gridLayout[i];
}
}
} else
remainingLen[k] -= totalFixed;
if (totalPercent > remainingLen[k]) {
int remainingPercent = remainingLen[k];
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isPercent()) {
gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent;
remainingLen[k] -= gridLayout[i];
}
}
} else
remainingLen[k] -= totalPercent;
if (countRelative) {
int lastRelative = 0;
int remainingRelative = remainingLen[k];
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isRelative()) {
gridLayout[i] = (max(grid[i].value(), 1) * remainingRelative) / totalRelative;
remainingLen[k] -= gridLayout[i];
lastRelative = i;
}
}
if (remainingLen[k]) {
gridLayout[lastRelative] += remainingLen[k];
remainingLen[k] = 0;
}
}
if (remainingLen[k]) {
if (countPercent && totalPercent) {
int remainingPercent = remainingLen[k];
int changePercent = 0;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isPercent()) {
changePercent = (remainingPercent * gridLayout[i]) / totalPercent;
gridLayout[i] += changePercent;
remainingLen[k] -= changePercent;
}
}
} else if (totalFixed) {
int remainingFixed = remainingLen[k];
int changeFixed = 0;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isFixed()) {
changeFixed = (remainingFixed * gridLayout[i]) / totalFixed;
gridLayout[i] += changeFixed;
remainingLen[k] -= changeFixed;
}
}
}
}
if (remainingLen[k] && countPercent) {
int remainingPercent = remainingLen[k];
int changePercent = 0;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isPercent()) {
changePercent = remainingPercent / countPercent;
gridLayout[i] += changePercent;
remainingLen[k] -= changePercent;
}
}
}
else if (remainingLen[k] && countFixed) {
int remainingFixed = remainingLen[k];
int changeFixed = 0;
for (int i = 0; i < gridLen; ++i) {
if (grid[i].isFixed()) {
changeFixed = remainingFixed / countFixed;
gridLayout[i] += changeFixed;
remainingLen[k] -= changeFixed;
}
}
}
if (remainingLen[k])
gridLayout[gridLen - 1] += remainingLen[k];
bool worked = true;
for (int i = 0; i < gridLen; ++i) {
if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0)
worked = false;
gridLayout[i] += gridDelta[i];
}
if (!worked) {
for (int i = 0; i < gridLen; ++i) {
gridLayout[i] -= gridDelta[i];
gridDelta[i] = 0;
}
}
}
else
gridLayout[0] = remainingLen[k];
}
if (flattenFrameset())
positionFramesWithFlattening();
else
positionFrames();
RenderObject *child = firstChild();
if (!child)
goto end2;
if (!m_hSplitVar && !m_vSplitVar) {
if (!m_vSplitVar && element()->totalCols() > 1) {
m_vSplitVar = new bool[element()->totalCols()];
for (int i = 0; i < element()->totalCols(); i++) m_vSplitVar[i] = true;
}
if (!m_hSplitVar && element()->totalRows() > 1) {
m_hSplitVar = new bool[element()->totalRows()];
for (int i = 0; i < element()->totalRows(); i++) m_hSplitVar[i] = true;
}
for (int r = 0; r < element()->totalRows(); r++) {
for (int c = 0; c < element()->totalCols(); c++) {
bool fixed = false;
if (child->isFrameSet())
fixed = static_cast<RenderFrameSet*>(child)->element()->noResize();
else
fixed = static_cast<RenderFrame*>(child)->element()->noResize();
if (fixed) {
if (element()->totalCols() > 1) {
if (c>0) m_vSplitVar[c-1] = false;
m_vSplitVar[c] = false;
}
if (element()->totalRows() > 1) {
if (r>0) m_hSplitVar[r-1] = false;
m_hSplitVar[r] = false;
}
child = child->nextSibling();
if (!child)
goto end1;
}
}
}
}
end1:
RenderContainer::layout();
end2:
setNeedsLayout(false);
}
void RenderFrameSet::calcMinMaxWidth()
{
RenderContainer::calcMinMaxWidth();
if (!flattenFrameset())
return;
if (!parent()->isFrameSet() && !element()->document()->frame()->ownerElement())
m_minWidth = max(m_minWidth, 200);
m_maxWidth = m_minWidth;
setMinMaxKnown();
}
void RenderFrameSet::positionFrames()
{
int r;
int c;
RenderObject *child = firstChild();
if (!child)
return;
int yPos = 0;
for (r = 0; r < element()->totalRows(); r++) {
int xPos = 0;
for (c = 0; c < element()->totalCols(); c++) {
child->setPos(xPos, yPos);
if ((m_gridLayout[1][c] != child->width()) || (m_gridLayout[0][r] != child->height())) {
child->setWidth(m_gridLayout[1][c]);
child->setHeight(m_gridLayout[0][r]);
child->setNeedsLayout(true, false);
child->layout();
}
xPos += m_gridLayout[1][c] + element()->border();
child = child->nextSibling();
if (!child)
return;
}
yPos += m_gridLayout[0][r] + element()->border();
}
while (child) {
child->setWidth(0);
child->setHeight(0);
child->setNeedsLayout(false);
child = child->nextSibling();
}
}
void RenderFrameSet::positionFramesWithFlattening()
{
RenderObject* child = firstChild();
if (!child)
return;
int rows = frameSet()->totalRows();
int cols = frameSet()->totalCols();
int yPos = 0;
int borderThickness = frameSet()->border();
bool changes = false;
bool out = false;
int widest = 0;
for(int r = 0; r < rows && !out; r++) {
int highest = 0;
int xPos = 0;
int extra = 0;
int height = m_gridLayout[0][r];
for(int c = 0; c < cols; c++) {
IntRect oldRect(child->xPos(), child->yPos(), child->width(), child->height());
child->setPos(xPos, yPos);
int width = m_gridLayout[1][c];
child->setWidth(width ? width + extra / (cols - c) : 0);
child->setHeight(height);
child->setNeedsLayout(true, false);
bool flexibleWidth = true;
bool flexibleHeight = true;
if (frameSet()->m_cols)
flexibleWidth = !frameSet()->m_cols[c].isFixed();
if (frameSet()->m_rows)
flexibleHeight = !frameSet()->m_rows[r].isFixed();
if (child->isFrameSet())
static_cast<RenderFrameSet*>(child)->layout();
else
static_cast<RenderFrame*>(child)->layoutWithFlattening(flexibleWidth, flexibleHeight);
if (IntRect(child->xPos(), child->yPos(), child->width(), child->height()) != oldRect)
changes = true;
extra += width - child->width();
if (highest < child->height())
highest = child->height();
xPos += child->width() + borderThickness;
child = child->nextSibling();
if (!child) {
out = true;
break;
}
}
if (xPos > widest)
widest = xPos;
yPos += highest + frameSet()->border();
}
m_height = yPos - borderThickness;
m_width = max(m_width, widest - borderThickness);
out = false;
child = firstChild();
for(int r = 0; r < rows && !out; r++) {
for(int c = 0; c < cols; c++) {
IntSize oldSize(child->width(), child->height());
if (r == rows - 1)
child->setHeight(m_height - child->yPos());
if (c == cols - 1)
child->setWidth(m_width - child->xPos());
m_gridLayout[1][c] = child->width();
m_gridLayout[0][r] = child->height();
if (IntSize(child->width(), child->height()) != oldSize) {
child->setNeedsLayout(true, false);
if (child->isFrameSet())
static_cast<RenderFrameSet*>(child)->layout();
else
static_cast<RenderFrame*>(child)->layoutWithFlattening(false, false);
changes = true;
}
child = child->nextSibling();
if (!child) {
out = true;
break;
}
}
}
if (changes)
repaint();
for (; child; child = child->nextSibling()) {
child->setWidth(0);
child->setHeight(0);
child->setNeedsLayout(false);
}
}
bool RenderFrameSet::flattenFrameset() const
{
return element()->document()->frame() && element()->document()->frame()->settings()->flatFrameSetLayoutEnabled();
}
bool RenderFrameSet::userResize(MouseEvent* evt)
{
if (needsLayout())
return false;
bool res = false;
int _x = evt->pageX();
int _y = evt->pageY();
if (!m_resizing && evt->type() == mousemoveEvent || evt->type() == mousedownEvent) {
m_hSplit = -1;
m_vSplit = -1;
int pos = m_gridLayout[1][0] + xPos();
for (int c = 1; c < element()->totalCols(); c++) {
if (_x >= pos && _x <= pos+element()->border()) {
if (m_vSplitVar && m_vSplitVar[c - 1])
m_vSplit = c - 1;
res = true;
break;
}
pos += m_gridLayout[1][c] + element()->border();
}
pos = m_gridLayout[0][0] + yPos();
for (int r = 1; r < element()->totalRows(); r++) {
if (_y >= pos && _y <= pos+element()->border()) {
if (m_hSplitVar && m_hSplitVar[r - 1])
m_hSplit = r - 1;
res = true;
break;
}
pos += m_gridLayout[0][r] + element()->border();
}
if (evt->type() == mousedownEvent) {
setResizing(true);
m_vSplitPos = _x;
m_hSplitPos = _y;
m_oldpos = -1;
} else
view()->frameView()->setCursor(pointerCursor());
}
if (m_resizing && evt->type() == mouseupEvent) {
setResizing(false);
if (m_vSplit != -1) {
int delta = m_vSplitPos - _x;
m_gridDelta[1][m_vSplit] -= delta;
m_gridDelta[1][m_vSplit+1] += delta;
}
if (m_hSplit != -1) {
int delta = m_hSplitPos - _y;
m_gridDelta[0][m_hSplit] -= delta;
m_gridDelta[0][m_hSplit+1] += delta;
}
setNeedsLayout(true);
} else if (m_resizing || evt->type() == mouseupEvent) {
FrameView* v = view()->frameView();
v->disableFlushDrawing();
GraphicsContext* context = v->lockDrawingFocus();
IntRect r(xPos(), yPos(), width(), height());
const int rBord = 3;
int sw = element()->border();
int p = m_resizing ? (m_vSplit > -1 ? _x : _y) : -1;
const RGBA32 greyQuarterOpacity = 0x40A0A0A0;
if (m_vSplit > -1) {
if (m_oldpos >= 0)
v->updateContents(IntRect(m_oldpos + sw/2 - rBord, r.y(), 2 * rBord, r.height()), true);
if (p >= 0) {
context->setPen(Pen::NoPen);
context->setFillColor(greyQuarterOpacity);
context->drawRect(IntRect(p + sw/2 - rBord, r.y(), 2 * rBord, r.height()));
}
} else {
if (m_oldpos >= 0)
v->updateContents(IntRect(r.x(), m_oldpos + sw/2 - rBord, r.width(), 2 * rBord), true);
if (p >= 0) {
context->setPen(Pen::NoPen);
context->setFillColor(greyQuarterOpacity);
context->drawRect(IntRect(r.x(), p + sw/2 - rBord, r.width(), 2 * rBord));
}
}
m_oldpos = p;
v->unlockDrawingFocus(context);
v->enableFlushDrawing();
}
return res;
}
void RenderFrameSet::setResizing(bool e)
{
m_resizing = e;
for (RenderObject* p = parent(); p; p = p->parent())
if (p->isFrameSet())
static_cast<RenderFrameSet*>(p)->m_clientResizing = m_resizing;
view()->frameView()->setResizingFrameSet(e ? element() : 0);
}
bool RenderFrameSet::canResize(int _x, int _y)
{
if (needsLayout() || !m_gridLayout[0] || !m_gridLayout[1])
return false;
int pos = m_gridLayout[1][0];
for (int c = 1; c < element()->totalCols(); c++)
if (_x >= pos && _x <= pos+element()->border())
return true;
pos = m_gridLayout[0][0];
for (int r = 1; r < element()->totalRows(); r++)
if (_y >= pos && _y <= pos+element()->border())
return true;
return false;
}
bool RenderFrameSet::isChildAllowed(RenderObject* child, RenderStyle* style) const
{
return child->isFrame() || child->isFrameSet();
}
#ifndef NDEBUG
void RenderFrameSet::dump(TextStream* stream, DeprecatedString ind) const
{
*stream << " totalrows=" << element()->totalRows();
*stream << " totalcols=" << element()->totalCols();
unsigned i;
for (i = 0; i < (unsigned)element()->totalRows(); i++)
*stream << " hSplitvar(" << i << ")=" << m_hSplitVar[i];
for (i = 0; i < (unsigned)element()->totalCols(); i++)
*stream << " vSplitvar(" << i << ")=" << m_vSplitVar[i];
RenderContainer::dump(stream,ind);
}
#endif
}