diff options
Diffstat (limited to 'dw/table.cc')
-rw-r--r-- | dw/table.cc | 1192 |
1 files changed, 1192 insertions, 0 deletions
diff --git a/dw/table.cc b/dw/table.cc new file mode 100644 index 00000000..4135799b --- /dev/null +++ b/dw/table.cc @@ -0,0 +1,1192 @@ +/* + * Dillo Widget + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +//#define DBG + +#include "table.hh" +#include "../lout/misc.hh" + +#define MAX misc::max + + +namespace dw { + +int Table::CLASS_ID = -1; + +Table::Table(bool limitTextWidth) +{ + registerName ("dw::Table", &CLASS_ID); + setFlags (USES_HINTS); + + this->limitTextWidth = limitTextWidth; + + rowClosed = false; + + // random values + availWidth = 100; + availAscent = 100; + availDescent = 0; + + numRows = 0; + numCols = 0; + curRow = -1; + curCol = 0; + + children = new misc::SimpleVector <Child*> (16); + colExtremes = new misc::SimpleVector<core::Extremes> (8); + colWidths = new misc::SimpleVector <int> (8); + cumHeight = new misc::SimpleVector <int> (8); + rowSpanCells = new misc::SimpleVector <int> (8); + colSpanCells = new misc::SimpleVector <int> (8); + baseline = new misc::SimpleVector <int> (8); + rowStyle = new misc::SimpleVector <core::style::Style*> (8); + + hasColPercent = 0; + colPercents = new misc::SimpleVector <float> (8); + + redrawX = 0; + redrawY = 0; +} + + +Table::~Table() +{ + for (int i = 0; i < children->size (); i++) { + if (children->get(i)) { + switch (children->get(i)->type) { + case Child::CELL: + delete children->get(i)->cell.widget; + break; + case Child::SPAN_SPACE: + break; + } + + delete children->get(i); + } + } + + for (int i = 0; i < rowStyle->size (); i++) + if (rowStyle->get (i)) + rowStyle->get(i)->unref (); + + delete children; + delete colExtremes; + delete colWidths; + delete cumHeight; + delete rowSpanCells; + delete colSpanCells; + delete baseline; + delete rowStyle; + delete colPercents; +} + +void Table::sizeRequestImpl (core::Requisition *requisition) +{ + forceCalcCellSizes (); + + /** + * \bug Baselines are not regarded here. + */ + requisition->width = getStyle()->boxDiffWidth () + + (numCols + 1) * getStyle()->hBorderSpacing; + for (int col = 0; col < numCols; col++) + requisition->width += colWidths->get (col); + + requisition->ascent = + getStyle()->boxDiffHeight () + cumHeight->get (numRows) + + getStyle()->vBorderSpacing; + requisition->descent = 0; + +} + +void Table::getExtremesImpl (core::Extremes *extremes) +{ + if (numCols == 0) { + extremes->minWidth = extremes->maxWidth = 0; + return; + } + + forceCalcColumnExtremes (); + + extremes->minWidth = extremes->maxWidth = + (numCols + 1) * getStyle()->hBorderSpacing + + getStyle()->boxDiffWidth (); + for (int col = 0; col < numCols; col++) { + extremes->minWidth += colExtremes->getRef(col)->minWidth; + extremes->maxWidth += colExtremes->getRef(col)->maxWidth; + } + if (core::style::isAbsLength (getStyle()->width)) { + extremes->minWidth = + MAX (extremes->minWidth, + core::style::absLengthVal(getStyle()->width)); + extremes->maxWidth = + MAX (extremes->maxWidth, + core::style::absLengthVal(getStyle()->width)); + } + +#ifdef DBG + printf(" Table::getExtremesImpl, {%d, %d} numCols=%d\n", + extremes->minWidth, extremes->maxWidth, numCols); +#endif +} + +void Table::sizeAllocateImpl (core::Allocation *allocation) +{ + calcCellSizes (); + + /** + * \bug Baselines are not regarded here. + */ + + int offy = + allocation->y + getStyle()->boxOffsetY () + getStyle()->vBorderSpacing; + int x = + allocation->x + getStyle()->boxOffsetX () + getStyle()->hBorderSpacing; + + for (int col = 0; col < numCols; col++) { + for (int row = 0; row < numRows; row++) { + int n = row * numCols + col; + if (childDefined (n)) { + int width = + (children->get(n)->cell.colspanEff - 1) + * getStyle()->hBorderSpacing; + for (int i = 0; i < children->get(n)->cell.colspanEff; i++) + width += colWidths->get (col + i); + + core::Allocation childAllocation; + core::Requisition childRequisition; + + children->get(n)->cell.widget->sizeRequest (&childRequisition); + + childAllocation.x = x; + childAllocation.y = cumHeight->get (row) + offy; + childAllocation.width = width; + childAllocation.ascent = childRequisition.ascent; + childAllocation.descent = + cumHeight->get (row + children->get(n)->cell.rowspan) + - cumHeight->get (row) - getStyle()->vBorderSpacing + - childRequisition.ascent; + children->get(n)->cell.widget->sizeAllocate (&childAllocation); + } + } + + x += colWidths->get (col) + getStyle()->hBorderSpacing; + } +} + +void Table::resizeDrawImpl () +{ + queueDrawArea (redrawX, 0, allocation.width - redrawX, getHeight ()); + queueDrawArea (0, redrawY, allocation.width, getHeight () - redrawY); + redrawX = allocation.width; + redrawY = getHeight (); +} + +void Table::setWidth (int width) +{ + // If limitTextWidth is set, a queueResize may also be necessary. + if (availWidth != width || limitTextWidth) { +#ifdef DBG + printf(" Table::setWidth %d\n", width); +#endif + availWidth = width; + queueResize (0, false); + } +} + +void Table::setAscent (int ascent) +{ + if (availAscent != ascent) { + availAscent = ascent; + queueResize (0, false); + } +} + +void Table::setDescent (int descent) +{ + if (availDescent != descent) { + availDescent = descent; + queueResize (0, false); + } +} + +void Table::draw (core::View *view, core::Rectangle *area) +{ + // Can be optimized, by iterating on the lines in area. + drawWidgetBox (view, area, false); + +#if 0 + int offx = getStyle()->boxOffsetX () + getStyle()->hBorderSpacing; + int offy = getStyle()->boxOffsetY () + getStyle()->vBorderSpacing; + int width = getContentWidth (); + + // This part seems unnecessary. It also segfaulted sometimes when + // cumHeight size was less than numRows. --jcid + for (int row = 0; row < numRows; row++) { + if (rowStyle->get (row)) + drawBox (view, rowStyle->get (row), area, + offx, offy + cumHeight->get (row), + width - 2*getStyle()->hBorderSpacing, + cumHeight->get (row + 1) - cumHeight->get (row) + - getStyle()->vBorderSpacing, false); + } +#endif + + for (int i = 0; i < children->size (); i++) { + if (childDefined (i)) { + Widget *child = children->get(i)->cell.widget; + core::Rectangle childArea; + if (child->intersects (area, &childArea)) + child->draw (view, &childArea); + } + } +} + +void Table::removeChild (Widget *child) +{ + /** \bug Not implemented. */ +} + +core::Iterator *Table::iterator (core::Content::Type mask, bool atEnd) +{ + return new TableIterator (this, mask, atEnd); +} + +void Table::addCell (Widget *widget, int colspan, int rowspan) +{ + Child *child; + int colspanEff; + + // We limit the values for colspan and rowspan to 50, to avoid + // attacks by malicious web pages. + if (colspan > 50 || colspan < 0) { + fprintf (stderr, "WARNING: colspan = %d is set to 50.\n", colspan); + colspan = 50; + } + if (rowspan > 50 || rowspan <= 0) { + fprintf (stderr, "WARNING: rowspan = %d is set to 50.\n", rowspan); + rowspan = 50; + } + + if (numRows == 0) { + // to prevent a crash + fprintf (stderr, "WARNING: Cell without row.\n"); + addRow (NULL); + } + + if (rowClosed) { + fprintf (stderr, "WARNING: Last cell had colspan=0.\n"); + addRow (NULL); + } + + if (colspan == 0) { + colspanEff = MAX (numCols - curCol, 1); + rowClosed = true; + } else + colspanEff = colspan; + + // Find next free cell- + while (curCol < numCols && + (child = children->get(curRow * numCols + curCol)) != NULL && + child->type == Child::SPAN_SPACE) + curCol++; + +#ifdef DBG + printf("Table::addCell numCols=%d,curCol=%d,colspan=%d,colspanEff=%d\n", + numCols, curCol, colspan, colspanEff); +#endif + // Increase children array, when necessary. + if (curRow + rowspan > numRows) + reallocChildren (numCols, curRow + rowspan); + if (curCol + colspanEff > numCols) + reallocChildren (curCol + colspanEff, numRows); + + // Fill span space. + for (int col = 0; col < colspanEff; col++) + for (int row = 0; row < rowspan; row++) + if (!(col == 0 && row == 0)) { + child = new Child (); + child->type = Child::SPAN_SPACE; + child->spanSpace.startCol = curCol; + child->spanSpace.startRow = curRow; + children->set ((curRow + row) * numCols + curCol + col, child); + } + + // Set the "root" cell. + child = new Child (); + child->type = Child::CELL; + child->cell.widget = widget; + child->cell.colspanOrig = colspan; + child->cell.colspanEff = colspanEff; + child->cell.rowspan = rowspan; + children->set (curRow * numCols + curCol, child); + + curCol += colspanEff; + + widget->setParent (this); + if (rowStyle->get (curRow)) + widget->setBgColor (rowStyle->get(curRow)->backgroundColor); + queueResize (0, true); + +#if 0 + // show table structure in stdout + for (int row = 0; row < numRows; row++) { + for (int col = 0; col < numCols; col++) { + int n = row * numCols + col; + if (!(child = children->get (n))) { + printf("[null ] "); + } else if (children->get(n)->type == Child::CELL) { + printf("[CELL rs=%d] ", child->cell.rowspan); + } else if (children->get(n)->type == Child::SPAN_SPACE) { + printf("[SPAN rs=%d] ", child->cell.rowspan); + } else { + printf("[Unk. ] "); + } + } + printf("\n"); + } + printf("\n"); +#endif +} + +void Table::addRow (core::style::Style *style) +{ + curRow++; + + if (curRow >= numRows) + reallocChildren (numCols, curRow + 1); + + if (rowStyle->get (curRow)) + rowStyle->get(curRow)->unref (); + + rowStyle->set (curRow, style); + if (style) + style->ref (); + + curCol = 0; + rowClosed = false; +} + +TableCell *Table::getCellRef () +{ + core::Widget *child; + + for (int row = 0; row <= numRows; row++) { + int n = curCol + row * numCols; + if (childDefined (n)) { + child = children->get(n)->cell.widget; + if (child->instanceOf (TableCell::CLASS_ID)) + return (TableCell*)child; + } + } + + return NULL; +} + +void Table::reallocChildren (int newNumCols, int newNumRows) +{ + assert (newNumCols >= numCols); + assert (newNumRows >= numRows); + + children->setSize (newNumCols * newNumRows); + + if (newNumCols > numCols) { + // Complicated case, array got also wider. + for (int row = newNumRows - 1; row >= 0; row--) { + int colspan0Col = -1, colspan0Row = -1; + + // Copy old part. + for (int col = numCols - 1; col >= 0; col--) { + int n = row * newNumCols + col; + children->set (n, children->get (row * numCols + col)); + if (children->get (n)) { + switch (children->get(n)->type) { + case Child::CELL: + if (children->get(n)->cell.colspanOrig == 0) { + colspan0Col = col; + colspan0Row = row; + children->get(n)->cell.colspanEff = newNumCols - col; + } + break; + case Child::SPAN_SPACE: + if (children->get(children->get(n)->spanSpace.startRow + * numCols + + children->get(n)->spanSpace.startCol) + ->cell.colspanOrig == 0) { + colspan0Col = children->get(n)->spanSpace.startCol; + colspan0Row = children->get(n)->spanSpace.startRow; + } + break; + } + } + } + + // Fill rest of the column. + if (colspan0Col == -1) { + for (int col = numCols; col < newNumCols; col++) + children->set (row * newNumCols + col, NULL); + } else { + for (int col = numCols; col < newNumCols; col++) { + Child *child = new Child (); + child->type = Child::SPAN_SPACE; + child->spanSpace.startCol = colspan0Col; + child->spanSpace.startRow = colspan0Row; + children->set (row * newNumCols + col, child); + } + } + } + } + + // Bottom part of the children array. + for (int row = numRows; row < newNumRows; row++) + for (int col = 0; col < newNumCols; col++) + children->set (row * newNumCols + col, NULL); + + // Simple arrays. + rowStyle->setSize (newNumRows); + for (int row = numRows; row < newNumRows; row++) + rowStyle->set (row, NULL); + // Rest is increased, when needed. + + numCols = newNumCols; + numRows = newNumRows; +} + +// ---------------------------------------------------------------------- + +void Table::calcCellSizes () +{ + if (needsResize ()) + forceCalcCellSizes (); +} + + +void Table::forceCalcCellSizes () +{ + int totalWidth = 0, childHeight, forceTotalWidth = 1; + core::Extremes extremes; + + // Will also call calcColumnExtremes(), when needed. + getExtremes (&extremes); + + if (core::style::isAbsLength (getStyle()->width)) { + totalWidth = core::style::absLengthVal (getStyle()->width); + } else if (core::style::isPerLength (getStyle()->width)) { + /* + * If the width is > 100%, we use 100%, this prevents ugly + * results. (May be changed in future, when a more powerful + * rendering is implemented, to handle fixed positions etc., + * as defined by CSS2.) + */ + totalWidth = + (int)(availWidth + * misc::min (core::style::perLengthVal (getStyle()->width), + 1.0)); + } else if (getStyle()->width == core::style::LENGTH_AUTO) { + totalWidth = availWidth; + forceTotalWidth = 0; + } +#ifdef DBG + printf(" availWidth = %d\n", availWidth); + printf(" totalWidth1 = %d\n", totalWidth); +#endif + if (totalWidth < extremes.minWidth) + totalWidth = extremes.minWidth; + totalWidth = totalWidth + - (numCols + 1) * getStyle()->hBorderSpacing + - getStyle()->boxDiffWidth (); +#ifdef DBG + printf(" totalWidth2 = %d curCol=%d\n", totalWidth,curCol); +#endif + + colWidths->setSize (numCols, 0); + cumHeight->setSize (numRows + 1, 0); + rowSpanCells->setSize (0); + baseline->setSize (numRows); +#ifdef DBG + printf(" extremes = %d,%d\n", extremes.minWidth, extremes.maxWidth); + printf(" getStyle()->boxDiffWidth() = %d\n", getStyle()->boxDiffWidth()); + printf(" getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing); +#endif + + apportion_percentages2 (totalWidth, forceTotalWidth); + if (!hasColPercent) + apportion2 (totalWidth, forceTotalWidth); + + setCumHeight (0, 0); + for (int row = 0; row < numRows; row++) { + /** + * \bug dw::Table::baseline is not filled. + */ + int rowHeight = 0; + + for (int col = 0; col < numCols; col++) { + int n = row * numCols + col; + if (childDefined (n)) { + int width = (children->get(n)->cell.colspanEff - 1) + * getStyle()->hBorderSpacing; + for (int i = 0; i < children->get(n)->cell.colspanEff; i++) + width += colWidths->get (col + i); + + core::Requisition childRequisition; + children->get(n)->cell.widget->setWidth (width); + children->get(n)->cell.widget->sizeRequest (&childRequisition); + childHeight = childRequisition.ascent + childRequisition.descent; + if (children->get(n)->cell.rowspan == 1) { + rowHeight = MAX (rowHeight, childHeight); + } else { + rowSpanCells->increase(); + rowSpanCells->set(rowSpanCells->size()-1, n); + } + } + }/*for col*/ + + setCumHeight (row + 1, + cumHeight->get (row) + rowHeight + getStyle()->vBorderSpacing); + + }/*for row*/ + + apportionRowSpan (); +} + +void Table::apportionRowSpan () +{ + int *rowHeight = NULL; + + for (int c = 0; c < rowSpanCells->size(); ++c) { + int n = rowSpanCells->get(c); + int row = n / numCols; + int rs = children->get(n)->cell.rowspan; + int sumRows = cumHeight->get(row+rs) - cumHeight->get(row); + core::Requisition childRequisition; + children->get(n)->cell.widget->sizeRequest (&childRequisition); + int spanHeight = childRequisition.ascent + childRequisition.descent + + getStyle()->vBorderSpacing; + if (sumRows >= spanHeight) + continue; + + // Cell size is too small. +#ifdef DBG + printf("Short cell %d, sumRows=%d spanHeight=%d\n", + n,sumRows,spanHeight); +#endif + // Fill height array + if (!rowHeight) { + rowHeight = new int[numRows]; + for (int i = 0; i < numRows; i++) + rowHeight[i] = cumHeight->get(i+1) - cumHeight->get(i); + } +#ifdef DBG + printf (" rowHeight { "); + for (int i = 0; i < numRows; i++) + printf ("%d ", rowHeight[i]); + printf ("}\n"); +#endif + + // Calc new row sizes for this span. + int cumHnew_i = 0, cumh_i = 0, hnew_i; + for (int i = row; i < row + rs; ++i) { + hnew_i = + sumRows == 0 ? (int)((float)(spanHeight-cumHnew_i)/(row+rs-i)) : + (sumRows-cumh_i) <= 0 ? 0 : + (int)((float)(spanHeight-cumHnew_i)*rowHeight[i]/(sumRows-cumh_i)); +#ifdef DBG + printf (" i=%-3d h=%d hnew_i=%d =%d*%d/%d cumh_i=%d cumHnew_i=%d\n", + i,rowHeight[i],hnew_i, + spanHeight-cumHnew_i,rowHeight[i],sumRows-cumh_i, + cumh_i, cumHnew_i); +#endif + cumHnew_i += hnew_i; + cumh_i += rowHeight[i]; + rowHeight[i] = hnew_i; + } + // Update cumHeight + for (int i = 0; i < numRows; ++i) + setCumHeight (i+1, cumHeight->get(i) + rowHeight[i]); + } + delete[] rowHeight; +} + + +/** + * \brief Fills dw::Table::colExtremes, only if recalculation is necessary. + * + * \bug Some parts are missing. + */ +void Table::calcColumnExtremes () +{ + if (extremesChanged ()) + forceCalcColumnExtremes (); +} + + +/** + * \brief Fills dw::Table::colExtremes in all cases. + */ +void Table::forceCalcColumnExtremes () +{ +#ifdef DBG + printf(" Table::forceCalcColumnExtremes numCols=%d\n", numCols); +#endif + if (numCols == 0) + return; + + colExtremes->setSize (numCols); + colPercents->setSize (numCols); + colSpanCells->setSize (0); + /* 1. cells with colspan = 1 */ + for (int col = 0; col < numCols; col++) { + colExtremes->getRef(col)->minWidth = 0; + colExtremes->getRef(col)->maxWidth = 0; + colPercents->set(col, LEN_AUTO); + + for (int row = 0; row < numRows; row++) { + int n = row * numCols + col; + if (!childDefined (n)) + continue; + if (children->get(n)->cell.colspanEff == 1) { + core::Extremes cellExtremes; + int cellMinW, cellMaxW, pbm; + core::style::Length width = + children->get(n)->cell.widget->getStyle()->width; + pbm = (numCols + 1) * getStyle()->hBorderSpacing + + children->get(n)->cell.widget->getStyle()->boxDiffWidth (); + children->get(n)->cell.widget->getExtremes (&cellExtremes); + if (core::style::isAbsLength (width)) { + // Fixed lengths include table padding, border and margin. + cellMinW = cellExtremes.minWidth; + cellMaxW = MAX (cellMinW, + core::style::absLengthVal(width) - pbm); + } else { + cellMinW = cellExtremes.minWidth; + cellMaxW = cellExtremes.maxWidth; + } +#ifdef DBG + printf("FCCE, col%d colMin,colMax,cellMin,cellMax = %d,%d,%d,%d\n", + col, + colExtremes->getRef(col)->minWidth, + colExtremes->getRef(col)->maxWidth, + cellMinW, cellMaxW); +#endif + colExtremes->getRef(col)->minWidth = + MAX (colExtremes->getRef(col)->minWidth, cellMinW); + colExtremes->getRef(col)->maxWidth = + MAX (colExtremes->getRef(col)->minWidth, MAX ( + colExtremes->getRef(col)->maxWidth, + cellMaxW)); + + // Also fill the colPercents array in this pass + if (core::style::isPerLength (width)) { + hasColPercent = 1; + if (colPercents->get(col) == LEN_AUTO) + colPercents->set(col, core::style::perLengthVal(width)); + } else if (core::style::isAbsLength (width)) { + // We treat LEN_ABS as a special case of LEN_AUTO. + /* + * if (colPercents->get(col) == LEN_AUTO) + * colPercents->set(col, LEN_ABS); + */ + } + } else { + colSpanCells->increase(); + colSpanCells->set(colSpanCells->size()-1, n); + } + } + } + + /* 2. cells with colspan > 1 */ + /* If needed, here we set proportionally apportioned col maximums */ + for (int c = 0; c < colSpanCells->size(); ++c) { + core::Extremes cellExtremes; + int cellMinW, cellMaxW, pbm; + int n = colSpanCells->get(c); + int col = n % numCols; + int cs = children->get(n)->cell.colspanEff; + core::style::Length width = + children->get(n)->cell.widget->getStyle()->width; + pbm = (numCols + 1) * getStyle()->hBorderSpacing + + children->get(n)->cell.widget->getStyle()->boxDiffWidth (); + children->get(n)->cell.widget->getExtremes (&cellExtremes); + if (core::style::isAbsLength (width)) { + // Fixed lengths include table padding, border and margin. + cellMinW = cellExtremes.minWidth; + cellMaxW = MAX (cellMinW, core::style::absLengthVal(width) - pbm); + } else { + cellMinW = cellExtremes.minWidth; + cellMaxW = cellExtremes.maxWidth; + } + int minSumCols = 0, maxSumCols = 0; + for (int i = 0; i < cs; ++i) { + minSumCols += colExtremes->getRef(col+i)->minWidth; + maxSumCols += colExtremes->getRef(col+i)->maxWidth; + } +#ifdef DBG + printf("cs=%d spanWidth=%d,%d sumCols=%d,%d\n", + cs,cellMinW,cellMaxW,minSumCols,maxSumCols); +#endif + if (minSumCols >= cellMinW && maxSumCols >= cellMaxW) + continue; + + // Cell size is too small; apportion {min,max} for this colspan. + int spanMinW = MAX (MAX(cs, minSumCols), + cellMinW - (cs-1) * getStyle()->hBorderSpacing), + spanMaxW = MAX (MAX(cs, maxSumCols), + cellMaxW - (cs-1) * getStyle()->hBorderSpacing); + + if (minSumCols == 0) { + // No single cells defined for this span => pre-apportion equally + minSumCols = spanMinW; maxSumCols = spanMaxW; + int minW = spanMinW, maxW = spanMaxW; + for (int i = 0; i < cs; ++i) { + colExtremes->getRef(col+i)->minWidth = minW / (cs - i); + colExtremes->getRef(col+i)->maxWidth = maxW / (cs - i); + minW -= colExtremes->getRef(col+i)->minWidth; + maxW -= colExtremes->getRef(col+i)->maxWidth; + } + } + + // This numbers will help if the span has percents. + int spanHasColPercent = 0; + int availSpanMinW = spanMinW; + float cumSpanPercent = 0.0f; + for (int i = col; i < col + cs; ++i) { + if (colPercents->get(i) > 0.0f) { + cumSpanPercent += colPercents->get(i); + ++spanHasColPercent; + } else + availSpanMinW -= colExtremes->getRef(i)->minWidth; + } + + // Calculate weighted-apportion columns for this span. + int wMin = 0, wMax; + int cumMaxWnew = 0, cumMaxWold = 0, goalMaxW = spanMaxW; + int curAppW = maxSumCols; + int curExtraW = spanMinW - minSumCols; + for (int i = col; i < col + cs; ++i) { + + if (!spanHasColPercent) { + int d_a = colExtremes->getRef(i)->maxWidth; + int d_w = curAppW > 0 ? (int)((float)curExtraW * d_a/curAppW) : 0; + if (d_a < 0||d_w < 0) { + printf("d_a=%d d_w=%d\n",d_a,d_w); + exit(1); + } + wMin = colExtremes->getRef(i)->minWidth + d_w; + colExtremes->getRef(i)->minWidth = wMin; + curExtraW -= d_w; + curAppW -= d_a; + } else { + if (colPercents->get(i) > 0.0f) { + wMin = MAX (colExtremes->getRef(i)->minWidth, + (int)(availSpanMinW + * colPercents->get(i)/cumSpanPercent)); + colExtremes->getRef(i)->minWidth = wMin; + } + } + + wMax = (goalMaxW-cumMaxWnew <= 0) ? 0 : + (int)((float)(goalMaxW-cumMaxWnew) + * colExtremes->getRef(i)->maxWidth + / (maxSumCols-cumMaxWold)); + wMax = MAX (wMin, wMax); + cumMaxWnew += wMax; + cumMaxWold += colExtremes->getRef(i)->maxWidth; + colExtremes->getRef(i)->maxWidth = wMax; +#ifdef DBG + printf ("i=%d, wMin=%d wMax=%d cumMaxWold=%d\n", + i,wMin,wMax,cumMaxWold); +#endif + } +#ifdef DBG + printf ("col min,max: ["); + for (int i = 0; i < numCols; i++) + printf ("%d,%d ", + colExtremes->getRef(i)->minWidth, + colExtremes->getRef(i)->maxWidth); + printf ("]\n"); + printf ("getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing); +#endif + } +} + +/** + * \brief Apportionment function for AUTO-length columns. + * 'extremes' comes filled, 'result' comes defined for percentage columns. + */ +void Table::apportion2 (int totalWidth, int forceTotalWidth) +{ + if (colExtremes->size() == 0) + return; +#ifdef DBG + printf("app2, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n", + availWidth, totalWidth, forceTotalWidth); + printf("app2, extremes: ( "); + for (int i = 0; i < colExtremes->size (); i++) + printf("%d,%d ", + colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth); + printf(")\n"); +#endif + int minAutoWidth = 0, maxAutoWidth = 0, availAutoWidth = totalWidth; + for (int col = 0; col < numCols; col++) { + if (colPercents->get(col) == LEN_ABS) { // set absolute lengths + setColWidth (col, colExtremes->get(col).minWidth); + } + if (colPercents->get(col) == LEN_AUTO) { + maxAutoWidth += colExtremes->get(col).maxWidth; + minAutoWidth += colExtremes->get(col).minWidth; + } else + availAutoWidth -= colWidths->get(col); + } + + if (!maxAutoWidth) // no LEN_AUTO cols! + return; + + colWidths->setSize (colExtremes->size (), 0); + + if (!forceTotalWidth && maxAutoWidth < availAutoWidth) { + // Enough space for the maximum table, don't widen past max. + availAutoWidth = maxAutoWidth; + } + + // General case. + int curTargetWidth = MAX (availAutoWidth, minAutoWidth); + int curExtraWidth = curTargetWidth - minAutoWidth; + int curMaxWidth = maxAutoWidth; + int curNewWidth = minAutoWidth; + for (int col = 0; col < numCols; col++) { +#ifdef DBG + printf("app2, col %d, minWidth=%d maxWidth=%d\n", + col,extremes->get(col).minWidth, colExtremes->get(col).maxWidth); +#endif + if (colPercents->get(col) != LEN_AUTO) + continue; + + int colMinWidth = colExtremes->getRef(col)->minWidth; + int colMaxWidth = colExtremes->getRef(col)->maxWidth; + int w = (curMaxWidth <= 0) ? 0 : + (int)((float)curTargetWidth * colMaxWidth/curMaxWidth); +#ifdef DBG + printf("app2, curTargetWidth=%d colMaxWidth=%d curMaxWidth=%d " + "curNewWidth=%d ", + curTargetWidth, colMaxWidth,curMaxWidth,curNewWidth); + printf("w = %d, ", w); +#endif + if (w <= colMinWidth) + w = colMinWidth; + else if (curNewWidth - colMinWidth + w > curTargetWidth) + w = colMinWidth + curExtraWidth; +#ifdef DBG + printf("w = %d\n", w); +#endif + curNewWidth -= colMinWidth; + curMaxWidth -= colMaxWidth; + curExtraWidth -= (w - colMinWidth); + curTargetWidth -= w; + setColWidth (col, w); + } +#ifdef DBG + printf("app2, result: ( "); + for (int i = 0; i < colWidths->size (); i++) + printf("%d ", colWidths->get (i)); + printf(")\n"); +#endif +} + +void Table::apportion_percentages2(int totalWidth, int forceTotalWidth) +{ + int hasTablePercent = core::style::isPerLength (getStyle()->width) ? 1 : 0; + + if (colExtremes->size() == 0 || (!hasTablePercent && !hasColPercent)) + return; + + // If there's a table-wide percentage, totalWidth comes already scaled. +#ifdef DBG + printf("APP_P, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n", + availWidth, totalWidth, forceTotalWidth); +#endif + + if (!hasColPercent) { +#ifdef DBG + printf("APP_P, only a table-wide percentage\n"); + printf("APP_P, extremes = { "); + for (int col = 0; col < numCols; col++) + printf("%d,%d ", colExtremes->getRef(col)->minWidth, + colExtremes->getRef(col)->maxWidth); + printf("}\n"); +#endif + // It has only a table-wide percentage. Apportion non-absolute widths. + int sumMaxWidth = 0, perAvailWidth = totalWidth; + for (int col = 0; col < numCols; col++) { + if (colPercents->get(col) == LEN_ABS) + perAvailWidth -= colExtremes->getRef(col)->maxWidth; + else + sumMaxWidth += colExtremes->getRef(col)->maxWidth; + } +#ifdef DBG + printf("APP_P, perAvailWidth=%d, sumMaxWidth=%d\n", + perAvailWidth, sumMaxWidth); +#endif + for (int col = 0; col < numCols; col++) { + int max_wi = colExtremes->getRef(col)->maxWidth, new_wi; + if (colPercents->get(col) != LEN_ABS) { + new_wi = MAX (colExtremes->getRef(col)->minWidth, + (int)((float)max_wi * perAvailWidth/sumMaxWidth)); + setColWidth (col, new_wi); + perAvailWidth -= new_wi; + sumMaxWidth -= max_wi; + } + } +#ifdef DBG + printf("APP_P, result = { "); + for (int col = 0; col < numCols; col++) + printf("%d ", result->get(col)); + printf("}\n"); +#endif + + } else { + // we'll have to apportion... +#ifdef DBG + printf("APP_P, we'll have to apportion...\n"); +#endif + // Calculate cumPercent and available space + float cumPercent = 0.0f; + int hasAutoCol = 0; + int sumMinWidth = 0, sumMaxWidth = 0, sumMinNonPer = 0, sumMaxNonPer = 0; + for (int col = 0; col < numCols; col++) { + if (colPercents->get(col) > 0.0f) { + cumPercent += colPercents->get(col); + } else { + sumMinNonPer += colExtremes->getRef(col)->minWidth; + sumMaxNonPer += colExtremes->getRef(col)->maxWidth; + hasAutoCol += (colPercents->get(col) == LEN_AUTO); + } + sumMinWidth += colExtremes->getRef(col)->minWidth; + sumMaxWidth += colExtremes->getRef(col)->maxWidth; +#ifdef DBG + printf("APP_P, col %d minWidth=%d maxWidth=%d\n", col, + colExtremes->getRef(col)->minWidth, + colExtremes->getRef(col)->maxWidth); +#endif + } + int oldTotalWidth = totalWidth; + if (!forceTotalWidth) { + if (sumMaxNonPer == 0 || cumPercent < 0.99f) { + // only percentage columns, or cumPercent < 100% => restrict width + int totW = (int)(sumMaxNonPer/(1.0f-cumPercent)); + for (int col = 0; col < numCols; col++) { + totW = MAX (totW, (int)(colExtremes->getRef(col)->maxWidth + / colPercents->get(col))); + } + totalWidth = misc::min (totW, totalWidth); + } + } + + // make sure there's enough space + totalWidth = MAX (totalWidth, sumMinWidth); + // extraWidth is always >= 0 + int extraWidth = totalWidth - sumMinWidth; + int sumMinWidthPer = sumMinWidth - sumMinNonPer; + int curPerWidth = sumMinWidthPer; + // percentages refer to workingWidth + int workingWidth = totalWidth - sumMinNonPer; + if (cumPercent < 0.99f) { + // In this case, use the whole table width + workingWidth = totalWidth; + curPerWidth = sumMinWidth; + } +#ifdef DBG + printf("APP_P, oldTotalWidth=%d totalWidth=%d" + " workingWidth=%d extraWidth=%d sumMinNonPer=%d\n", + oldTotalWidth,totalWidth,workingWidth,extraWidth,sumMinNonPer); +#endif + for (int col = 0; col < numCols; col++) { + int colMinWidth = colExtremes->getRef(col)->minWidth; + if (colPercents->get(col) >= 0.0f) { + int w = (int)(workingWidth * colPercents->get(col)); + if (w < colMinWidth) + w = colMinWidth; + else if (curPerWidth - colMinWidth + w > workingWidth) + w = colMinWidth + extraWidth; + extraWidth -= (w - colMinWidth); + curPerWidth += (w - colMinWidth); + setColWidth (col, w); + } else { + setColWidth (col, colMinWidth); + } + } + + if (cumPercent < 0.99f) { + // Will have to apportion the other columns +#ifdef DBG + printf("APP_P, extremes: ( "); + for (int i = 0; i < colExtremes->size (); i++) + printf("%d,%d ", + colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth); + printf(")\n"); +#endif + curPerWidth -= sumMinNonPer; + int perWidth = (int)(curPerWidth/cumPercent); + totalWidth = MAX (totalWidth, perWidth); + totalWidth = misc::min (totalWidth, oldTotalWidth); +#ifdef DBG + printf("APP_P, curPerWidth=%d perWidth=%d, totalWidth=%d\n", + curPerWidth, perWidth, totalWidth); +#endif + if (hasAutoCol == 0) { + // Special case, cumPercent < 100% and no other columns to expand. + // We'll honor totalWidth by expanding the percentage cols. + int extraWidth = totalWidth - curPerWidth - sumMinNonPer; + for (int col = 0; col < numCols; col++) { + if (colPercents->get(col) >= 0.0f) { + int d = (int)(extraWidth * colPercents->get(col)/cumPercent); + setColWidth (col, colWidths->get(col) + d); + } + } + } + } +#ifdef DBG + printf("APP_P, result ={ "); + for (int col = 0; col < numCols; col++) + printf("%d ", colWidths->get(col)); + printf("}\n"); +#endif + apportion2 (totalWidth, 2); + +#ifdef DBG + printf("APP_P, percent={"); + for (int col = 0; col < numCols; col++) + printf("%f ", colPercents->get(col)); + printf("}\n"); + printf("APP_P, result ={ "); + for (int col = 0; col < numCols; col++) + printf("%d ", colWidths->get(col)); + printf("}\n"); +#endif + } +} + +// ---------------------------------------------------------------------- + +Table::TableIterator::TableIterator (Table *table, + core::Content::Type mask, bool atEnd): + core::Iterator (table, mask, atEnd) +{ + index = atEnd ? table->children->size () : -1; + content.type = atEnd ? core::Content::END : core::Content::START; +} + +Table::TableIterator::TableIterator (Table *table, + core::Content::Type mask, int index): + core::Iterator (table, mask, false) +{ + this->index = index; + + if (index < 0) + content.type = core::Content::START; + else if (index >= table->children->size ()) + content.type = core::Content::END; + else { + content.type = core::Content::WIDGET; + content.widget = table->children->get(index)->cell.widget; + } +} + +object::Object *Table::TableIterator::clone() +{ + return new TableIterator ((Table*)getWidget(), getMask(), index); +} + +int Table::TableIterator::compareTo(misc::Comparable *other) +{ + return index - ((TableIterator*)other)->index; +} + +bool Table::TableIterator::next () +{ + Table *table = (Table*)getWidget(); + + if (content.type == core::Content::END) + return false; + + // tables only contain widgets: + if ((getMask() & core::Content::WIDGET) == 0) { + content.type = core::Content::END; + return false; + } + + do { + index++; + if (index >= table->children->size ()) { + content.type = core::Content::END; + return false; + } + } while (table->children->get(index) == NULL || + table->children->get(index)->type != Child::CELL); + + content.type = core::Content::WIDGET; + content.widget = table->children->get(index)->cell.widget; + return true; +} + +bool Table::TableIterator::prev () +{ + Table *table = (Table*)getWidget(); + + if (content.type == core::Content::START) + return false; + + // tables only contain widgets: + if ((getMask() & core::Content::WIDGET) == 0) { + content.type = core::Content::START; + return false; + } + + do { + index--; + if (index < 0) { + content.type = core::Content::START; + return false; + } + } while (table->children->get(index) == NULL || + table->children->get(index)->type != Child::CELL); + + content.type = core::Content::WIDGET; + content.widget = table->children->get(index)->cell.widget; + return true; +} + +void Table::TableIterator::highlight (int start, int end, + core::HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void Table::TableIterator::unhighlight (int direction, + core::HighlightLayer layer) +{ +} + +void Table::TableIterator::getAllocation (int start, int end, + core::Allocation *allocation) +{ + /** \bug Not implemented. */ +} + +} // namespace dw |