webengine/osswebengine/WebCore/rendering/FixedTableLayout.cpp
changeset 0 dd21522fd290
child 92 e1bea15f9a39
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebCore/rendering/FixedTableLayout.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,300 @@
+/*
+ * This file is part of the HTML rendering engine for KDE.
+ *
+ * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
+ *           (C) 2002 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2006 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "FixedTableLayout.h"
+
+#include "RenderTable.h"
+#include "RenderTableCell.h"
+#include "RenderTableCol.h"
+#include "RenderTableSection.h"
+
+/*
+  The text below is from the CSS 2.1 specs.
+
+  Fixed table layout
+
+  With this (fast) algorithm, the horizontal layout of the table does
+  not depend on the contents of the cells; it only depends on the
+  table's width, the width of the columns, and borders or cell
+  spacing.
+
+  The table's width may be specified explicitly with the 'width'
+  property. A value of 'auto' (for both 'display: table' and 'display:
+  inline-table') means use the automatic table layout algorithm.
+
+  In the fixed table layout algorithm, the width of each column is
+  determined as follows:
+
+    1. A column element with a value other than 'auto' for the 'width'
+    property sets the width for that column.
+
+    2. Otherwise, a cell in the first row with a value other than
+    'auto' for the 'width' property sets the width for that column. If
+    the cell spans more than one column, the width is divided over the
+    columns.
+
+    3. Any remaining columns equally divide the remaining horizontal
+    table space (minus borders or cell spacing).
+
+  The width of the table is then the greater of the value of the
+  'width' property for the table element and the sum of the column
+  widths (plus cell spacing or borders). If the table is wider than
+  the columns, the extra space should be distributed over the columns.
+
+
+  In this manner, the user agent can begin to lay out the table once
+  the entire first row has been received. Cells in subsequent rows do
+  not affect column widths. Any cell that has content that overflows
+  uses the 'overflow' property to determine whether to clip the
+  overflow content.
+*/
+
+using namespace std;
+
+namespace WebCore {
+
+FixedTableLayout::FixedTableLayout(RenderTable* table)
+    : TableLayout(table)
+{
+}
+
+int FixedTableLayout::calcWidthArray(int tableWidth)
+{
+    int usedWidth = 0;
+
+    // iterate over all <col> elements
+    RenderObject* child = m_table->firstChild();
+    int cCol = 0;
+    int nEffCols = m_table->numEffCols();
+    m_width.resize(nEffCols);
+    m_width.fill(Length(Auto));
+
+    Length grpWidth;
+    while (child) {
+        if (child->isTableCol()) {
+            RenderTableCol *col = static_cast<RenderTableCol *>(child);
+            int span = col->span();
+            if (col->firstChild())
+                grpWidth = col->style()->width();
+            else {
+                Length w = col->style()->width();
+                if (w.isAuto())
+                    w = grpWidth;
+                int effWidth = 0;
+                if (w.isFixed() && w.value() > 0)
+                    effWidth = w.value();
+                
+                int usedSpan = 0;
+                int i = 0;
+                while (usedSpan < span) {
+                    if(cCol + i >= nEffCols) {
+                        m_table->appendColumn(span - usedSpan);
+                        nEffCols++;
+                        m_width.resize(nEffCols);
+                        m_width[nEffCols-1] = Length();
+                    }
+                    int eSpan = m_table->spanOfEffCol(cCol+i);
+                    if ((w.isFixed() || w.isPercent()) && w.isPositive()) {
+                        m_width[cCol + i].setRawValue(w.type(), w.rawValue() * eSpan);
+                        usedWidth += effWidth * eSpan;
+                    }
+                    usedSpan += eSpan;
+                    i++;
+                }
+                cCol += i;
+            }
+        } else
+            break;
+
+        RenderObject *next = child->firstChild();
+        if (!next)
+            next = child->nextSibling();
+        if (!next && child->parent()->isTableCol()) {
+            next = child->parent()->nextSibling();
+            grpWidth = Length();
+        }
+        child = next;
+    }
+
+    // Iterate over the first row in case some are unspecified.
+    RenderTableSection* section = m_table->header();
+    if (!section)
+        section = m_table->firstBody();
+    if (!section)
+        section = m_table->footer();
+    if (section && !section->numRows())
+        section = m_table->sectionBelow(section, true);
+    if (section) {
+        cCol = 0;
+        RenderObject* firstRow = section->firstChild();
+        child = firstRow->firstChild();
+        while (child) {
+            if (child->isTableCell()) {
+                RenderTableCell* cell = static_cast<RenderTableCell*>(child);
+                if (cell->prefWidthsDirty())
+                    cell->calcPrefWidths();
+
+                Length w = cell->styleOrColWidth();
+                int span = cell->colSpan();
+                int effWidth = 0;
+                if (w.isFixed() && w.isPositive())
+                    effWidth = w.value();
+                
+                int usedSpan = 0;
+                int i = 0;
+                while (usedSpan < span) {
+                    ASSERT(cCol + i < nEffCols);
+                    int eSpan = m_table->spanOfEffCol(cCol + i);
+                    // Only set if no col element has already set it.
+                    if (m_width[cCol + i].isAuto() && w.type() != Auto) {
+                        m_width[cCol + i].setRawValue(w.type(), w.rawValue() * eSpan / span);
+                        usedWidth += effWidth * eSpan / span;
+                    }
+                    usedSpan += eSpan;
+                    i++;
+                }
+                cCol += i;
+            }
+            child = child->nextSibling();
+        }
+    }
+
+    return usedWidth;
+}
+
+void FixedTableLayout::calcPrefWidths(int& minWidth, int& maxWidth)
+{
+    // FIXME: This entire calculation is incorrect for both minwidth and maxwidth.
+    
+    // we might want to wait until we have all of the first row before
+    // layouting for the first time.
+
+    // only need to calculate the minimum width as the sum of the
+    // cols/cells with a fixed width.
+    //
+    // The maximum width is max(minWidth, tableWidth).
+    int bs = m_table->bordersPaddingAndSpacing();
+    
+    int tableWidth = m_table->style()->width().isFixed() ? m_table->style()->width().value() - bs : 0;
+    int mw = calcWidthArray(tableWidth) + bs;
+
+    minWidth = max(mw, tableWidth);
+    maxWidth = minWidth;
+}
+
+void FixedTableLayout::layout()
+{
+    int tableWidth = m_table->width() - m_table->bordersPaddingAndSpacing();
+    int nEffCols = m_table->numEffCols();
+    Vector<int> calcWidth(nEffCols, 0);
+
+    int numAuto = 0;
+    int totalFixedWidth = 0;
+    int totalPercentWidth = 0;
+    int totalRawPercent = 0;
+
+    // Compute requirements and try to satisfy fixed and percent widths.
+    // Percentages are of the table's width, so for example
+    // for a table width of 100px with columns (40px, 10%), the 10% compute
+    // to 10px here, and will scale up to 20px in the final (80px, 20px).
+    for (int i = 0; i < nEffCols; i++) {
+        if (m_width[i].isFixed()) {
+            calcWidth[i] = m_width[i].value();
+            totalFixedWidth += calcWidth[i];
+        } else if (m_width[i].isPercent()) {
+            calcWidth[i] = m_width[i].calcValue(tableWidth);
+            totalPercentWidth += calcWidth[i];
+            totalRawPercent += m_width[i].rawValue();
+        } else if (m_width[i].isAuto())
+            numAuto++;
+    }
+
+    int totalWidth = totalFixedWidth + totalPercentWidth;
+    if (!numAuto || totalWidth > tableWidth) {
+        // If there are no auto columns, or if the total is too wide, take
+        // what we have and scale it to fit as necessary.
+        if (totalWidth != tableWidth) {
+            // Fixed widths only scale up
+            if (totalFixedWidth && totalWidth < tableWidth) {
+                totalFixedWidth = 0;
+                for (int i = 0; i < nEffCols; i++) {
+                    if (m_width[i].isFixed()) {
+                        calcWidth[i] = calcWidth[i] * tableWidth / totalWidth;
+                        totalFixedWidth += calcWidth[i];
+                    }
+                }
+            }
+            if (totalRawPercent) {
+                totalPercentWidth = 0;
+                for (int i = 0; i < nEffCols; i++) {
+                    if (m_width[i].isPercent()) {
+                        calcWidth[i] = m_width[i].rawValue() * (tableWidth - totalFixedWidth) / totalRawPercent;
+                        totalPercentWidth += calcWidth[i];
+                    }
+                }
+            }
+            totalWidth = totalFixedWidth + totalPercentWidth;
+        }
+    } else {
+        // Divide the remaining width among the auto columns.
+        int remainingWidth = tableWidth - totalFixedWidth - totalPercentWidth;
+        int lastAuto = 0;
+        for (int i = 0; i < nEffCols; i++) {
+            if (m_width[i].isAuto()) {
+                calcWidth[i] = remainingWidth / numAuto;
+                remainingWidth -= calcWidth[i];
+                if (!remainingWidth)
+                    break;
+                lastAuto = i;
+                numAuto--;
+            }
+        }
+        // Last one gets the remainder.
+        if (remainingWidth)
+            calcWidth[lastAuto] += remainingWidth;
+        totalWidth = tableWidth;
+    }
+
+    if (totalWidth < tableWidth) {
+        // Spread extra space over columns.
+        int remainingWidth = tableWidth - totalWidth;
+        int total = nEffCols;
+        while (total) {
+            int w = remainingWidth / total;
+            remainingWidth -= w;
+            calcWidth[--total] += w;
+        }
+        calcWidth[nEffCols - 1] += remainingWidth;
+    }
+    
+    int pos = 0;
+    int hspacing = m_table->hBorderSpacing();
+    for (int i = 0; i < nEffCols; i++) {
+        m_table->columnPositions()[i] = pos;
+        pos += calcWidth[i] + hspacing;
+    }
+    m_table->columnPositions()[m_table->columnPositions().size() - 1] = pos;
+}
+
+} // namespace WebCore