Dillo v3.1.1-98-g318d1f14
Loading...
Searching...
No Matches
table.cc
Go to the documentation of this file.
1/*
2 * Dillo Widget
3 *
4 * Copyright 2005-2007, 2014 Sebastian Geerken <sgeerken@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20//#define DBG
21
22#include "table.hh"
23#include "../lout/msg.h"
24#include "../lout/misc.hh"
25#include "../lout/debug.hh"
26
27using namespace lout;
28
29namespace dw {
30
32int Table::CLASS_ID = -1;
33
34Table::Table(bool limitTextWidth)
35{
36 DBG_OBJ_CREATE ("dw::Table");
37 registerName ("dw::Table", &CLASS_ID);
38 setButtonSensitive(false);
39
40 this->limitTextWidth = limitTextWidth;
41
42 rowClosed = false;
43
44 numRows = 0;
45 numCols = 0;
46 curRow = -1;
47 curCol = 0;
48
49 DBG_OBJ_SET_NUM ("numCols", numCols);
50 DBG_OBJ_SET_NUM ("numRows", numCols);
51
52 children = new misc::SimpleVector <Child*> (16);
56 colWidths = new misc::SimpleVector <int> (8);
57 cumHeight = new misc::SimpleVector <int> (8);
58 rowSpanCells = new misc::SimpleVector <int> (8);
59 baseline = new misc::SimpleVector <int> (8);
60 rowStyle = new misc::SimpleVector <core::style::Style*> (8);
61
63 DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes",
65
68
69 redrawX = 0;
70 redrawY = 0;
71}
72
74{
75 for (int i = 0; i < children->size (); i++) {
76 if (children->get(i)) {
77 switch (children->get(i)->type) {
78 case Child::CELL:
79 delete children->get(i)->cell.widget;
80 break;
82 break;
83 }
84
85 delete children->get(i);
86 }
87 }
88
89 for (int i = 0; i < rowStyle->size (); i++)
90 if (rowStyle->get (i))
91 rowStyle->get(i)->unref ();
92
93 delete children;
94 delete colExtremes;
95 delete colWidthSpecified;
96 delete colWidthPercentage;
97 delete colWidths;
98 delete cumHeight;
99 delete rowSpanCells;
100 delete baseline;
101 delete rowStyle;
102
104}
105
107{
108 DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl");
109
110 forceCalcCellSizes (true);
111
117 for (int col = 0; col < numCols; col++)
118 requisition->width += colWidths->get (col);
119
122 requisition->descent = 0;
123
125 false);
126
127 // For the order, see similar reasoning for dw::Textblock.
129
130 DBG_OBJ_LEAVE ();
131}
132
134{
135 DBG_OBJ_ENTER0 ("resize", 0, "getExtremesImpl");
136
137 if (numCols == 0)
140 boxDiffWidth ();
141 else {
143
147 for (int col = 0; col < numCols; col++) {
148 extremes->minWidth += colExtremes->getRef(col)->minWidth;
150 colExtremes->getRef(col)->minWidthIntrinsic;
151 extremes->maxWidth += colExtremes->getRef(col)->maxWidth;
153 colExtremes->getRef(col)->maxWidthIntrinsic;
154 extremes->adjustmentWidth += colExtremes->getRef(col)->adjustmentWidth;
155 }
156 }
157
159
160 // For the order, see similar reasoning for dw::Textblock.
162
163 DBG_OBJ_LEAVE ();
164}
165
167{
168 DBG_OBJ_ENTER ("resize", 0, "sizeAllocateImpl", "%d, %d; %d * (%d + %d)",
171
173
174 calcCellSizes (true);
175
180 int offy = allocation->y + boxOffsetY () + getStyle()->vBorderSpacing;
181 int x = allocation->x + boxOffsetX () + getStyle()->hBorderSpacing;
182
183 for (int col = 0; col < numCols; col++) {
184 for (int row = 0; row < numRows; row++) {
185 int n = row * numCols + col;
186 if (childDefined (n)) {
187 int width = (children->get(n)->cell.colspanEff - 1)
189 for (int i = 0; i < children->get(n)->cell.colspanEff; i++)
190 width += colWidths->get (col + i);
191
192 core::Allocation childAllocation;
193 core::Requisition childRequisition;
194
195 children->get(n)->cell.widget->sizeRequest (&childRequisition);
196
197 childAllocation.x = x;
198 childAllocation.y = cumHeight->get (row) + offy;
199 childAllocation.width = width;
200 childAllocation.ascent = childRequisition.ascent;
201 childAllocation.descent =
202 cumHeight->get (row + children->get(n)->cell.rowspan)
204 - childRequisition.ascent;
205 children->get(n)->cell.widget->sizeAllocate (&childAllocation);
206 }
207 }
208
209 x += colWidths->get (col) + getStyle()->hBorderSpacing;
210 }
211
213
214 DBG_OBJ_LEAVE ();
215}
216
224
225int Table::getAvailWidthOfChild (Widget *child, bool forceValue)
226{
227 DBG_OBJ_ENTER ("resize", 0, "getAvailWidthOfChild", "%p, %s",
228 child, forceValue ? "true" : "false");
229
230 int width;
231 oof::OutOfFlowMgr *oofm;
232
233 if (isWidgetOOF(child) && (oofm = getWidgetOutOfFlowMgr(child)) &&
234 oofm->dealingWithSizeOfChild (child))
235 width = oofm->getAvailWidthOfChild (child, forceValue);
236 else {
237 // We do not calculate the column widths at this point, because
238 // this tends to be rather inefficient for tables with many
239 // cells:
240 //
241 // For each of the n cells, some text is added (say, only one word
242 // per cell). Textblock::addText will eventually (via addText0
243 // etc.) call this method, Table::getAvailWidthOfChild. If
244 // calcCellSizes() is called here, this will call
245 // forceCalcCellSizes(), since the last call, sizes have to be
246 // re-calculated (because cells have been added). This will
247 // calculate the extremes for each existing cell, so
248 // Widget::getExtremes is called n * (n + 1) / 2 times. Even if the
249 // extremes are cached (so that getExtremesImpl does not have to be
250 // called in each case), this would make rendering tables with more
251 // than a few hundred cells unacceptably slow.
252 //
253 // Instead, column widths are calculated in Table::sizeRequestImpl.
254 //
255 // An alternative would be incremental resizing for tables; this
256 // approach resembles the behaviour before GROWS.
257
258 // TODO Does it still make sence to return -1 when forceValue is
259 // set?
260 if (forceValue)
261 width = calcAvailWidthForDescendant (child);
262 else
263 width = -1;
264 }
265
266 DBG_OBJ_MSGF ("resize", 1, "=> %d", width);
267 DBG_OBJ_LEAVE ();
268 return width;
269}
270
272{
273 DBG_OBJ_ENTER ("resize", 0, "calcAvailWidthForDescendant", "%p", child);
274
275 // "child" is not a direct child, but a direct descendant. Search
276 // for the actual childs.
277 Widget *actualChild = child;
278 while (actualChild != NULL && actualChild->getParent () != this)
279 actualChild = actualChild->getParent ();
280
281 assert (actualChild != NULL);
282
283 // ActualChild->parentRef contains (indirectly) the position in the
284 // children array (see addCell()), so the column can be easily
285 // determined.
286 int childNo = getParentRefInFlowSubRef (actualChild->parentRef);
287 int col = childNo % numCols;
288 DBG_OBJ_MSGF ("resize", 1, "actualChild = %p, "
289 "childNo = getParentRefInFlowSubRef (%d) = %d, "
290 "column = %d %% %d = %d",
291 actualChild, actualChild->parentRef, childNo, childNo,
292 numCols, col);
293 int colspanEff = children->get(childNo)->cell.colspanEff;
294 DBG_OBJ_MSGF ("resize", 1, "calculated from column %d, colspanEff = %d",
295 col, colspanEff);
296
297 int width = (colspanEff - 1) * getStyle()->hBorderSpacing;
298 for (int i = 0; i < colspanEff; i++)
299 width += colWidths->get (col + i);
300 width = misc::max (width, 0);
301
302 if (child != actualChild) {
303 // For table cells (direct children: child == actualChild), CSS
304 // 'width' is already regarded in the column calculation.
305 // However, for children of the table cells, CSS 'width' must be
306 // regarded here.
307
308 int corrWidth = width;
309 child->calcFinalWidth (child->getStyle(), -1, this, 0, true, &corrWidth);
310
311 // But better not exceed it ... (TODO: Only here?)
312 width = misc::min (width, corrWidth);
313 }
314
315 DBG_OBJ_MSGF ("resize", 1, "=> %d", width);
316 DBG_OBJ_LEAVE ();
317 return width;
318}
319
320int Table::applyPerWidth (int containerWidth, core::style::Length perWidth)
321{
322 return core::style::multiplyWithPerLength (containerWidth, perWidth);
323}
324
325int Table::applyPerHeight (int containerHeight, core::style::Length perHeight)
326{
327 return core::style::multiplyWithPerLength (containerHeight, perHeight);
328}
329
331{
332 DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
333
334 for (int col = 0; col < numCols; col++) {
335 for (int row = 0; row < numRows; row++) {
336 int n = row * numCols + col;
337 if (childDefined (n))
338 children->get(n)->cell.widget->containerSizeChanged ();
339 }
340 }
341
343
344 DBG_OBJ_LEAVE ();
345}
346
348{
349 DBG_OBJ_ENTER ("resize", 0, "affectsSizeChangeContainerChild", "%p", child);
350
351 bool ret;
352
353 // This is a bit more complicated, as compared to the standard
354 // implementation (Widget::affectsSizeChangeContainerChild).
355 // Height would handled the same way, but width is more
356 // complicated: we would have to track numerous values here. Always
357 // returning true is correct in all cases, but generally
358 // inefficient.
359
360 // TODO Better solution?
361
362 ret = true;
363
364 DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false");
365 DBG_OBJ_LEAVE ();
366 return ret;
367}
368
370{
371 return true;
372}
373
375{
376 return true;
377}
378
379void Table::drawLevel (core::View *view, core::Rectangle *area, int level,
380 core::DrawingContext *context)
381{
382 DBG_OBJ_ENTER ("draw", 0, "Table::drawLevel", "[%d, %d, %d * %d], %s",
383 area->x, area->y, area->width, area->height,
384 stackingLevelText (level));
385
386#if 0
387 // This old code belongs perhaps to the background. Check when reactivated.
388 int offx = getStyle()->boxOffsetX () + getStyle()->hBorderSpacing;
389 int offy = getStyle()->boxOffsetY () + getStyle()->vBorderSpacing;
390 int width = getContentWidth ();
391
392 // This part seems unnecessary. It also segfaulted sometimes when
393 // cumHeight size was less than numRows. --jcid
394 for (int row = 0; row < numRows; row++) {
395 if (rowStyle->get (row))
396 drawBox (view, rowStyle->get (row), area,
397 offx, offy + cumHeight->get (row),
398 width - 2*getStyle()->hBorderSpacing,
399 cumHeight->get (row + 1) - cumHeight->get (row)
400 - getStyle()->vBorderSpacing, false);
401 }
402#endif
403
404 switch (level) {
405 case SL_IN_FLOW:
406 for (int i = 0; i < children->size (); i++) {
407 if (childDefined (i)) {
408 Widget *child = children->get(i)->cell.widget;
409 core::Rectangle childArea;
411 && child->intersects (this, area, &childArea))
412 child->draw (view, &childArea, context);
413 }
414 }
415 break;
416
417 default:
418 OOFAwareWidget::drawLevel (view, area, level, context);
419 break;
420 }
421
422 DBG_OBJ_LEAVE ();
423}
424
427 *context)
428{
429 DBG_OBJ_ENTER ("events", 0, "Table::getWidgetAtPointLevel", "%d, %d, %s",
430 x, y, stackingLevelText (level));
431
432 Widget *widgetAtPoint = NULL;
433
434 switch (level) {
435 case SL_IN_FLOW:
436 for (int i = children->size () - 1; widgetAtPoint == NULL && i >= 0;
437 i--) {
438 if (childDefined (i)) {
439 Widget *child = children->get(i)->cell.widget;
441 widgetAtPoint = child->getWidgetAtPoint (x, y, context);
442 }
443 }
444 break;
445
446 default:
447 widgetAtPoint =
448 OOFAwareWidget::getWidgetAtPointLevel (x, y, level, context);
449 break;
450 }
451
452 DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint);
453 DBG_OBJ_LEAVE ();
454
455 return widgetAtPoint;
456}
457
458void Table::removeChild (Widget *child)
459{
461}
462
464{
465 return new TableIterator (this, mask, atEnd);
466}
467
468void Table::addCell (Widget *widget, int colspan, int rowspan)
469{
470 DBG_OBJ_ENTER ("resize", 0, "addCell", "%p, %d, %d",
471 widget, colspan, rowspan);
472
473 const int maxspan = 100;
474 Child *child;
475 int colspanEff;
476
477 // We limit the values for colspan and rowspan to avoid
478 // attacks by malicious web pages.
479 if (colspan > maxspan || colspan < 0) {
480 MSG_WARN("colspan = %d is set to %d.\n", colspan, maxspan);
481 colspan = maxspan;
482 }
483 if (rowspan > maxspan || rowspan <= 0) {
484 MSG_WARN("rowspan = %d is set to %d.\n", rowspan, maxspan);
485 rowspan = maxspan;
486 }
487
488 if (numRows == 0) {
489 // to prevent a crash
490 MSG("addCell: cell without row.\n");
491 addRow (NULL);
492 }
493
494 if (rowClosed) {
495 MSG_WARN("Last cell had colspan=0.\n");
496 addRow (NULL);
497 }
498
499 if (colspan == 0) {
500 colspanEff = misc::max (numCols - curCol, 1);
501 rowClosed = true;
502 } else
503 colspanEff = colspan;
504
505 // Find next free cell-
506 while (curCol < numCols &&
507 (child = children->get(curRow * numCols + curCol)) != NULL &&
508 child->type == Child::SPAN_SPACE)
509 curCol++;
510
511 _MSG("Table::addCell numCols=%d,curCol=%d,colspan=%d,colspanEff=%d\n",
512 numCols, curCol, colspan, colspanEff);
513
514 // Increase children array, when necessary.
515 if (curRow + rowspan > numRows)
516 reallocChildren (numCols, curRow + rowspan);
517 if (curCol + colspanEff > numCols)
518 reallocChildren (curCol + colspanEff, numRows);
519
520 // Fill span space.
521 for (int col = 0; col < colspanEff; col++)
522 for (int row = 0; row < rowspan; row++)
523 if (!(col == 0 && row == 0)) {
524 int i = (curRow + row) * numCols + curCol + col;
525
526 child = children->get(i);
527 if (child) {
528 MSG("Overlapping spans in table.\n");
529 assert(child->type == Child::SPAN_SPACE);
530 delete child;
531 }
532 child = new Child ();
533 child->type = Child::SPAN_SPACE;
534 child->spanSpace.startCol = curCol;
535 child->spanSpace.startRow = curRow;
536 children->set (i, child);
537 }
538
539 // Set the "root" cell.
540 child = new Child ();
541 child->type = Child::CELL;
542 child->cell.widget = widget;
543 child->cell.colspanOrig = colspan;
544 child->cell.colspanEff = colspanEff;
545 child->cell.rowspan = rowspan;
546 children->set (curRow * numCols + curCol, child);
547
548 // The position in the children array is (indirectly) assigned to parentRef,
549 // although incremental resizing is not implemented. Useful, e. g., in
550 // calcAvailWidthForDescendant(). See also reallocChildren().
551 widget->parentRef = makeParentRefInFlow (curRow * numCols + curCol);
552 DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef);
553
554 curCol += colspanEff;
555
556 widget->setParent (this);
557 if (rowStyle->get (curRow))
558 widget->setBgColor (rowStyle->get(curRow)->backgroundColor);
559 queueResize (0, true);
560
561#if 0
562 // show table structure in stdout
563 for (int row = 0; row < numRows; row++) {
564 for (int col = 0; col < numCols; col++) {
565 int n = row * numCols + col;
566 if (!(child = children->get (n))) {
567 MSG("[null ] ");
568 } else if (children->get(n)->type == Child::CELL) {
569 MSG("[CELL rs=%d] ", child->cell.rowspan);
570 } else if (children->get(n)->type == Child::SPAN_SPACE) {
571 MSG("[SPAN rs=%d] ", child->cell.rowspan);
572 } else {
573 MSG("[Unk. ] ");
574 }
575 }
576 MSG("\n");
577 }
578 MSG("\n");
579#endif
580
581 DBG_OBJ_LEAVE ();
582}
583
585{
586 curRow++;
587
588 if (curRow >= numRows)
590
591 if (rowStyle->get (curRow))
592 rowStyle->get(curRow)->unref ();
593
594 rowStyle->set (curRow, style);
595 if (style)
596 style->ref ();
597
598 curCol = 0;
599 rowClosed = false;
600}
601
603{
604 core::Widget *child;
605
606 for (int row = 0; row <= numRows; row++) {
607 int n = curCol + row * numCols;
608 if (childDefined (n)) {
609 child = children->get(n)->cell.widget;
611 return (AlignedTableCell*)child;
612 }
613 }
614
615 return NULL;
616}
617
619{
620 switch (mod) {
621 case MIN:
622 return "MIN";
623
624 case MIN_INTR:
625 return "MIN_INTR";
626
627 case MIN_MIN:
628 return "MIN_MIN";
629
630 case MAX_MIN:
631 return "MAX_MIN";
632
633 case MAX:
634 return "MAX";
635
636 case MAX_INTR:
637 return "MAX_INTR";
638
639 case DATA:
640 return "DATA";
641
642 default:
644 return NULL;
645 }
646}
647
649{
650 switch (mod) {
651 case MIN:
652 return extremes->minWidth;
653
654 case MIN_INTR:
656
657 case MIN_MIN:
659
660 case MAX_MIN:
662
663 case MAX:
664 return extremes->maxWidth;
665
666 case MAX_INTR:
668
669 default:
671 return 0;
672 }
673}
674
675void Table::setExtreme (core::Extremes *extremes, ExtrMod mod, int value)
676{
677 switch (mod) {
678 case MIN:
679 extremes->minWidth = value;
680 break;
681
682 case MIN_INTR:
684 break;
685
686 // MIN_MIN and MAX_MIN not supported here.
687
688 case MAX:
689 extremes->maxWidth = value;
690 break;
691
692 case MAX_INTR:
694 break;
695
696 default:
698 }
699}
700
701int Table::getColExtreme (int col, ExtrMod mod, void *data)
702{
703 switch (mod) {
704 case DATA:
705 return ((misc::SimpleVector<int>*)data)->get (col);
706
707 default:
708 return getExtreme (colExtremes->getRef(col), mod);
709 }
710}
711
712void Table::setColExtreme (int col, ExtrMod mod, void *data, int value)
713{
714 switch (mod) {
715 case DATA:
716 ((misc::SimpleVector<int>*)data)->set (col, value);
717 /* fallthrough */
718
719 default:
720 setExtreme (colExtremes->getRef(col), mod, value);
721 }
722}
723
724void Table::reallocChildren (int newNumCols, int newNumRows)
725{
726 assert (newNumCols >= numCols);
727 assert (newNumRows >= numRows);
728
729 children->setSize (newNumCols * newNumRows);
730
731 if (newNumCols > numCols) {
732 // Complicated case, array got also wider.
733 for (int row = newNumRows - 1; row >= 0; row--) {
734 int colspan0Col = -1, colspan0Row = -1;
735
736 // Copy old part.
737 for (int col = numCols - 1; col >= 0; col--) {
738 int n = row * newNumCols + col;
739 children->set (n, children->get (row * numCols + col));
740 if (children->get (n)) {
741 switch (children->get(n)->type) {
742 case Child::CELL:
743 if (children->get(n)->cell.colspanOrig == 0) {
744 colspan0Col = col;
745 colspan0Row = row;
746 children->get(n)->cell.colspanEff = newNumCols - col;
747 }
748 break;
750 if (children->get(children->get(n)->spanSpace.startRow
751 * numCols +
752 children->get(n)->spanSpace.startCol)
753 ->cell.colspanOrig == 0) {
754 colspan0Col = children->get(n)->spanSpace.startCol;
755 colspan0Row = children->get(n)->spanSpace.startRow;
756 }
757 break;
758 }
759 }
760 }
761
762 // Fill rest of the column.
763 if (colspan0Col == -1) {
764 for (int col = numCols; col < newNumCols; col++)
765 children->set (row * newNumCols + col, NULL);
766 } else {
767 for (int col = numCols; col < newNumCols; col++) {
768 Child *child = new Child ();
769 child->type = Child::SPAN_SPACE;
770 child->spanSpace.startCol = colspan0Col;
771 child->spanSpace.startRow = colspan0Row;
772 children->set (row * newNumCols + col, child);
773 }
774 }
775 }
776 }
777
778 // Bottom part of the children array.
779 for (int row = numRows; row < newNumRows; row++)
780 for (int col = 0; col < newNumCols; col++)
781 children->set (row * newNumCols + col, NULL);
782
783 // Simple arrays.
784 rowStyle->setSize (newNumRows);
785 for (int row = numRows; row < newNumRows; row++)
786 rowStyle->set (row, NULL);
787 // Rest is increased, when needed.
788
789 if (newNumCols > numCols) {
790 // Re-calculate parentRef. See addCell().
791 for (int row = 1; row < newNumRows; row++)
792 for (int col = 0; col < newNumCols; col++) {
793 int n = row * newNumCols + col;
794 Child *child = children->get (n);
795 if (child != NULL && child->type == Child::CELL) {
797 DBG_OBJ_SET_NUM_O (child->cell.widget, "parentRef",
798 child->cell.widget->parentRef);
799 }
800 }
801 }
802
803 numCols = newNumCols;
804 numRows = newNumRows;
805
806 // We initiate the column widths with a random value, to have a
807 // defined available width for the children before the column
808 // widths are actually calculated.
809
810 colWidths->setSize (numCols, 100);
811
813 DBG_OBJ_SET_NUM ("colWidths.size", colWidths->size ());
814 for (int i = 0; i < colWidths->size (); i++)
815 DBG_OBJ_ARRSET_NUM ("colWidths", i, colWidths->get (i));
816 }
817
818 DBG_OBJ_SET_NUM ("numCols", numCols);
819 DBG_OBJ_SET_NUM ("numRows", numCols);
820}
821
822// ----------------------------------------------------------------------
823
824void Table::calcCellSizes (bool calcHeights)
825{
826 DBG_OBJ_ENTER ("resize", 0, "calcCellSizes", "%s",
827 calcHeights ? "true" : "false");
828
829 bool sizeChanged = needsResize () || resizeQueued ();
830 bool extremesChanget = extremesChanged () || extremesQueued ();
831
832 if (calcHeights ? (extremesChanget || sizeChanged) :
833 (extremesChanget || !colWidthsUpToDateWidthColExtremes))
834 forceCalcCellSizes (calcHeights);
835
836 DBG_OBJ_LEAVE ();
837}
838
839
840void Table::forceCalcCellSizes (bool calcHeights)
841{
842 DBG_OBJ_ENTER ("resize", 0, "forceCalcCellSizes", "%s",
843 calcHeights ? "true" : "false");
844
845 // Since Table::getAvailWidthOfChild does not calculate the column
846 // widths, and so initially a random value (100) is returned, a
847 // correction is necessary. The old values are temporary preserved
848 // ...
849
850 lout::misc::SimpleVector<int> oldColWidths (8);
851 oldColWidths.setSize (colWidths->size ());
852 colWidths->copyTo (&oldColWidths);
853
854 actuallyCalcCellSizes (calcHeights);
855
856 // ... and then compared to the new ones. In case of a difference,
857 // the cell is told about this.
858
859 for (int col = 0; col < colWidths->size (); col++) {
860 if (oldColWidths.get (col) != colWidths->get (col)) {
861 for (int row = 0; row < numRows; row++) {
862 int n = row * numCols + col, col2;
863 Child *child = children->get(n);
864 if (child) {
865 Widget *cell;
866 switch (child->type) {
867 case Child::CELL:
868 cell = child->cell.widget;
869 break;
870
872 // TODO Are Child::spanSpace::startRow and
873 // Child::spanSpace::startCol not defined?
874
875 // Search for actual cell. If not found, this means
876 // that a cell is spanning multiple columns *and*
877 // rows; in this case it has been processed before.
878
879 cell = NULL;
880 for (col2 = col - 1; col2 >= 0 && cell == NULL; col2--) {
881 int n2 = row * numCols + col2;
882 Child *child2 = children->get(n2);
883 if (child2 != NULL && child2->type == Child::CELL)
884 cell = child2->cell.widget;
885 }
886 break;
887
888 default:
890 cell = NULL;
891 }
892
893 if (cell)
894 cell->containerSizeChanged ();
895 }
896 }
897 }
898 }
899
900 DBG_OBJ_LEAVE ();
901}
902
903void Table::actuallyCalcCellSizes (bool calcHeights)
904{
905 DBG_OBJ_ENTER ("resize", 0, "actuallyCalcCellSizes", "%s",
906 calcHeights ? "true" : "false");
907
908 int childHeight;
910
911 // Will also call forceCalcColumnExtremes(), when needed.
913
914 int availWidth = getAvailWidth (true);
915 // When adjust_table_min_width is set, use perhaps the adjustment
916 // width for correction. (TODO: Is this necessary?)
917 int corrWidth =
919 int totalWidth = misc::max (availWidth, corrWidth)
920 - ((numCols + 1) * getStyle()->hBorderSpacing + boxDiffWidth ());
921
922 DBG_OBJ_MSGF ("resize", 1,
923 "totalWidth = max (%d, %d) - ((%d - 1) * %d + %d) = <b>%d</b>",
924 availWidth, corrWidth, numCols, getStyle()->hBorderSpacing,
925 boxDiffWidth (), totalWidth);
926
927 assert (colWidths->size () == numCols); // This is set in addCell.
928 cumHeight->setSize (numRows + 1, 0);
931
932 misc::SimpleVector<int> *oldColWidths = colWidths;
933 colWidths = new misc::SimpleVector <int> (8);
935
936 int minWidth = 0, minWidthIntrinsic = 0, maxWidth = 0;
937 for (int col = 0; col < colExtremes->size(); col++) {
938 minWidth += colExtremes->getRef(col)->minWidth;
939 minWidthIntrinsic += colExtremes->getRef(col)->minWidthIntrinsic;
940 maxWidth += colExtremes->getRef(col)->maxWidth;
941 }
942
943 // CSS 'width' defined and effective?
944 bool totalWidthSpecified = false;
945 if (getStyle()->width != core::style::LENGTH_AUTO) {
946 // Even if 'width' is defined, it may not have a defined value. We try
947 // this trick (should perhaps be replaced by a cleaner solution):
948 core::Requisition testReq = { -1, -1, -1 };
950 false);
951 if (testReq.width != -1)
952 totalWidthSpecified = true;
953 }
954
955 DBG_OBJ_MSGF ("resize", 1,
956 "minWidth = %d, minWidthIntrinsic = %d, maxWidth %d, "
957 "totalWidth = %d, %s",
958 minWidth, minWidthIntrinsic, maxWidth, totalWidth,
959 totalWidthSpecified ? "specified" : "not specified");
960
961 if (minWidth > totalWidth) {
962 DBG_OBJ_MSG ("resize", 1, "case 1: minWidth > totalWidth");
963
964 // The sum of all column minima is larger than the available
965 // width, so we narrow the columns (see also CSS2 spec,
966 // section 17.5, #6). We use a similar apportioning, but not
967 // bases on minimal and maximal widths, but on intrinsic minimal
968 // widths and corrected minimal widths. This way, intrinsic
969 // extremes are preferred (so avoiding columns too narrow for
970 // the actual contents), at the expenses of corrected ones
971 // (which means that sometimes CSS values are handled
972 // incorrectly).
973
974 // A special case is a table with columns whose widths are
975 // defined by percentage values. In this case, all other columns
976 // are applied the intrinsic minimal width, while larger values
977 // are applied to the columns with percentage width (but not
978 // larger than the corrected width). The left columns are
979 // preferred, but it is ensured that no column is narrower than
980 // the intrinsic minimum.
981 //
982 // Example two columns with both "width: 70%" will be displayed like
983 // this:
984 //
985 // --------------------------------------------------
986 // | | |
987 // --------------------------------------------------
988 //
989 // The first gets indeed 70% of the total width, the second only
990 // the rest.
991 //
992 // This somewhat strange behaviour tries to mimic the somewhat
993 // strange behaviour of Firefox and Chromium.
994
995 if (numColWidthPercentage == 0 || minWidthIntrinsic >= totalWidth) {
996 // Latter case (minWidthIntrinsic >= totalWidth): special treating
997 // of percentage values would not make sense.
998
999 DBG_OBJ_MSG ("resize", 1, "case 1a: simple apportioning");
1000
1001 apportion2 (totalWidth, 0, colExtremes->size() - 1, MIN_MIN, MAX_MIN,
1002 NULL, colWidths, 0);
1003 } else {
1004 DBG_OBJ_MSG ("resize", 1, "case 1b: treat percentages specially");
1005
1006 // Keep track of the width which is apportioned to the rest
1007 // of the columns with percentage width (widthPartPer), and
1008 // the minimal width (intrinsic minimum) which is needed for
1009 // the rest of these columns (minWidthIntrinsicPer).
1010
1011 int widthPartPer = totalWidth, minWidthIntrinsicPer = 0;
1012 for (int col = 0; col < colExtremes->size(); col++)
1013 if (colWidthPercentage->get (col))
1014 minWidthIntrinsicPer +=
1015 colExtremes->getRef(col)->minWidthIntrinsic;
1016 else
1017 // Columns without percentage width get only the
1018 // intrinsic mininal, so subtract this from the width for the
1019 // columns *with* percentage
1020 widthPartPer -=
1021 colExtremes->getRef(col)->minWidthIntrinsic;
1022
1023 DBG_OBJ_MSGF ("resize", 1,
1024 "widthPartPer = %d, minWidthIntrinsicPer = %d",
1025 widthPartPer, minWidthIntrinsicPer);
1026
1027 for (int col = 0; col < colExtremes->size(); col++)
1028 if (colWidthPercentage->get (col)) {
1029 int colWidth = colExtremes->getRef(col)->minWidth;
1030 int minIntr = colExtremes->getRef(col)->minWidthIntrinsic;
1031
1032 minWidthIntrinsicPer -= minIntr;
1033
1034 if (colWidth > widthPartPer - minWidthIntrinsicPer)
1035 colWidth = widthPartPer - minWidthIntrinsicPer;
1036
1037 colWidths->set (col, colWidth);
1038 widthPartPer -= colWidth;
1039
1040 DBG_OBJ_MSGF ("resize", 1,
1041 "#%d: colWidth = %d ... widthPartPer = %d, "
1042 "minWidthIntrinsicPer = %d",
1043 col, colWidth, widthPartPer, minWidthIntrinsicPer);
1044
1045 } else
1046 colWidths->set (col,
1047 colExtremes->getRef(col)->minWidthIntrinsic);
1048
1049 }
1050 } else if (totalWidthSpecified && totalWidth > maxWidth) {
1051 DBG_OBJ_MSG ("resize", 1,
1052 "case 2: totalWidthSpecified && totalWidth > maxWidth");
1053
1054 // The width is specified (and so enforced), but all maxima sum
1055 // up to less than this specified width. The columns will have
1056 // there maximal width, and the extra space is apportioned
1057 // according to the column widths, and so to the column
1058 // maxima. This is done by simply passing MAX twice to the
1059 // apportioning function.
1060
1061 // When column widths are specified (numColWidthSpecified > 0,
1062 // as calculated in forceCalcColumnExtremes()), they are treated
1063 // specially and excluded from the apportioning, so that the
1064 // specified column widths are enforced. An exception is when
1065 // all columns are specified: in this case they must be
1066 // enlargened to fill the whole table width.
1067
1068 if (numColWidthSpecified == 0 ||
1069 numColWidthSpecified == colExtremes->size()) {
1070 DBG_OBJ_MSG ("resize", 1,
1071 "subcase 2a: no or all columns with specified width");
1072 apportion2 (totalWidth, 0, colExtremes->size() - 1, MAX, MAX, NULL,
1073 colWidths, 0);
1074 } else {
1075 DBG_OBJ_MSGF ("resize", 1,
1076 "subcase 2b: %d column(s) with specified width",
1078
1079 // Seperate columns with specified and unspecified width, and
1080 // apply apportion2() only to the latter.
1081
1082 int numNotSpecified = colExtremes->size() - numColWidthSpecified;
1083
1084 misc::SimpleVector<int> widthsNotSpecified (numNotSpecified);
1085 widthsNotSpecified.setSize (numNotSpecified);
1086 misc::SimpleVector<int> apportionDest (numNotSpecified);
1087
1088 int totalWidthNotSpecified = totalWidth, indexNotSpecified = 0;
1089 for (int col = 0; col < colExtremes->size(); col++)
1090 if (colWidthSpecified->get (col))
1091 totalWidthNotSpecified -= colExtremes->getRef(col)->maxWidth;
1092 else {
1093 widthsNotSpecified.set (indexNotSpecified,
1094 colExtremes->getRef(col)->maxWidth);
1095 indexNotSpecified++;
1096 }
1097
1098 DBG_IF_RTFL {
1099 DBG_OBJ_MSGF ("resize", 1, "totalWidthNotSpecified = %d",
1100 totalWidthNotSpecified);
1101
1102 DBG_OBJ_MSG ("resize", 1, "widthsNotSpecified:");
1104
1105 for (int i = 0; i < widthsNotSpecified.size (); i++)
1106 DBG_OBJ_MSGF ("resize", 1, "#%d: %d",
1107 i, widthsNotSpecified.get (i));
1108
1109 DBG_OBJ_MSG_END ();
1110 }
1111
1112 apportion2 (totalWidthNotSpecified, 0, numNotSpecified - 1, DATA, DATA,
1113 (void*)&widthsNotSpecified, &apportionDest, 0);
1114
1115 DBG_IF_RTFL {
1116 DBG_OBJ_MSG ("resize", 1, "apportionDest:");
1118
1119 for (int i = 0; i < apportionDest.size (); i++)
1120 DBG_OBJ_MSGF ("resize", 1, "#%d: %d", i, apportionDest.get (i));
1121
1122 DBG_OBJ_MSG_END ();
1123 }
1124
1125 DBG_OBJ_MSG ("resize", 1, "finally setting column widths:");
1127
1128 indexNotSpecified = 0;
1129 for (int col = 0; col < colExtremes->size(); col++)
1130 if (colWidthSpecified->get (col)) {
1131 DBG_OBJ_MSGF ("resize", 1, "#%d: specified, gets maximum %d",
1132 col, colExtremes->getRef(col)->maxWidth);
1133 colWidths->set (col, colExtremes->getRef(col)->maxWidth);
1134 } else {
1135 DBG_OBJ_MSGF ("resize", 1, "#%d: not specified, gets value %d "
1136 "at position %d from temporary list",
1137 col, apportionDest.get (indexNotSpecified),
1138 indexNotSpecified);
1139 colWidths->set (col, apportionDest.get (indexNotSpecified));
1140 indexNotSpecified++;
1141 }
1142
1143 DBG_OBJ_MSG_END ();
1144 }
1145 } else {
1146 // Normal apportioning.
1147 int width =
1148 totalWidthSpecified ? totalWidth : misc::min (totalWidth, maxWidth);
1149 DBG_OBJ_MSGF ("resize", 1, "case 3: else; width = %d", width);
1150 apportion2 (width, 0, colExtremes->size() - 1, MIN, MAX, NULL, colWidths,
1151 0);
1152 }
1153
1154 // TODO: Adapted from old inline function "setColWidth". But (i) is
1155 // this anyway correct (col width is is not x)? And does the
1156 // performance gain actually play a role?
1157 for (int col = 0; col < colExtremes->size(); col++) {
1158 if (colWidths->get (col) != oldColWidths->get (col))
1160 }
1161
1162 DBG_IF_RTFL {
1163 DBG_OBJ_SET_NUM ("colWidths.size", colWidths->size ());
1164 for (int i = 0; i < colWidths->size (); i++)
1165 DBG_OBJ_ARRSET_NUM ("colWidths", i, colWidths->get (i));
1166 }
1167
1169 DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes",
1171
1172 for (int col = 0; col < numCols; col++) {
1173 if (col >= oldColWidths->size () || col >= colWidths->size () ||
1174 oldColWidths->get (col) != colWidths->get (col)) {
1175 // Column width has changed, tell children about this.
1176 for (int row = 0; row < numRows; row++) {
1177 int n = row * numCols + col;
1178 // TODO: Columns spanning several rows are only regarded
1179 // when the first column is affected.
1180 if (childDefined (n))
1181 children->get(n)->cell.widget->containerSizeChanged ();
1182 }
1183 }
1184 }
1185
1186 delete oldColWidths;
1187
1188 if (calcHeights) {
1189 setCumHeight (0, 0);
1190 for (int row = 0; row < numRows; row++) {
1194 int rowHeight = 0;
1195
1196 for (int col = 0; col < numCols; col++) {
1197 int n = row * numCols + col;
1198 if (childDefined (n)) {
1199 /* FIXME: Variable width is not used */
1200#if 0
1201 int width = (children->get(n)->cell.colspanEff - 1)
1203 for (int i = 0; i < children->get(n)->cell.colspanEff; i++)
1204 width += colWidths->get (col + i);
1205#endif
1206
1207 core::Requisition childRequisition;
1208 //children->get(n)->cell.widget->setWidth (width);
1209 children->get(n)->cell.widget->sizeRequest (&childRequisition);
1210 childHeight = childRequisition.ascent + childRequisition.descent;
1211 if (children->get(n)->cell.rowspan == 1) {
1212 rowHeight = misc::max (rowHeight, childHeight);
1213 } else {
1216 }
1217 }
1218 } // for col
1219
1220 setCumHeight (row + 1,
1221 cumHeight->get (row) + rowHeight + getStyle()->vBorderSpacing);
1222 } // for row
1223
1225 }
1226
1227 DBG_OBJ_LEAVE ();
1228}
1229
1231{
1232 DBG_OBJ_ENTER0 ("resize", 0, "apportionRowSpan");
1233
1234 int *rowHeight = NULL;
1235
1236 for (int c = 0; c < rowSpanCells->size(); ++c) {
1237 int n = rowSpanCells->get(c);
1238 int row = n / numCols;
1239 int rs = children->get(n)->cell.rowspan;
1240 int sumRows = cumHeight->get(row+rs) - cumHeight->get(row);
1241 core::Requisition childRequisition;
1242 children->get(n)->cell.widget->sizeRequest (&childRequisition);
1243 int spanHeight = childRequisition.ascent + childRequisition.descent
1245 if (sumRows >= spanHeight)
1246 continue;
1247
1248 // Cell size is too small.
1249 _MSG("Short cell %d, sumRows=%d spanHeight=%d\n",
1250 n,sumRows,spanHeight);
1251
1252 // Fill height array
1253 if (!rowHeight) {
1254 rowHeight = new int[numRows];
1255 for (int i = 0; i < numRows; i++)
1256 rowHeight[i] = cumHeight->get(i+1) - cumHeight->get(i);
1257 }
1258#ifdef DBG
1259 MSG(" rowHeight { ");
1260 for (int i = 0; i < numRows; i++)
1261 MSG("%d ", rowHeight[i]);
1262 MSG("}\n");
1263#endif
1264
1265 // Calc new row sizes for this span.
1266 int cumHnew_i = 0, cumh_i = 0, hnew_i;
1267 for (int i = row; i < row + rs; ++i) {
1268 hnew_i =
1269 sumRows == 0 ? (int)((float)(spanHeight-cumHnew_i)/(row+rs-i)) :
1270 (sumRows-cumh_i) <= 0 ? 0 :
1271 (int)((float)(spanHeight-cumHnew_i)*rowHeight[i]/(sumRows-cumh_i));
1272
1273 _MSG(" i=%-3d h=%d hnew_i=%d =%d*%d/%d cumh_i=%d cumHnew_i=%d\n",
1274 i,rowHeight[i],hnew_i,
1275 spanHeight-cumHnew_i,rowHeight[i],sumRows-cumh_i,
1276 cumh_i, cumHnew_i);
1277
1278 cumHnew_i += hnew_i;
1279 cumh_i += rowHeight[i];
1280 rowHeight[i] = hnew_i;
1281 }
1282 // Update cumHeight
1283 for (int i = 0; i < numRows; ++i)
1284 setCumHeight (i+1, cumHeight->get(i) + rowHeight[i]);
1285 }
1286 delete[] rowHeight;
1287
1288 DBG_OBJ_LEAVE ();
1289}
1290
1291
1296{
1297 DBG_OBJ_ENTER0 ("resize", 0, "forceCalcColumnExtremes");
1298
1299 if (numCols > 0) {
1300 lout::misc::SimpleVector<int> colSpanCells (8);
1301 colExtremes->setSize (numCols);
1304
1305 // 1. cells with colspan = 1
1306 for (int col = 0; col < numCols; col++) {
1307 DBG_OBJ_MSGF ("resize", 1, "column %d", col);
1309
1310 colWidthSpecified->set (col, false);
1311 colWidthPercentage->set (col, false);
1312
1313 colExtremes->getRef(col)->minWidth = 0;
1314 colExtremes->getRef(col)->minWidthIntrinsic = 0;
1315 colExtremes->getRef(col)->maxWidth = 0;
1316 colExtremes->getRef(col)->maxWidthIntrinsic = 0;
1317 colExtremes->getRef(col)->adjustmentWidth = 0;
1318
1319 for (int row = 0; row < numRows; row++) {
1320 DBG_OBJ_MSGF ("resize", 1, "row %d", row);
1322
1323 int n = row * numCols + col;
1324
1325 if (childDefined (n)) {
1326 if (children->get(n)->cell.colspanEff == 1) {
1327 core::Extremes cellExtremes;
1328 children->get(n)->cell.widget->getExtremes (&cellExtremes);
1329
1330 DBG_OBJ_MSGF ("resize", 1, "child: %d / %d",
1331 cellExtremes.minWidth, cellExtremes.maxWidth);
1332
1333 colExtremes->getRef(col)->minWidthIntrinsic =
1334 misc::max (colExtremes->getRef(col)->minWidthIntrinsic,
1335 cellExtremes.minWidthIntrinsic);
1336 colExtremes->getRef(col)->maxWidthIntrinsic =
1337 misc::max (colExtremes->getRef(col)->minWidthIntrinsic,
1338 colExtremes->getRef(col)->maxWidthIntrinsic,
1339 cellExtremes.maxWidthIntrinsic);
1340
1341 colExtremes->getRef(col)->minWidth =
1342 misc::max (colExtremes->getRef(col)->minWidth,
1343 cellExtremes.minWidth);
1344 colExtremes->getRef(col)->maxWidth =
1345 misc::max (colExtremes->getRef(col)->minWidth,
1346 colExtremes->getRef(col)->maxWidth,
1347 cellExtremes.maxWidth);
1348
1349 colExtremes->getRef(col)->adjustmentWidth =
1350 misc::max (colExtremes->getRef(col)->adjustmentWidth,
1351 cellExtremes.adjustmentWidth);
1352
1353 core::style::Length childWidth =
1354 children->get(n)->cell.widget->getStyle()->width;
1355 if (childWidth != core::style::LENGTH_AUTO) {
1356 colWidthSpecified->set (col, true);
1357 if (core::style::isPerLength (childWidth))
1358 colWidthPercentage->set (col, true);
1359 }
1360
1361 DBG_OBJ_MSGF ("resize", 1, "column: %d / %d (%d / %d)",
1362 colExtremes->getRef(col)->minWidth,
1363 colExtremes->getRef(col)->maxWidth,
1364 colExtremes->getRef(col)->minWidthIntrinsic,
1365 colExtremes->getRef(col)->maxWidthIntrinsic);
1366 } else {
1367 colSpanCells.increase ();
1368 colSpanCells.setLast (n);
1369 }
1370 }
1371
1372 DBG_OBJ_MSG_END ();
1373 }
1374
1375 DBG_OBJ_MSG_END ();
1376 }
1377
1378 // 2. cells with colspan > 1
1379
1380 // TODO: Is this old comment still relevant? "If needed, here we
1381 // set proportionally apportioned col maximums."
1382
1383 for (int i = 0; i < colSpanCells.size(); i++) {
1384 int n = colSpanCells.get (i);
1385 int col = n % numCols;
1386 int cs = children->get(n)->cell.colspanEff;
1387
1388 core::Extremes cellExtremes;
1389 children->get(n)->cell.widget->getExtremes (&cellExtremes);
1390
1391 calcExtremesSpanMultiCols (col, cs, &cellExtremes, MIN, MAX, NULL);
1392 calcExtremesSpanMultiCols (col, cs, &cellExtremes, MIN_INTR, MAX_INTR,
1393 NULL);
1394 calcAdjustmentWidthSpanMultiCols (col, cs, &cellExtremes);
1395
1396 core::style::Length childWidth =
1397 children->get(n)->cell.widget->getStyle()->width;
1398 if (childWidth != core::style::LENGTH_AUTO) {
1399 for (int j = 0; j < cs; j++)
1400 colWidthSpecified->set (col + j, true);
1401 if (core::style::isPerLength (childWidth))
1402 for (int j = 0; j < cs; j++)
1403 colWidthPercentage->set (col + j, true);
1404 }
1405 }
1406 }
1407
1410 for (int i = 0; i < colExtremes->size (); i++) {
1411 if (colWidthSpecified->get (i))
1413 if (colWidthPercentage->get (i))
1415 }
1416
1417 DBG_IF_RTFL {
1418 DBG_OBJ_SET_NUM ("colExtremes.size", colExtremes->size ());
1419 for (int i = 0; i < colExtremes->size (); i++) {
1420 DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "minWidth",
1421 colExtremes->get(i).minWidth);
1422 DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "minWidthIntrinsic",
1423 colExtremes->get(i).minWidthIntrinsic);
1424 DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "maxWidth",
1425 colExtremes->get(i).maxWidth);
1426 DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "maxWidthIntrinsic",
1427 colExtremes->get(i).maxWidthIntrinsic);
1428 }
1429
1430 DBG_OBJ_SET_NUM ("colWidthSpecified.size", colWidthSpecified->size ());
1431 for (int i = 0; i < colWidthSpecified->size (); i++)
1432 DBG_OBJ_ARRSET_BOOL ("colWidthSpecified", i,
1434 DBG_OBJ_SET_NUM ("numColWidthSpecified", numColWidthSpecified);
1435
1436 DBG_OBJ_SET_NUM ("colWidthPercentage.size", colWidthPercentage->size ());
1437 for (int i = 0; i < colWidthPercentage->size (); i++)
1438 DBG_OBJ_ARRSET_BOOL ("colWidthPercentage", i,
1440 DBG_OBJ_SET_NUM ("numColWidthPercentage", numColWidthPercentage);
1441 }
1442
1444 DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes",
1446
1447 DBG_OBJ_LEAVE ();
1448}
1449
1451 core::Extremes *cellExtremes,
1452 ExtrMod minExtrMod, ExtrMod maxExtrMod,
1453 void *extrData)
1454{
1455 DBG_OBJ_ENTER ("resize", 0, "calcExtremesSpanMulteCols",
1456 "%d, %d, ..., %s, %s, ...",
1457 col, cs, getExtrModName (minExtrMod),
1458 getExtrModName (maxExtrMod));
1459
1460 int cellMin = getExtreme (cellExtremes, minExtrMod);
1461 int cellMax = getExtreme (cellExtremes, maxExtrMod);
1462
1463 int minSumCols = 0, maxSumCols = 0;
1464
1465 for (int j = 0; j < cs; j++) {
1466 minSumCols += getColExtreme (col + j, minExtrMod, extrData);
1467 maxSumCols += getColExtreme (col + j, maxExtrMod, extrData);
1468 }
1469
1470 DBG_OBJ_MSGF ("resize", 1, "cs = %d, cell: %d / %d, sum: %d / %d\n",
1471 cs, cellMin, cellMax, minSumCols, maxSumCols);
1472
1473 bool changeMin = cellMin > minSumCols;
1474 bool changeMax = cellMax > maxSumCols;
1475 if (changeMin || changeMax) {
1476 // TODO This differs from the documentation? Should work, anyway.
1477 misc::SimpleVector<int> newMin, newMax;
1478 if (changeMin)
1479 apportion2 (cellMin, col, col + cs - 1, MIN, MAX, NULL, &newMin, 0);
1480 if (changeMax)
1481 apportion2 (cellMax, col, col + cs - 1, MIN, MAX, NULL, &newMax, 0);
1482
1483 for (int j = 0; j < cs; j++) {
1484 if (changeMin)
1485 setColExtreme (col + j, minExtrMod, extrData, newMin.get (j));
1486 if (changeMax)
1487 setColExtreme (col + j, maxExtrMod, extrData, newMax.get (j));
1488
1489 // For cases where min and max are somewhat confused:
1490 setColExtreme (col + j, maxExtrMod, extrData,
1491 misc::max (getColExtreme (col + j, minExtrMod,
1492 extrData),
1493 getColExtreme (col + j, maxExtrMod,
1494 extrData)));
1495 }
1496 }
1497
1498 DBG_OBJ_LEAVE ();
1499}
1500
1502 core::Extremes *cellExtremes)
1503{
1504 DBG_OBJ_ENTER ("resize", 0, "calcAdjustmentWidthSpanMultiCols",
1505 "%d, %d, ...", col, cs);
1506
1507 int sumAdjustmentWidth = 0;
1508 for (int j = 0; j < cs; j++)
1509 sumAdjustmentWidth += colExtremes->getRef(col + j)->adjustmentWidth;
1510
1511 if (cellExtremes->adjustmentWidth > sumAdjustmentWidth) {
1512 misc::SimpleVector<int> newAdjustmentWidth;
1513 apportion2 (cellExtremes->adjustmentWidth, col, col + cs - 1, MIN, MAX,
1514 NULL, &newAdjustmentWidth, 0);
1515 for (int j = 0; j < cs; j++)
1516 colExtremes->getRef(col + j)->adjustmentWidth =
1517 newAdjustmentWidth.get (j);
1518 }
1519
1520 DBG_OBJ_LEAVE ();
1521}
1522
1526void Table::apportion2 (int totalWidth, int firstCol, int lastCol,
1527 ExtrMod minExtrMod, ExtrMod maxExtrMod, void *extrData,
1528 misc::SimpleVector<int> *dest, int destOffset)
1529{
1530 DBG_OBJ_ENTER ("resize", 0, "apportion2", "%d, %d, %d, %s, %s, ..., %d",
1531 totalWidth, firstCol, lastCol, getExtrModName (minExtrMod),
1532 getExtrModName (maxExtrMod), destOffset);
1533
1534 if (lastCol >= firstCol) {
1535 dest->setSize (destOffset + lastCol - firstCol + 1, 0);
1536
1537 int totalMin = 0, totalMax = 0;
1538 for (int col = firstCol; col <= lastCol; col++) {
1539 totalMin += getColExtreme (col, minExtrMod, extrData);
1540 totalMax += getColExtreme (col, maxExtrMod, extrData);
1541 }
1542
1543 DBG_OBJ_MSGF ("resize", 1,
1544 "totalWidth = %d, totalMin = %d, totalMax = %d",
1545 totalWidth, totalMin, totalMax);
1546
1547 // The actual calculation is rather simple, the ith value is:
1548 //
1549 //
1550 // (max[i] - min[i]) * (totalMax - totalMin)
1551 // width[i] = min[i] + -----------------------------------------
1552 // (totalWidth - totalMin)
1553 //
1554 // (Regard "total" as "sum".) With the following general
1555 // definitions (for both the list and sums):
1556 //
1557 // diffExtr = max - min
1558 // diffWidth = width - min
1559 //
1560 // it is simplified to:
1561 //
1562 // diffExtr[i] * totalDiffWidth
1563 // diffWidth[i] = ----------------------------
1564 // totalDiffExtr
1565 //
1566 // Of course, if totalDiffExtr is 0, this is not defined;
1567 // instead, we apportion according to the minima:
1568 //
1569 // min[i] * totalWidth
1570 // width[i] = -------------------
1571 // totalMin
1572 //
1573 // Since min[i] <= max[i] for all i, totalMin == totalMax
1574 // implies that min[i] == max[i] for all i.
1575 //
1576 // Third, it totalMin == 0 (which also implies min[i] = max[i] = 0),
1577 // the result is
1578 //
1579 // width[i] = totalWidth / n
1580
1581 int totalDiffExtr = totalMax - totalMin;
1582 if (totalDiffExtr != 0) {
1583 // Normal case. The algorithm described in
1584 // "rounding-errors.doc" is used, with:
1585 //
1586 // x[i] = diffExtr[i]
1587 // y[i] = diffWidth[i]
1588 // a = totalDiffWidth
1589 // b = totalDiffExtr
1590
1591 DBG_OBJ_MSG ("resize", 1, "normal case");
1592
1593 int totalDiffWidth = totalWidth - totalMin;
1594 int cumDiffExtr = 0, cumDiffWidth = 0;
1595
1596 for (int col = firstCol; col <= lastCol; col++) {
1597 int min = getColExtreme (col, minExtrMod, extrData);
1598 int max = getColExtreme (col, maxExtrMod, extrData);
1599 int diffExtr = max - min;
1600
1601 cumDiffExtr += diffExtr;
1602 int diffWidth =
1603 (cumDiffExtr * totalDiffWidth) / totalDiffExtr - cumDiffWidth;
1604 cumDiffWidth += diffWidth;
1605
1606 dest->set (destOffset - firstCol + col, diffWidth + min);
1607 }
1608 } else if (totalMin != 0) {
1609 // Special case. Again, same algorithm, with
1610 //
1611 // x[i] = min[i]
1612 // y[i] = width[i]
1613 // a = totalWidth
1614 // b = totalMin
1615
1616 DBG_OBJ_MSG ("resize", 1, "special case 1");
1617
1618 int cumMin = 0, cumWidth = 0;
1619 for (int col = firstCol; col <= lastCol; col++) {
1620 int min = getColExtreme (col, minExtrMod, extrData);
1621 cumMin += min;
1622 int width = (cumMin * totalWidth) / totalMin - cumWidth;
1623 cumWidth += width;
1624
1625 dest->set (destOffset - firstCol + col, width);
1626 }
1627 } else { // if (totalMin == 0)
1628 // Last special case. Ssame algorithm, with
1629 //
1630 // x[i] = 1 (so cumX = i = col - firstCol + 1)
1631 // y[i] = width[i]
1632 // a = totalWidth
1633 // b = n = lastCol - firstCol + 1
1634
1635 DBG_OBJ_MSG ("resize", 1, "special case 2");
1636
1637 int cumWidth = 0, n = (lastCol - firstCol + 1);
1638 for (int col = firstCol; col <= lastCol; col++) {
1639 int i = (col - firstCol + 1);
1640 int width = (i * totalWidth) / n - cumWidth;
1641 cumWidth += width;
1642
1643 dest->set (destOffset - firstCol + col, width);
1644 }
1645 }
1646 }
1647
1648 DBG_OBJ_LEAVE ();
1649}
1650
1651} // namespace dw
#define _MSG(...)
Definition bookmarks.c:45
#define MSG(...)
Definition bookmarks.c:46
AlignedTableCell * getCellRef()
Definition table.cc:602
void drawLevel(core::View *view, core::Rectangle *area, int level, core::DrawingContext *context)
Definition table.cc:379
int getExtreme(core::Extremes *extremes, ExtrMod mod)
Definition table.cc:648
void setCumHeight(int row, int value)
Definition table.hh:458
~Table()
Definition table.cc:73
lout::misc::SimpleVector< core::style::Style * > * rowStyle
Definition table.hh:419
int calcAvailWidthForDescendant(Widget *child)
Definition table.cc:271
Table(bool limitTextWidth)
Definition table.cc:34
void calcAdjustmentWidthSpanMultiCols(int col, int cs, core::Extremes *cellExtremes)
Definition table.cc:1501
void apportionRowSpan()
Definition table.cc:1230
int getColExtreme(int col, ExtrMod mod, void *data)
Definition table.cc:701
lout::misc::SimpleVector< int > * baseline
Definition table.hh:417
void calcExtremesSpanMultiCols(int col, int cs, core::Extremes *cellExtremes, ExtrMod minExtrMod, ExtrMod maxExtrMod, void *extrData)
Definition table.cc:1450
void forceCalcColumnExtremes()
Fills dw::Table::colExtremes in all cases.
Definition table.cc:1295
void calcCellSizes(bool calcHeights)
Definition table.cc:824
void forceCalcCellSizes(bool calcHeights)
Definition table.cc:840
lout::misc::SimpleVector< int > * colWidths
The widths of all columns.
Definition table.hh:405
int applyPerWidth(int containerWidth, core::style::Length perWidth)
Definition table.cc:320
int getAvailWidthOfChild(Widget *child, bool forceValue)
Definition table.cc:225
void resizeDrawImpl()
Called after sizeAllocateImpl() to redraw necessary areas.
Definition table.cc:217
bool affectsSizeChangeContainerChild(Widget *child)
Definition table.cc:347
void addCell(Widget *widget, int colspan, int rowspan)
Definition table.cc:468
void getExtremesSimpl(core::Extremes *extremes)
Simple variant, to be implemented by widgets with extremes not depending on positions.
Definition table.cc:133
int numCols
Definition table.hh:370
int redrawX
Definition table.hh:373
@ MIN_INTR
Definition table.hh:423
@ MAX_MIN
Definition table.hh:423
@ MIN_MIN
Definition table.hh:423
@ MAX_INTR
Definition table.hh:423
int curRow
Definition table.hh:370
void containerSizeChangedForChildren()
Definition table.cc:330
void addRow(core::style::Style *style)
Definition table.cc:584
bool usesAvailWidth()
Must be implemengted by a method returning true, when getAvailWidth() is called.
Definition table.cc:369
bool isBlockLevel()
Definition table.cc:374
int numRows
Definition table.hh:370
lout::misc::SimpleVector< core::Extremes > * colExtremes
The extremes of all columns.
Definition table.hh:378
lout::misc::SimpleVector< int > * rowSpanCells
If a Cell has rowspan > 1, it goes into this array.
Definition table.hh:416
int curCol
Definition table.hh:370
lout::misc::SimpleVector< Child * > * children
Definition table.hh:371
void actuallyCalcCellSizes(bool calcHeights)
Definition table.cc:903
void sizeAllocateImpl(core::Allocation *allocation)
See Sizes of Dillo Widgets.
Definition table.cc:166
bool colWidthsUpToDateWidthColExtremes
Definition table.hh:421
bool limitTextWidth
Definition table.hh:368
void sizeRequestSimpl(core::Requisition *requisition)
Simple variant, to be implemented by widgets with sizes not depending on positions.
Definition table.cc:106
void removeChild(Widget *child)
Definition table.cc:458
int numColWidthPercentage
Definition table.hh:400
core::Iterator * iterator(core::Content::Type mask, bool atEnd)
Return an iterator for this widget.
Definition table.cc:463
void setColExtreme(int col, ExtrMod mod, void *data, int value)
Definition table.cc:712
static bool getAdjustTableMinWidth()
Definition table.hh:500
lout::misc::SimpleVector< int > * cumHeight
Row cumulative height array: cumHeight->size() is numRows + 1, cumHeight->get(0) is 0,...
Definition table.hh:412
void setExtreme(core::Extremes *extremes, ExtrMod mod, int value)
Definition table.cc:675
lout::misc::SimpleVector< bool > * colWidthPercentage
Wether the column itself (in the future?) or at least one cell in this column or spanning over this c...
Definition table.hh:399
void reallocChildren(int newNumCols, int newNumRows)
Definition table.cc:724
void apportion2(int totalWidth, int firstCol, int lastCol, ExtrMod minExtrMod, ExtrMod maxExtrMod, void *extrData, lout::misc::SimpleVector< int > *dest, int destOffset)
Actual apportionment function.
Definition table.cc:1526
bool rowClosed
Definition table.hh:368
bool childDefined(int n)
Definition table.hh:431
int applyPerHeight(int containerHeight, core::style::Length perHeight)
Definition table.cc:325
int numColWidthSpecified
Definition table.hh:389
Widget * getWidgetAtPointLevel(int x, int y, int level, core::GettingWidgetAtPointContext *context)
Definition table.cc:425
lout::misc::SimpleVector< bool > * colWidthSpecified
Wether the column itself (in the future?) or at least one cell in this column or spanning over this c...
Definition table.hh:388
static bool adjustTableMinWidth
Definition table.hh:366
const char * getExtrModName(ExtrMod mod)
Definition table.cc:618
static int CLASS_ID
Definition table.hh:495
int redrawY
Definition table.hh:373
Set at the top when drawing.
Definition types.hh:295
Set at the top when getting the widget at the point.
Definition types.hh:313
Iterators are used to iterate through the contents of a widget.
Definition iterator.hh:20
dw::core::Shape implemtation for simple rectangles.
Definition types.hh:70
void draw(core::View *view, core::style::Style *style, int x, int y)
Definition types.cc:41
static bool handledByStackingContextMgr(Widget *widget)
An interface to encapsulate platform dependent drawing.
Definition view.hh:17
The base class of all dillo widgets.
Definition widget.hh:44
Extremes extremes
Analogue to dw::core::Widget::requisition.
Definition widget.hh:166
Allocation allocation
The current allocation: size and position, always relative to the canvas.
Definition widget.hh:204
void setButtonSensitive(bool buttonSensitive)
Definition widget.cc:1568
int boxDiffWidth()
Definition widget.hh:495
int parentRef
This value is defined by the parent widget, and used for incremential resizing.
Definition widget.hh:195
int getAvailWidth(bool forceValue)
Return available width including margin/border/padding (extraSpace?), not only the content width.
Definition widget.cc:665
void correctRequisition(Requisition *requisition, void(*splitHeightFun)(int, int *, int *), bool allowDecreaseWidth, bool allowDecreaseHeight)
Definition widget.cc:820
style::Style * getStyle()
Definition widget.hh:482
bool resizeQueued()
Definition widget.hh:469
Requisition requisition
Size_request() stores the result of the last call of size_request_impl().
Definition widget.hh:160
void queueDrawArea(int x, int y, int width, int height)
Definition widget.cc:297
bool extremesQueued()
Definition widget.hh:470
bool extremesChanged()
Definition widget.hh:474
bool needsResize()
Definition widget.hh:471
style::Style * style
Definition widget.hh:150
void queueResize(int ref, bool extremesChanged, bool fast)
This method should be called, when a widget changes its size.
Definition widget.cc:321
int boxDiffHeight()
Definition widget.hh:500
void correctExtremes(Extremes *extremes, bool useAdjustmentWidth)
Definition widget.cc:870
void getExtremes(Extremes *extremes, int numPos=0, Widget **references=NULL, int *x=NULL, int *y=NULL)
Wrapper for Widget::getExtremesImpl().
Definition widget.cc:1093
void drawBox(View *view, style::Style *style, Rectangle *area, int x, int y, int width, int height, bool inverse)
Draw borders and background of a widget part, which allocation is given by (x, y, width,...
Definition widget.cc:1471
int getContentWidth()
Definition widget.hh:207
int getParentRefInFlowSubRef(int parentRef)
void sizeAllocateStart(core::Allocation *allocation)
bool isWidgetOOF(Widget *widget)
static const char * stackingLevelText(int level)
oof::OutOfFlowMgr * getWidgetOutOfFlowMgr(Widget *widget)
void correctRequisitionByOOF(core::Requisition *requisition, void(*splitHeightFun)(int, int *, int *))
void correctExtremesByOOF(core::Extremes *extremes)
int makeParentRefInFlow(int inFlowSubRef)
Represents additional data for OOF containers.
virtual int getAvailWidthOfChild(core::Widget *child, bool forceValue)=0
virtual bool dealingWithSizeOfChild(core::Widget *child)=0
bool instanceOf(int otherClassId)
Returns, whether this class is an instance of the class, given by otherClassId, or of a sub class of ...
Definition identity.cc:105
void registerName(const char *className, int *classId)
This method must be called in the constructor for the sub class.
Definition identity.cc:83
Simple (simpler than container::untyped::Vector and container::typed::Vector) template based vector.
Definition misc.hh:115
void setSize(int newSize)
Set the size explicitly.
Definition misc.hh:188
void increase()
Increase the vector size by one.
Definition misc.hh:181
void copyTo(SimpleVector< T > *dest, int thisStart=0, int thisLast=-1, int destStart=0)
Copies some elements into another vector of the same type.
Definition misc.hh:287
void set(int i, T t)
Store an object in the vector.
Definition misc.hh:267
T get(int i) const
Return the one element, explicitly.
Definition misc.hh:222
int size() const
Return the number of elements put into this vector.
Definition misc.hh:162
void setLast(T t)
Store an object at the end of the vector.
Definition misc.hh:275
#define DBG_IF_RTFL
Definition debug.hh:73
#define DBG_OBJ_ENTER0(aspect, prio, funname)
#define DBG_OBJ_ARRSET_BOOL(var, ind, val)
#define DBG_OBJ_DELETE()
#define DBG_OBJ_CREATE(klass)
#define DBG_OBJ_SET_BOOL(var, val)
#define DBG_OBJ_MSG_END()
#define DBG_OBJ_MSGF(aspect, prio, fmt,...)
#define DBG_OBJ_SET_NUM(var, val)
#define DBG_OBJ_MSG(aspect, prio, msg)
#define DBG_OBJ_ARRSET_NUM(var, ind, val)
#define DBG_OBJ_ENTER(aspect, prio, funname, fmt,...)
#define DBG_OBJ_LEAVE()
#define DBG_OBJ_MSG_START()
#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val)
#define DBG_OBJ_SET_NUM_O(obj, var, val)
#define MSG_WARN(...)
Definition msg.h:26
int multiplyWithPerLength(int x, Length l)
Multiply an int with a percentage length, returning int.
Definition style.hh:474
int Length
Type for representing all lengths within dw::core::style.
Definition style.hh:429
bool isPerLength(Length l)
Returns true if l is a percentage.
Definition style.hh:446
@ LENGTH_AUTO
Represents "auto" lengths.
Definition style.hh:495
void splitHeightPreserveDescent(int height, int *ascent, int *descent)
Definition widget.cc:2258
Dw is in this namespace, or sub namespaces of this one.
T min(T a, T b)
Definition misc.hh:40
T max(T a, T b)
Definition misc.hh:41
void assertNotReached()
Definition misc.hh:56
core::Widget * widget
Definition table.hh:329
struct SpanSpace spanSpace
Definition table.hh:344
struct Cell cell
Definition table.hh:343
enum dw::Table::Child::@22 type
Represents the allocation, i.e.
Definition types.hh:164