diff options
44 files changed, 3489 insertions, 2017 deletions
@@ -37,6 +37,7 @@ ^test/dw-links2$ ^test/dw-lists$ ^test/dw-resource-test$ +^test/dw-simple-container-test$ ^test/dw-table$ ^test/dw-table-aligned$ ^test/dw-ui-test$ @@ -599,8 +599,7 @@ EXCLUDE = dlib \ dpi \ dpid \ dpip \ - src \ - test + src # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded diff --git a/doc/dw-grows.doc b/doc/dw-grows.doc index 32800c19..ddaa926b 100644 --- a/doc/dw-grows.doc +++ b/doc/dw-grows.doc @@ -14,6 +14,124 @@ calculating widget sizes. Goals are: - *display: inline-block*; - <button>. -(...) -*/
\ No newline at end of file +A short sketch +============== + +**dw::core::Widget::sizeRequest and dw::core::Widget::getExtremes will +return final results.** The caller does not have to correct the size, +e. g. when percentages are defined. As an example, +dw::Textblock::calcWidgetSize has already become much simpler. + +**A new hierarchy, *container*:** Aside from dw::core::Widget::parent +and dw::core::Widget::generator, there is a third hierarchy +dw::core::Widget::container, which is (unlike *generator*) always a +direct ancestor, and represents what in CSS is called *containing +block*. Containers are important to define the "context size", which +is (not solely) used for percentage sizes. + +(There is another "containing block", dw::Textblock::containingBlock; +these may be consolidated some day.) + +**The process of size calculation is split between the widget itself +and its container:** + +- The container provides some abstract methods: + dw::core::Widget::getAvailWidthOfChild, + dw::core::Widget::getAvailHeightOfChild, + dw::core::Widget::correctRequisitionOfChild, and + dw::core::Widget::correctExtremesOfChild, which can be used in the + actual implementation of dw::core::Widget::sizeRequestImpl; + different containers with different ways how to arrange their + children will implement these methods in a different way. (Simple + example: the *available width* for children within a textblock is + the *available width* for the textblock itself, minus + margin/border/padding; on the other hand, it is completely different + for children of tables, for which a complex column width calculation + is used.) + +- The actual size calculation is, however, controlled by the widget + itself, which only *uses* these methods above. + +<div style="border: 2px solid #ffff00; margin-top: 0.5em; + margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0"> + <b>Update:</b> This is not fully correct; the parents are also involved + for calculating available widths and heights, at least when CSS 'width' + and 'height' are not set.</div> + +**Size hints are removed.** Instead, the container methods in the +previous paragraph are used. Changes of container sizes (especially +viewport the size) are handled in a different way. + +**Extremes are extended by intrinsic values.** In some cases (see +dw::Table::forceCalcCellSizes, case *minWidth* > *totalWidth*, for an +example) it is useful to know about minimal and maximal width of a +widget independent of CSS attributes. For this, dw::core::Extremes is +extended by: + +- dw::core::Extremes::minWidthIntrinsic and +- dw::core::Extremes::maxWidthIntrinsic. + +The rules for the calculation: + +1. If a widget has no children, it calculates *minWidthIntrinsic* and + *maxWidthIntrinsic* as those values not affected by CSS hints. + (dw::core::Widget::correctExtremes will not change these values.) +2. A widget must calculate *minWidthIntrinsic* and *maxWidthIntrinsic* + from *minWidthIntrinsic* and *maxWidthIntrinsic* of its children, + and *minWidth* and *maxWidth* from *minWidth* and *maxWidth* of its + children. +3. At the end, *minWidth* and *maxWidth* of a widget are corrected by + CSS attributes. (dw::core::Widget::correctExtremes will do this.) + +<div style="border: 2px solid #ffff00; margin-top: 0.5em; + margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0"> + <b>Notice:</b> Currently, dw::core::Widget::getExtremesImpl must + set all four members in dw::core::Extremes; this may change.</div> + +Open issues +=========== + +**Do CSS size dimensions override intrinsic sizes in all cases?** If a +textblock needs at least, say, 100 pixels width so that the text can +be read, but has a specification "width: 50px", should half of the +text be invisible? Or should the width be corrected again to 100 +pixels? + +Currently, in the CSS size specification is honoured in all cases, +with one exception: see dw::Textblock::sizeRequestImpl and see +dw::Textblock::getExtremesImpl (the time when +dw::core::Widget::correctRequisition and +dw::core::Widget::correctExtremes, respectively, is called). + +*Not* honouring the CSS size specification in all cases could improve +readability in some cases, so this could depend on a user preference. + +**Percentage values for margins and paddings, as well as negative +margins** are interesting applications, but have not been considered +yet. For negative margins, a new attribute +dw::core::Widget::extraSpace could solve the problem of widgets +sticking out of the allocation of parent. + +**Clarify percentage heights.** Search in widget.cc, and compare +section 10.5 ('height') of the CSS 2.1 specification to section 10.2 +('width'). + +**Fast queue resize does not work fully.** Example: run +*test/dw-simple-container-test* (dw_simple_container_test.cc), resize +(best maximize) the window and follow (e. g. by using RTFL) what +happens in consequence of dw::core::Layout::viewportSizeChanged. The +dw::SimpleContainer in the middle is not affected, so only the two +dw::Textblock's (at the top and at the bottom) call queueResize with +*fast = true*, and so get *NEEDS_RESIZE* set; but since it is not set +for the dw::SimpleContainer, *sizeRequest* is never called for the +bottom dw::Textblock. + +There does not seem to be a real case for this problem in dillo, since +all widgets which may contain other widgets (except +dw::SimpleContainer, which is not used outside tests) use the +available width and height (dw::core::Widget::usesAvailWidth and +dw::core::Widget::usesAvailHeight), and so are always affected by +viewport size changes. + +*/ diff --git a/doc/dw-widget-sizes.doc b/doc/dw-widget-sizes.doc index a9b6b00c..a82d3b99 100644 --- a/doc/dw-widget-sizes.doc +++ b/doc/dw-widget-sizes.doc @@ -1,5 +1,9 @@ /** \page dw-widget-sizes Sizes of Dillo Widgets +<div style="border: 2px solid #ff4040; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b> +Not up to date, see \ref dw-grows.</div> + Allocation ========== @@ -87,23 +91,9 @@ the image buffer when allocated at another size.) Size Hints ========== -Some widgets do not have an inherent size, but depend on the context, -e.g. the viewport size. These widgets should adhere to <i>size hints</i>, -i.e. implement the methods dw::core::Widget::setWidth, -dw::core::Widget::setAscent and dw::core::Widget::setDescent. The values -passed to the callees are - -- the viewport size (ascent is the heigt here, while descent is 0) for - the toplevel widget, and -- determined by the parent for its child widgets. - -Generally, the values should define the available space for the -widget. - -A widget, which depends on size hints, should call -dw::core::Widget::queueResize, when apropriate. - -\todo There should be a definition of "available space". +<div style="border: 2px solid #ff4040; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b> +Size hints have been removed, see \ref dw-grows.</div> Width Extremes diff --git a/dw/Makefile.am b/dw/Makefile.am index 5306c5a5..25d8bbce 100644 --- a/dw/Makefile.am +++ b/dw/Makefile.am @@ -57,6 +57,8 @@ libDw_fltk_a_SOURCES = \ libDw_fltk_a_CXXFLAGS = @LIBFLTK_CXXFLAGS@ libDw_widgets_a_SOURCES = \ + alignedtablecell.cc \ + alignedtablecell.hh \ alignedtextblock.cc \ alignedtextblock.hh \ bullet.cc \ @@ -71,10 +73,11 @@ libDw_widgets_a_SOURCES = \ outofflowmgr.hh \ ruler.cc \ ruler.hh \ + simpletablecell.cc \ + simpletablecell.hh \ table.cc \ + table_iterator.cc \ table.hh \ - tablecell.cc \ - tablecell.hh \ textblock.cc \ textblock_iterator.cc \ textblock_linebreaking.cc \ diff --git a/dw/tablecell.cc b/dw/alignedtablecell.cc index cbd5e0bf..a9f091f3 100644 --- a/dw/tablecell.cc +++ b/dw/alignedtablecell.cc @@ -19,19 +19,19 @@ -#include "tablecell.hh" +#include "alignedtablecell.hh" #include "../lout/debug.hh" #include <stdio.h> namespace dw { -int TableCell::CLASS_ID = -1; +int AlignedTableCell::CLASS_ID = -1; -TableCell::TableCell (TableCell *ref, bool limitTextWidth): +AlignedTableCell::AlignedTableCell (AlignedTableCell *ref, bool limitTextWidth): AlignedTextblock (limitTextWidth) { - DBG_OBJ_CREATE ("dw::TableCell"); - registerName ("dw::TableCell", &CLASS_ID); + DBG_OBJ_CREATE ("dw::AlignedTableCell"); + registerName ("dw::AlignedTableCell", &CLASS_ID); /** \bug ignoreLine1OffsetSometimes does not work? */ //ignoreLine1OffsetSometimes = true; @@ -40,12 +40,29 @@ TableCell::TableCell (TableCell *ref, bool limitTextWidth): setButtonSensitive(true); } -TableCell::~TableCell() +AlignedTableCell::~AlignedTableCell() { DBG_OBJ_DELETE (); } -int TableCell::wordWrap(int wordIndex, bool wrapAll) +bool AlignedTableCell::isBlockLevel () +{ + return false; +} + +int AlignedTableCell::applyPerWidth (int containerWidth, + core::style::Length perWidth) +{ + return core::style::multiplyWithPerLength (containerWidth, perWidth); +} + +int AlignedTableCell::applyPerHeight (int containerHeight, + core::style::Length perHeight) +{ + return core::style::multiplyWithPerLength (containerHeight, perHeight); +} + +int AlignedTableCell::wordWrap(int wordIndex, bool wrapAll) { Textblock::Word *word; const char *p; @@ -73,7 +90,7 @@ int TableCell::wordWrap(int wordIndex, bool wrapAll) return ret; } -int TableCell::getValue () +int AlignedTableCell::getValue () { Textblock::Word *word; int i, wordIndex; @@ -104,7 +121,7 @@ int TableCell::getValue () return w; } -void TableCell::setMaxValue (int maxValue, int value) +void AlignedTableCell::setMaxValue (int maxValue, int value) { line1Offset = maxValue - value; queueResize (OutOfFlowMgr::createRefNormalFlow (0), true); diff --git a/dw/alignedtablecell.hh b/dw/alignedtablecell.hh new file mode 100644 index 00000000..caf59307 --- /dev/null +++ b/dw/alignedtablecell.hh @@ -0,0 +1,34 @@ +#ifndef __DW_ALIGNEDTABLECELL_HH__ +#define __DW_ALIGNEDTABLECELL_HH__ + +#include "core.hh" +#include "alignedtextblock.hh" + +namespace dw { + +class AlignedTableCell: public AlignedTextblock +{ +private: + int charWordIndex, charWordPos; + +protected: + int wordWrap (int wordIndex, bool wrapAll); + + int getValue (); + void setMaxValue (int maxValue, int value); + +public: + static int CLASS_ID; + + AlignedTableCell(AlignedTableCell *ref, bool limitTextWidth); + ~AlignedTableCell(); + + int applyPerWidth (int containerWidth, core::style::Length perWidth); + int applyPerHeight (int containerHeight, core::style::Length perHeight); + + bool isBlockLevel (); +}; + +} // namespace dw + +#endif // __DW_ALIGNEDTABLECELL_HH__ diff --git a/dw/bullet.cc b/dw/bullet.cc index af7f5451..0ae8ccb7 100644 --- a/dw/bullet.cc +++ b/dw/bullet.cc @@ -27,6 +27,12 @@ namespace dw { Bullet::Bullet () { + DBG_OBJ_CREATE ("dw::Bullet"); +} + +Bullet::~Bullet () +{ + DBG_OBJ_DELETE (); } void Bullet::sizeRequestImpl (core::Requisition *requisition) @@ -36,6 +42,19 @@ void Bullet::sizeRequestImpl (core::Requisition *requisition) requisition->descent = 0; } +void Bullet::getExtremesImpl (core::Extremes *extremes) +{ + extremes->minWidth = extremes->maxWidth = + lout::misc::max (getStyle()->font->xHeight * 4 / 5, 1); +} + +void Bullet::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + // Nothing to do. + DBG_OBJ_LEAVE (); +} + void Bullet::draw (core::View *view, core::Rectangle *area) { int x, y, l; diff --git a/dw/bullet.hh b/dw/bullet.hh index 98854abb..004187cd 100644 --- a/dw/bullet.hh +++ b/dw/bullet.hh @@ -15,11 +15,14 @@ class Bullet: public core::Widget { protected: void sizeRequestImpl (core::Requisition *requisition); + void getExtremesImpl (core::Extremes *extremes); + void containerSizeChangedForChildren (); void draw (core::View *view, core::Rectangle *area); core::Iterator *iterator (core::Content::Type mask, bool atEnd); public: Bullet (); + ~Bullet (); }; } // namespace dw diff --git a/dw/fltkui.cc b/dw/fltkui.cc index bfc07528..29008055 100644 --- a/dw/fltkui.cc +++ b/dw/fltkui.cc @@ -213,12 +213,14 @@ void FltkResource::detachView (FltkView *view) void FltkResource::sizeAllocate (core::Allocation *allocation) { - DBG_OBJ_MSGF ("resize", 0, "<b>sizeAllocate</b> (%d, %d; %d * (%d + %d))", - allocation->x, allocation->y, allocation->width, - allocation->ascent, allocation->descent); + DBG_OBJ_ENTER ("resize", 0, "sizeAllocate", "%d, %d; %d * (%d + %d)", + allocation->x, allocation->y, allocation->width, + allocation->ascent, allocation->descent); this->allocation = *allocation; view->allocateFltkWidget (widget, allocation); + + DBG_OBJ_LEAVE (); } void FltkResource::draw (core::View *view, core::Rectangle *area) @@ -389,8 +391,7 @@ Fl_Widget *FltkLabelButtonResource::createNewWidget (core::Allocation void FltkLabelButtonResource::sizeRequest (core::Requisition *requisition) { - DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); if (style) { FltkFont *font = (FltkFont*)style->font; @@ -408,7 +409,7 @@ void FltkLabelButtonResource::sizeRequest (core::Requisition *requisition) DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /* @@ -674,8 +675,7 @@ void FltkEntryResource::setDisplayed(bool displayed) void FltkEntryResource::sizeRequest (core::Requisition *requisition) { - DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); if (displayed() && style) { FltkFont *font = (FltkFont*)style->font; @@ -696,7 +696,7 @@ void FltkEntryResource::sizeRequest (core::Requisition *requisition) DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void FltkEntryResource::sizeAllocate (core::Allocation *allocation) @@ -831,8 +831,7 @@ void FltkMultiLineTextResource::setWidgetStyle (Fl_Widget *widget, void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition) { - DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); if (style) { FltkFont *font = (FltkFont*)style->font; @@ -855,7 +854,7 @@ void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition) DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } const char *FltkMultiLineTextResource::getText () @@ -923,8 +922,7 @@ void FltkToggleButtonResource<I>::setWidgetStyle (Fl_Widget *widget, template <class I> void FltkToggleButtonResource<I>::sizeRequest (core::Requisition *requisition) { - DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); FltkFont *font = (FltkFont *) (this->FltkResource::style ? this->FltkResource::style->font : NULL); @@ -942,7 +940,7 @@ void FltkToggleButtonResource<I>::sizeRequest (core::Requisition *requisition) DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } @@ -1182,8 +1180,7 @@ int FltkOptionMenuResource::getMaxItemWidth() void FltkOptionMenuResource::sizeRequest (core::Requisition *requisition) { - DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); if (style) { FltkFont *font = (FltkFont*)style->font; @@ -1202,7 +1199,7 @@ void FltkOptionMenuResource::sizeRequest (core::Requisition *requisition) DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void FltkOptionMenuResource::enlargeMenu () @@ -1478,8 +1475,7 @@ int FltkListResource::getMaxItemWidth() void FltkListResource::sizeRequest (core::Requisition *requisition) { - DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); if (style) { CustBrowser *b = (CustBrowser *) widget; @@ -1504,7 +1500,7 @@ void FltkListResource::sizeRequest (core::Requisition *requisition) DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } int FltkListResource::getNumberOfItems() diff --git a/dw/image.cc b/dw/image.cc index e71c8f2f..9dc6d6ee 100644 --- a/dw/image.cc +++ b/dw/image.cc @@ -170,45 +170,56 @@ Image::~Image() void Image::sizeRequestImpl (core::Requisition *requisition) { + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl"); + if (buffer) { - if (getStyle ()->height == core::style::LENGTH_AUTO && - core::style::isAbsLength (getStyle ()->width) && - buffer->getRootWidth () > 0) { - // preserve aspect ratio when only width is given - requisition->width = core::style::absLengthVal (getStyle ()->width); - requisition->ascent = buffer->getRootHeight () * - requisition->width / buffer->getRootWidth (); - } else if (getStyle ()->width == core::style::LENGTH_AUTO && - core::style::isAbsLength (getStyle ()->height) && - buffer->getRootHeight () > 0) { - // preserve aspect ratio when only height is given - requisition->ascent = core::style::absLengthVal (getStyle ()->height); - requisition->width = buffer->getRootWidth () * - requisition->ascent / buffer->getRootHeight (); - } else { - requisition->width = buffer->getRootWidth (); - requisition->ascent = buffer->getRootHeight (); - } - requisition->descent = 0; - } else { - if (altText && altText[0]) { - if (altTextWidth == -1) - altTextWidth = - layout->textWidth (getStyle()->font, altText, strlen (altText)); + requisition->width = buffer->getRootWidth (); + requisition->ascent = buffer->getRootHeight (); + } else + requisition->width = requisition->ascent = 0; + + requisition->width += boxDiffWidth (); + requisition->ascent += boxOffsetY (); + + requisition->descent = boxRestHeight (); - requisition->width = altTextWidth; - requisition->ascent = getStyle()->font->ascent; - requisition->descent = getStyle()->font->descent; - } else { - requisition->width = 0; - requisition->ascent = 0; - requisition->descent = 0; + correctRequisition (requisition, core::splitHeightPreserveDescent); + + if (buffer) { + // If one dimension is set, preserve the aspect ratio (without + // extraSpace/margin/border/padding). Notice that + // requisition->descent could have been changed in + // core::splitHeightPreserveDescent, so we do not make any + // assumtions here about it (and requisition->ascent). + + // TODO Check again possible overflows. (Aren't buffer + // dimensions limited to 2^15?) + + if (getStyle()->width == core::style::LENGTH_AUTO && + getStyle()->height != core::style::LENGTH_AUTO) { + requisition->width = + (requisition->ascent + requisition->descent - boxDiffHeight ()) + * buffer->getRootWidth () / buffer->getRootHeight () + + boxDiffWidth (); + } else if (getStyle()->width != core::style::LENGTH_AUTO && + getStyle()->height == core::style::LENGTH_AUTO) { + requisition->ascent = (requisition->width + boxDiffWidth ()) + * buffer->getRootHeight () / buffer->getRootWidth () + + boxOffsetY (); + requisition->descent = boxRestHeight (); } - } + } - requisition->width += getStyle()->boxDiffWidth (); - requisition->ascent += getStyle()->boxOffsetY (); - requisition->descent += getStyle()->boxRestHeight (); + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Image::getExtremesImpl (core::Extremes *extremes) +{ + extremes->minWidth = extremes->maxWidth = + (buffer ? buffer->getRootWidth () : 0) + boxDiffWidth (); + correctExtremes (extremes); } void Image::sizeAllocateImpl (core::Allocation *allocation) @@ -247,6 +258,13 @@ void Image::sizeAllocateImpl (core::Allocation *allocation) } } +void Image::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + // Nothing to do. + DBG_OBJ_LEAVE (); +} + void Image::enterNotifyImpl (core::EventCrossing *event) { // BUG: this is wrong for image maps, but the cursor position is unknown. diff --git a/dw/image.hh b/dw/image.hh index a712936e..71ed85b9 100644 --- a/dw/image.hh +++ b/dw/image.hh @@ -130,7 +130,9 @@ private: protected: void sizeRequestImpl (core::Requisition *requisition); + void getExtremesImpl (core::Extremes *extremes); void sizeAllocateImpl (core::Allocation *allocation); + void containerSizeChangedForChildren (); void draw (core::View *view, core::Rectangle *area); diff --git a/dw/layout.cc b/dw/layout.cc index 1609dae0..82a21f57 100644 --- a/dw/layout.cc +++ b/dw/layout.cc @@ -277,6 +277,11 @@ Layout::Layout (Platform *platform) viewportWidth = viewportHeight = 0; hScrollbarThickness = vScrollbarThickness = 0; + DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth); + DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight); + DBG_OBJ_SET_NUM ("hScrollbarThickness", hScrollbarThickness); + DBG_OBJ_SET_NUM ("vScrollbarThickness", vScrollbarThickness); + requestedAnchor = NULL; scrollIdleId = -1; scrollIdleNotInterrupted = false; @@ -371,13 +376,17 @@ void Layout::addWidget (Widget *widget) topLevel = widget; widget->layout = this; + widget->container = NULL; + DBG_OBJ_SET_PTR_O (widget, "container", widget->container); + queueResizeList->clear (); - widget->notifySetAsTopLevel(); + widget->notifySetAsTopLevel (); findtextState.setWidget (widget); canvasHeightGreater = false; - setSizeHints (); + DBG_OBJ_SET_SYM ("canvasHeightGreater", + canvasHeightGreater ? "true" : "false"); // Do not directly call Layout::queueResize(), but // Widget::queueResize(), so that all flags are set properly, @@ -471,6 +480,11 @@ void Layout::attachView (View *view) hScrollbarThickness = view->getHScrollbarThickness (); vScrollbarThickness = view->getVScrollbarThickness (); } + + DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth); + DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight); + DBG_OBJ_SET_NUM ("hScrollbarThickness", hScrollbarThickness); + DBG_OBJ_SET_NUM ("vScrollbarThickness", vScrollbarThickness); } /* @@ -834,8 +848,7 @@ void Layout::setBgImage (style::StyleImage *bgImage, void Layout::resizeIdle () { - DBG_OBJ_MSG ("resize", 0, "<b>resizeIdle</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "resizeIdle"); enterResizeIdle (); @@ -873,7 +886,10 @@ void Layout::resizeIdle () // called again. resizeIdleId = -1; - if (topLevel) { + // If this method is triggered by a viewport change, we can save + // time when the toplevel widget is not affected (as for a toplevel + // image resource). + if (topLevel && (topLevel->needsResize () || topLevel->needsAllocate ())) { Requisition requisition; Allocation allocation; @@ -882,9 +898,11 @@ void Layout::resizeIdle () requisition.width, requisition.ascent, requisition.descent); // This method is triggered by Widget::queueResize, which will, - // in any case, set NEEDS_ALLOCATE (indirectly, as - // ALLOCATE_QUEUED). This assertion helps to find - // inconsistences. + // in any case, set NEEDS_ALLOCATE (indirectly, as ALLOCATE_QUEUED). + // This assertion helps to find inconsistences. (Cases where + // this method is triggered by a viewport change, but the + // toplevel widget is not affected, are filtered out some lines + // above: "if (topLevel && topLevel->needsResize ())".) assert (topLevel->needsAllocate ()); allocation.x = allocation.y = 0; @@ -908,12 +926,12 @@ void Layout::resizeIdle () int currVThickness = currVScrollbarThickness(); if (!canvasHeightGreater && - canvasAscent + canvasDescent - > viewportHeight - currHThickness) { + canvasAscent + canvasDescent > viewportHeight - currHThickness) { canvasHeightGreater = true; - setSizeHints (); - /* May queue a new resize. */ - } + DBG_OBJ_SET_SYM ("canvasHeightGreater", + canvasHeightGreater ? "true" : "false"); + containerSizeChanged (); + } // Set viewport sizes. view->setViewportSize (viewportWidth, viewportHeight, @@ -927,21 +945,11 @@ void Layout::resizeIdle () DBG_OBJ_MSGF ("resize", 1, "after resizeIdle: resizeIdleId = %d", resizeIdleId); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); leaveResizeIdle (); } -void Layout::setSizeHints () -{ - if (topLevel) { - topLevel->setWidth (viewportWidth - - (canvasHeightGreater ? vScrollbarThickness : 0)); - topLevel->setAscent (viewportHeight - hScrollbarThickness); - topLevel->setDescent (0); - } -} - void Layout::queueDraw (int x, int y, int width, int height) { Rectangle area; @@ -980,9 +988,8 @@ void Layout::queueDrawExcept (int x, int y, int width, int height, void Layout::queueResize (bool extremesChanged) { - DBG_OBJ_MSGF ("resize", 0, "<b>queueResize</b> (%s)", - extremesChanged ? "true" : "false"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 0, "queueResize", "%s", + extremesChanged ? "true" : "false"); if (resizeIdleId == -1) { view->cancelQueueDraw (); @@ -993,7 +1000,7 @@ void Layout::queueResize (bool extremesChanged) emitter.emitResizeQueued (extremesChanged); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } @@ -1264,13 +1271,16 @@ void Layout::scrollPosChanged (View *view, int x, int y) */ void Layout::viewportSizeChanged (View *view, int width, int height) { - _MSG("Layout::viewportSizeChanged w=%d h=%d new_w=%d new_h=%d\n", - viewportWidth, viewportHeight, width, height); + DBG_OBJ_ENTER ("resize", 0, "viewportSizeChanged", "%p, %d, %d", + view, width, height); /* If the width has become higher, we test again, whether the vertical * scrollbar (so to speak) can be hidden again. */ - if (usesViewport && width > viewportWidth) + if (usesViewport && width > viewportWidth) { canvasHeightGreater = false; + DBG_OBJ_SET_SYM ("canvasHeightGreater", + canvasHeightGreater ? "true" : "false"); + } /* if size changes, redraw this view. * TODO: this is a resize call (redraw/resize code needs a review). */ @@ -1285,7 +1295,24 @@ void Layout::viewportSizeChanged (View *view, int width, int height) viewportWidth = width; viewportHeight = height; - setSizeHints (); + DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth); + DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight); + + containerSizeChanged (); + + DBG_OBJ_LEAVE (); +} + +void Layout::containerSizeChanged () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChanged"); + + if (topLevel) { + topLevel->containerSizeChanged (); + queueResize (true); + } + + DBG_OBJ_LEAVE (); } } // namespace core diff --git a/dw/layout.hh b/dw/layout.hh index 2120d877..88532187 100644 --- a/dw/layout.hh +++ b/dw/layout.hh @@ -157,13 +157,15 @@ private: public: Widget *widget; int ref; - bool extremesChanged; + bool extremesChanged, fast; - inline QueueResizeItem (Widget *widget, int ref, bool extremesChanged) + inline QueueResizeItem (Widget *widget, int ref, bool extremesChanged, + bool fast) { this->widget = widget; this->ref = ref; this->extremesChanged = extremesChanged; + this->fast = fast; } }; @@ -328,6 +330,8 @@ public: return buttonEvent (BUTTON_PRESS, view, numPressed, x, y, state, button); } + void containerSizeChanged (); + /** * \brief This function is called by a view, to delegate a button press * event. diff --git a/dw/listitem.cc b/dw/listitem.cc index 05344d79..65293d8d 100644 --- a/dw/listitem.cc +++ b/dw/listitem.cc @@ -69,7 +69,7 @@ int ListItem::getValue () void ListItem::setMaxValue (int maxValue, int value) { - innerPadding = maxValue; + leftInnerPadding = maxValue; line1Offset = - value; redrawY = 0; queueResize (0, true); diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc index eb5bf3e8..62386b15 100644 --- a/dw/outofflowmgr.cc +++ b/dw/outofflowmgr.cc @@ -41,9 +41,8 @@ OutOfFlowMgr::WidgetInfo::WidgetInfo (OutOfFlowMgr *oofm, Widget *widget) void OutOfFlowMgr::WidgetInfo::update (bool wasAllocated, int xCB, int yCB, int width, int height) { - DBG_OBJ_MSGF_O ("resize.oofm", 0, widget, - "<b>update</b> (%s, %d, %d, %d, %d)", - wasAllocated ? "true" : "false", xCB, yCB, width, height); + DBG_OBJ_ENTER_O ("resize.oofm", 0, widget, "update", "%s, %d, %d, %d, %d", + wasAllocated ? "true" : "false", xCB, yCB, width, height); this->wasAllocated = wasAllocated; this->xCB = xCB; @@ -55,6 +54,8 @@ void OutOfFlowMgr::WidgetInfo::update (bool wasAllocated, int xCB, int yCB, DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.yCB", yCB); DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.width", width); DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.height", height); + + DBG_OBJ_LEAVE_O (widget); } // ---------------------------------------------------------------------- @@ -80,18 +81,20 @@ OutOfFlowMgr::Float::Float (OutOfFlowMgr *oofm, Widget *widget, DBG_OBJ_SET_NUM_O (widget, "<Float>.size.width", size.width); DBG_OBJ_SET_NUM_O (widget, "<Float>.size.ascent", size.ascent); DBG_OBJ_SET_NUM_O (widget, "<Float>.size.descent", size.descent); + DBG_OBJ_SET_BOOL_O (widget, "<Float>.dirty", dirty); + DBG_OBJ_SET_BOOL_O (widget, "<Float>.sizeChangedSinceLastAllocation", + sizeChangedSinceLastAllocation); } } void OutOfFlowMgr::Float::updateAllocation () { - DBG_OBJ_MSG_O ("resize.oofm", 0, getWidget (), "<b>updateAllocation</b> ()"); - DBG_OBJ_MSG_START_O (getWidget ()); + DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation"); update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), getNewHeight ()); - DBG_OBJ_MSG_END_O (getWidget ()); + DBG_OBJ_LEAVE_O (getWidget ()); } void OutOfFlowMgr::Float::intoStringBuffer(StringBuffer *sb) @@ -132,10 +135,9 @@ void OutOfFlowMgr::Float::intoStringBuffer(StringBuffer *sb) bool OutOfFlowMgr::Float::covers (Textblock *textblock, int y, int h) { - DBG_OBJ_MSGF_O ("border", 0, getOutOfFlowMgr (), - "<b>covers</b> (%p, %d, %d) [vloat: %p]", - textblock, y, h, getWidget ()); - DBG_OBJ_MSG_START_O (getOutOfFlowMgr ()); + DBG_OBJ_ENTER_O ("border", 0, getOutOfFlowMgr (), "covers", + "%p, %d, %d [vloat: %p]", + textblock, y, h, getWidget ()); bool b; @@ -176,7 +178,7 @@ bool OutOfFlowMgr::Float::covers (Textblock *textblock, int y, int h) } } - DBG_OBJ_MSG_END_O (getOutOfFlowMgr ()); + DBG_OBJ_LEAVE_O (getOutOfFlowMgr ()); return b; } @@ -186,10 +188,9 @@ int OutOfFlowMgr::Float::ComparePosition::compare (Object *o1, Object *o2) Float *fl1 = (Float*)o1, *fl2 = (Float*)o2; int r; - DBG_OBJ_MSGF_O ("border", 1, oofm, - "<b>ComparePosition::compare</b> (#%d, #%d) [refTB = %p]", - fl1->getIndex (type), fl2->getIndex (type), refTB); - DBG_OBJ_MSG_START_O (oofm); + DBG_OBJ_ENTER_O ("border", 1, oofm, + "ComparePosition/compare", "(#%d, #%d) [refTB = %p]", + fl1->getIndex (type), fl2->getIndex (type), refTB); if (refTB == fl1->generatingBlock && refTB == fl2->generatingBlock) { DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is generating both floats"); @@ -237,7 +238,7 @@ int OutOfFlowMgr::Float::ComparePosition::compare (Object *o1, Object *o2) } DBG_OBJ_MSGF_O ("border", 1, oofm, "result: %d", r); - DBG_OBJ_MSG_END_O (oofm); + DBG_OBJ_LEAVE_O (oofm); return r; } @@ -252,12 +253,11 @@ int OutOfFlowMgr::Float::CompareGBAndExtIndex::compare (Object *o1, Object *o2) Float *f1 = (Float*)o1, *f2 = (Float*)o2; int r = -123; // Compiler happiness: GCC 4.7 does not handle this?; - DBG_OBJ_MSGF_O ("border", 1, oofm, - "<b>CompareGBAndExtIndex::compare</b> (#%d -> %p/%d, " - "#%d -> %p/#%d)", - f1->getIndex (type), f1->generatingBlock, f1->externalIndex, - f2->getIndex (type), f2->generatingBlock, f2->externalIndex); - DBG_OBJ_MSG_START_O (oofm); + DBG_OBJ_ENTER_O ("border", 1, oofm, "CompareGBAndExtIndex/compare", + "#%d -> %p/%d, #%d -> %p/#%d", + f1->getIndex (type), f1->generatingBlock, f1->externalIndex, + f2->getIndex (type), f2->generatingBlock, + f2->externalIndex); if (f1->generatingBlock == f2->generatingBlock) { r = f1->externalIndex - f2->externalIndex; @@ -301,16 +301,15 @@ int OutOfFlowMgr::Float::CompareGBAndExtIndex::compare (Object *o1, Object *o2) } DBG_OBJ_MSGF_O ("border", 2, oofm, "result: %d", r); - DBG_OBJ_MSG_END_O (oofm); + DBG_OBJ_LEAVE_O (oofm); return r; } int OutOfFlowMgr::SortedFloatsVector::findFloatIndex (Textblock *lastGB, int lastExtIndex) { - DBG_OBJ_MSGF_O ("border", 0, oofm, "<b>findFloatIndex</b> (%p, %d)", - lastGB, lastExtIndex); - DBG_OBJ_MSG_START_O (oofm); + DBG_OBJ_ENTER_O ("border", 0, oofm, "findFloatIndex", "%p, %d", + lastGB, lastExtIndex); Float key (oofm, NULL, lastGB, lastExtIndex); key.setIndex (type, -1); // for debugging @@ -345,16 +344,15 @@ int OutOfFlowMgr::SortedFloatsVector::findFloatIndex (Textblock *lastGB, //} DBG_OBJ_MSGF_O ("border", 1, oofm, "=> r = %d", r); - DBG_OBJ_MSG_END_O (oofm); + DBG_OBJ_LEAVE_O (oofm); return r; } int OutOfFlowMgr::SortedFloatsVector::find (Textblock *textblock, int y, int start, int end) { - DBG_OBJ_MSGF_O ("border", 0, oofm, "<b>find</b> (%p, %d, %d, %d)", - textblock, y, start, end); - DBG_OBJ_MSG_START_O (oofm); + DBG_OBJ_ENTER_O ("border", 0, oofm, "find", "%p, %d, %d, %d", + textblock, y, start, end); Float key (oofm, NULL, NULL, 0); key.generatingBlock = textblock; @@ -364,7 +362,7 @@ int OutOfFlowMgr::SortedFloatsVector::find (Textblock *textblock, int y, int result = bsearch (&key, false, start, end, &comparator); DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); - DBG_OBJ_MSG_END_O (oofm); + DBG_OBJ_LEAVE_O (oofm); return result; } @@ -373,9 +371,8 @@ int OutOfFlowMgr::SortedFloatsVector::findFirst (Textblock *textblock, Textblock *lastGB, int lastExtIndex) { - DBG_OBJ_MSGF_O ("border", 0, oofm, "<b>findFirst</b> (%p, %d, %d, %p, %d)", - textblock, y, h, lastGB, lastExtIndex); - DBG_OBJ_MSG_START_O (oofm); + DBG_OBJ_ENTER_O ("border", 0, oofm, "findFirst", "%p, %d, %d, %p, %d", + textblock, y, h, lastGB, lastExtIndex); DBG_IF_RTFL { DBG_OBJ_MSG_O ("border", 2, oofm, "searching in list:"); @@ -420,7 +417,7 @@ int OutOfFlowMgr::SortedFloatsVector::findFirst (Textblock *textblock, result = -1; DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); - DBG_OBJ_MSG_END_O (oofm); + DBG_OBJ_LEAVE_O (oofm); return result; } @@ -461,13 +458,12 @@ OutOfFlowMgr::TBInfo::~TBInfo () void OutOfFlowMgr::TBInfo::updateAllocation () { - DBG_OBJ_MSG_O ("resize.oofm", 0, getWidget (), "<b>updateAllocation</b> ()"); - DBG_OBJ_MSG_START_O (getWidget ()); + DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation"); update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), getNewHeight ()); - DBG_OBJ_MSG_END_O (getWidget ()); + DBG_OBJ_LEAVE_O (getWidget ()); } OutOfFlowMgr::AbsolutelyPositioned::AbsolutelyPositioned (OutOfFlowMgr *oofm, @@ -489,6 +485,9 @@ OutOfFlowMgr::OutOfFlowMgr (Textblock *containingBlock) leftFloatsCB = new SortedFloatsVector (this, LEFT, CB); rightFloatsCB = new SortedFloatsVector (this, RIGHT, CB); + DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size()); + DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size()); + leftFloatsAll = new Vector<Float> (1, true); rightFloatsAll = new Vector<Float> (1, true); @@ -539,11 +538,10 @@ OutOfFlowMgr::~OutOfFlowMgr () void OutOfFlowMgr::sizeAllocateStart (Textblock *caller, Allocation *allocation) { - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>sizeAllocateStart</b> (%p, (%d, %d, %d * (%d + %d)))", - caller, allocation->x, allocation->y, allocation->width, - allocation->ascent, allocation->descent); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateStart", + "%p, (%d, %d, %d * (%d + %d))", + caller, allocation->x, allocation->y, allocation->width, + allocation->ascent, allocation->descent); getTextblock(caller)->allocation = *allocation; getTextblock(caller)->wasAllocated = true; @@ -566,13 +564,12 @@ void OutOfFlowMgr::sizeAllocateStart (Textblock *caller, Allocation *allocation) lastAllocatedLeftFloat = lastAllocatedRightFloat = -1; } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void OutOfFlowMgr::sizeAllocateEnd (Textblock *caller) { - DBG_OBJ_MSGF ("resize.oofm", 0, "<b>sizeAllocateEnd</b> (%p)", caller); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateEnd", "%p", caller); // (Later, absolutely positioned blocks have to be allocated.) @@ -616,7 +613,7 @@ void OutOfFlowMgr::sizeAllocateEnd (Textblock *caller) Textblock *tb = key->getTypedValue(); tbInfo->updateAllocation (); - tbInfo->availWidth = tb->getAvailWidth (); + tbInfo->lineBreakWidth = tb->getLineBreakWidth (); } // There are cases where some allocated floats (TODO: later also @@ -638,16 +635,33 @@ void OutOfFlowMgr::sizeAllocateEnd (Textblock *caller) containingBlock->oofSizeChanged (extremesChanged); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); +} + +void OutOfFlowMgr::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + DBG_OBJ_MSGF ("resize", 0, + "%d left floats, %d right floats %d abspos", + leftFloatsAll->size (), rightFloatsAll->size (), + absolutelyPositioned->size()); + + for (int i = 0; i < leftFloatsAll->size (); i++) + leftFloatsAll->get(i)->getWidget()->containerSizeChanged (); + for (int i = 0; i < rightFloatsAll->size (); i++) + rightFloatsAll->get(i)->getWidget()->containerSizeChanged (); + for (int i = 0; i < absolutelyPositioned->size(); i++) + absolutelyPositioned->get(i)->widget->containerSizeChanged (); + + DBG_OBJ_LEAVE (); } bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, int *minFloatPos, Widget **minFloat) { - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>hasRelationChanged</b> (<i>widget:</i> %p, ...)", - tbInfo->getWidget ()); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", + "<i>widget:</i> %p, ...", tbInfo->getWidget ()); int leftMinPos, rightMinPos; Widget *leftMinFloat, *rightMinFloat; @@ -680,17 +694,16 @@ bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, int *minFloatPos, else DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return c1 || c2; } bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, Side side, int *minFloatPos, Widget **minFloat) { - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>hasRelationChanged</b> (<i>widget:</i> %p, %s, ...)", - tbInfo->getWidget (), side == LEFT ? "LEFT" : "RIGHT"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", + "<i>widget:</i> %p, %s, ...", + tbInfo->getWidget (), side == LEFT ? "LEFT" : "RIGHT"); SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; bool changed = false; @@ -709,8 +722,8 @@ bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, Side side, int newFlx = calcFloatX (vloat, side, - gba->x - containingBlockAllocation.x, - gba->width, vloat->generatingBlock->getAvailWidth ()); + gba->x - containingBlockAllocation.x, gba->width, + vloat->generatingBlock->getLineBreakWidth ()); int newFly = vloat->generatingBlock->getAllocation()->y - containingBlockAllocation.y + vloat->yReal; @@ -758,7 +771,7 @@ bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, Side side, else DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return changed; } @@ -778,10 +791,8 @@ bool OutOfFlowMgr::hasRelationChanged (bool oldTBAlloc, int newFlw, int newFlh, Side side, int *floatPos) { - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>hasRelationChanged</b> (<i>see below</i>, %s, ...)", - side == LEFT ? "LEFT" : "RIGHT"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", + "<i>see below</i>, %s, ...", side == LEFT ? "LEFT" : "RIGHT"); if (oldTBAlloc) DBG_OBJ_MSGF ("resize.oofm", 1, "old TB: %d, %d; %d * %d", @@ -884,16 +895,23 @@ bool OutOfFlowMgr::hasRelationChanged (bool oldTBAlloc, else DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return result; } bool OutOfFlowMgr::doFloatsExceedCB (Side side) { - DBG_OBJ_MSGF ("resize.oofm", 0, "<b>doFloatsExceedCB</b> (%s)", - side == LEFT ? "LEFT" : "RIGHT"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "doFloatsExceedCB", "%s", + side == LEFT ? "LEFT" : "RIGHT"); + + // This method is called to determine whether the *requisition* of + // the CB must be recalculated. So, we check the float allocations + // against the *requisition* of the CB, which may (e. g. within + // tables) differ from the new allocation. (Generally, a widget may + // allocated at a different size.) + core::Requisition cbReq; + containingBlock->sizeRequest (&cbReq); SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; bool exceeds = false; @@ -909,14 +927,10 @@ bool OutOfFlowMgr::doFloatsExceedCB (Side side) "(%d, %d, %d * %d)?", fla->x, fla->y, fla->width, fla->ascent + fla->descent, containingBlockAllocation.x, containingBlockAllocation.y, - containingBlockAllocation.width, - containingBlockAllocation.ascent - + containingBlockAllocation.descent); - if (fla->x + fla->width > - containingBlockAllocation.x + containingBlockAllocation.width || - fla->y + fla->ascent + fla->descent > - containingBlockAllocation.y + containingBlockAllocation.ascent - + containingBlockAllocation.descent) { + cbReq.width, cbReq.ascent + cbReq.descent); + if (fla->x + fla->width > containingBlockAllocation.x + cbReq.width || + fla->y + fla->ascent + fla->descent + > containingBlockAllocation.y + cbReq.ascent + cbReq.descent) { exceeds = true; DBG_OBJ_MSG ("resize.oofm", 2, "Yes."); } else @@ -927,16 +941,15 @@ bool OutOfFlowMgr::doFloatsExceedCB (Side side) DBG_OBJ_MSG_END (); DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", exceeds ? "true" : "false"); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return exceeds; } bool OutOfFlowMgr::haveExtremesChanged (Side side) { - DBG_OBJ_MSGF ("resize.oofm", 0, "<b>haveExtremesChanged</b> (%s)", - side == LEFT ? "LEFT" : "RIGHT"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "haveExtremesChanged", "%s", + side == LEFT ? "LEFT" : "RIGHT"); // This is quite different from doFloatsExceedCB, since there is no // counterpart to getExtremes, as sizeAllocate is a counterpart to @@ -986,13 +999,16 @@ bool OutOfFlowMgr::haveExtremesChanged (Side side) } DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", changed ? "true" : "false"); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return changed; } void OutOfFlowMgr::moveFromGBToCB (Side side) { + DBG_OBJ_ENTER ("oofm.resize", 0, "moveFromGBToCB", "%s", + side == LEFT ? "LEFT" : "RIGHT"); + SortedFloatsVector *dest = side == LEFT ? leftFloatsCB : rightFloatsCB; int *floatsMark = side == LEFT ? &leftFloatsMark : &rightFloatsMark; @@ -1007,20 +1023,24 @@ void OutOfFlowMgr::moveFromGBToCB (Side side) // "vloat->indexCBList == -1": prevent copying the vloat twice. if (vloat->indexCBList == -1 && vloat->mark == mark) { dest->put (vloat); - //printf("[%p] moving %s float %p (%s %p, mark %d) to CB list\n", - // containingBlock, side == LEFT ? "left" : "right", - // vloat, vloat->widget->getClassName(), vloat->widget, - // vloat->mark); + DBG_OBJ_MSGF ("oofm.resize", 1, + "moving float %p (mark %d) to CB list\n", + vloat->getWidget (), vloat->mark); + DBG_OBJ_SET_NUM (side == LEFT ? + "leftFloatsCB.size" : "rightFloatsCB.size", + dest->size()); + DBG_OBJ_ARRATTRSET_PTR (side == LEFT ? + "leftFloatsCB" : "rightFloatsCB", + dest->size() - 1, "widget", + vloat->getWidget ()); + } } } *floatsMark = 0; - //printf ("[%p] new %s list:\n", - // containingBlock, side == LEFT ? "left" : "right"); - //for (int i = 0; i < dest->size(); i++) - // printf (" %d: %s\n", i, dest->get(i)->toString()); + DBG_OBJ_LEAVE (); } void OutOfFlowMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) @@ -1029,11 +1049,10 @@ void OutOfFlowMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) int *lastAllocatedFloat = side == LEFT ? &lastAllocatedLeftFloat : &lastAllocatedRightFloat; - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>sizeAllocateFloats</b> (%s, [%d ->] %d [size = %d])", - side == LEFT ? "LEFT" : "RIGHT", *lastAllocatedFloat, - newLastAllocatedFloat, list->size ()); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateFloats", + "%s, [%d ->] %d [size = %d]", + side == LEFT ? "LEFT" : "RIGHT", *lastAllocatedFloat, + newLastAllocatedFloat, list->size ()); Allocation *cba = &containingBlockAllocation; @@ -1042,11 +1061,11 @@ void OutOfFlowMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) ensureFloatSize (vloat); Allocation *gba = getAllocation (vloat->generatingBlock); - int availWidth = vloat->generatingBlock->getAvailWidth(); + int lineBreakWidth = vloat->generatingBlock->getLineBreakWidth(); Allocation childAllocation; childAllocation.x = cba->x + - calcFloatX (vloat, side, gba->x - cba->x, gba->width, availWidth); + calcFloatX (vloat, side, gba->x - cba->x, gba->width, lineBreakWidth); childAllocation.y = gba->y + vloat->yReal; childAllocation.width = vloat->size.width; childAllocation.ascent = vloat->size.ascent; @@ -1057,7 +1076,7 @@ void OutOfFlowMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) *lastAllocatedFloat = newLastAllocatedFloat; - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } @@ -1067,41 +1086,49 @@ void OutOfFlowMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) * gbX is given relative to the CB, as is the return value. */ int OutOfFlowMgr::calcFloatX (Float *vloat, Side side, int gbX, int gbWidth, - int gbAvailWidth) + int gbLineBreakWidth) { - DBG_OBJ_MSGF ("resize.oofm", 0, "<b>calcFloatX</b> (%p, %s, %d, %d, %d)", - vloat->getWidget (), side == LEFT ? "LEFT" : "RIGHT", gbX, - gbWidth, gbAvailWidth); - DBG_OBJ_MSG_START (); - - int gbActualWidth, x; + DBG_OBJ_ENTER ("resize.common", 0, "calcFloatX", "%p, %s, %d, %d, %d", + vloat->getWidget (), side == LEFT ? "LEFT" : "RIGHT", gbX, + gbWidth, gbLineBreakWidth); + int x; switch (side) { case LEFT: // Left floats are always aligned on the left side of the - // generator (content, not allocation). + // generator (content, not allocation) ... x = gbX + vloat->generatingBlock->getStyle()->boxOffsetX(); DBG_OBJ_MSGF ("resize.oofm", 1, "left: x = %d + %d = %d", gbX, vloat->generatingBlock->getStyle()->boxOffsetX(), x); + // ... but when the float exceeds the line break width of the + // container, it is corrected (but not left of the container). + // This way, we save space and, especially within tables, avoid + // some problems. + if (wasAllocated (containingBlock) && + x + vloat->size.width > containingBlock->getLineBreakWidth ()) { + x = max (0, containingBlock->getLineBreakWidth () - vloat->size.width); + DBG_OBJ_MSGF ("resize.common", 1, + "corrected to: max (0, %d - %d) = %d", + containingBlock->getLineBreakWidth (), vloat->size.width, + x); + } break; case RIGHT: - // In some cases, the actual (allocated) width is too large; we - // use the "available" width here. - gbActualWidth = min (gbWidth, gbAvailWidth); - DBG_OBJ_MSGF ("resize.oofm", 1, - "right: gbActualWidth = min (%d, %d) = %d", - gbWidth, gbAvailWidth, gbActualWidth); - // Similar for right floats, but in this case, floats are // shifted to the right when they are too big (instead of // shifting the generator to the right). - x = max (gbX + gbActualWidth - vloat->size.width + + // Notice that not the actual width, but the line break width is + // used. (This changed for GROWS, where the width of a textblock + // is often smaller that the line break.) + + x = max (gbX + gbLineBreakWidth - vloat->size.width - vloat->generatingBlock->getStyle()->boxRestWidth(), // Do not exceed CB allocation: 0); - DBG_OBJ_MSGF ("resize.oofm", 1, "x = max (%d + %d - %d - %d, 0) = %d", - gbX, gbActualWidth, vloat->size.width, + DBG_OBJ_MSGF ("resize.common", 1, "x = max (%d + %d - %d - %d, 0) = %d", + gbX, gbLineBreakWidth, vloat->size.width, vloat->generatingBlock->getStyle()->boxRestWidth(), x); break; @@ -1111,7 +1138,7 @@ int OutOfFlowMgr::calcFloatX (Float *vloat, Side side, int gbX, int gbWidth, break; } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return x; } @@ -1190,8 +1217,8 @@ void OutOfFlowMgr::addWidgetInFlow (Textblock *textblock, void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, int externalIndex) { - DBG_OBJ_MSGF ("construct.oofm", 0, "<b>addWidgetOOF</b> (%p, %p, %d)", - widget, generatingBlock, externalIndex); + DBG_OBJ_ENTER ("construct.oofm", 0, "addWidgetOOF", "%p, %p, %d", + widget, generatingBlock, externalIndex); if (isWidgetFloat (widget)) { TBInfo *tbInfo = getTextblock (generatingBlock); @@ -1206,13 +1233,17 @@ void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, case FLOAT_LEFT: leftFloatsAll->put (vloat); DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size()); + DBG_OBJ_ARRATTRSET_PTR ("leftFloatsAll", leftFloatsAll->size() - 1, + "widget", vloat->getWidget ()); + widget->parentRef = createRefLeftFloat (leftFloatsAll->size() - 1); tbInfo->leftFloatsGB->put (vloat); if (wasAllocated (generatingBlock)) { leftFloatsCB->put (vloat); - //printf ("[%p] adding left float %p (%s %p) to CB list\n", - // containingBlock, vloat, widget->getClassName(), widget); + DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size()); + DBG_OBJ_ARRATTRSET_PTR ("leftFloatsCB", leftFloatsCB->size() - 1, + "widget", vloat->getWidget ()); } else { if (tbInfo->index < lastLeftTBIndex) leftFloatsMark++; @@ -1230,13 +1261,17 @@ void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, case FLOAT_RIGHT: rightFloatsAll->put (vloat); DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size()); + DBG_OBJ_ARRATTRSET_PTR ("rightFloatsAll", rightFloatsAll->size() - 1, + "widget", vloat->getWidget ()); + widget->parentRef = createRefRightFloat (rightFloatsAll->size() - 1); tbInfo->rightFloatsGB->put (vloat); if (wasAllocated (generatingBlock)) { rightFloatsCB->put (vloat); - //printf ("[%p] adding right float %p (%s %p) to CB list\n", - // containingBlock, vloat, widget->getClassName(), widget); + DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size()); + DBG_OBJ_ARRATTRSET_PTR ("rightFloatsCB", rightFloatsCB->size() - 1, + "widget", vloat->getWidget ()); } else { if (tbInfo->index < lastRightTBIndex) rightFloatsMark++; @@ -1275,6 +1310,8 @@ void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, } else // May be extended. assertNotReached(); + + DBG_OBJ_LEAVE (); } void OutOfFlowMgr::moveExternalIndices (Textblock *generatingBlock, @@ -1310,8 +1347,7 @@ OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget) void OutOfFlowMgr::markSizeChange (int ref) { - DBG_OBJ_MSGF ("resize.oofm", 0, "<b>markSizeChange</b> (%d)", ref); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "markSizeChange", "%d", ref); if (isRefFloat (ref)) { Float *vloat; @@ -1331,6 +1367,11 @@ void OutOfFlowMgr::markSizeChange (int ref) vloat->dirty = vloat->sizeChangedSinceLastAllocation = true; + DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.dirty", vloat->dirty); + DBG_OBJ_SET_BOOL_O (vloat->getWidget (), + "<Float>.sizeChangedSinceLastAllocation", + vloat->sizeChangedSinceLastAllocation); + // The generating block is told directly about this. (Others later, in // sizeAllocateEnd.) Could be faster (cf. hasRelationChanged, which // differentiates many special cases), but the size is not known yet, @@ -1341,7 +1382,7 @@ void OutOfFlowMgr::markSizeChange (int ref) } else assertNotReached(); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } @@ -1404,9 +1445,8 @@ void OutOfFlowMgr::tellPosition (Widget *widget, int yReq) void OutOfFlowMgr::tellFloatPosition (Widget *widget, int yReq) { - DBG_OBJ_MSGF ("resize.oofm", 0, "<b>tellFloatPosition</b> (%p, %d)", - widget, yReq); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "tellFloatPosition", "%p, %d", + widget, yReq); assert (yReq >= 0); @@ -1483,7 +1523,7 @@ void OutOfFlowMgr::tellFloatPosition (Widget *widget, int yReq) DBG_OBJ_MSGF ("resize.oofm", 1, "vloat->yReq = %d, vloat->yReal = %d", vloat->yReq, vloat->yReal); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } bool OutOfFlowMgr::collidesV (Float *vloat, Float *other, SFVType type, @@ -1493,11 +1533,9 @@ bool OutOfFlowMgr::collidesV (Float *vloat, Float *other, SFVType type, // vloat->yReal; never to vloat->allocation->y, even when the GBs are // different. Used only in tellPosition. - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>collides</b> (#%d [%p], #%d [%p], ...)", - vloat->getIndex (type), vloat->getWidget (), - other->getIndex (type), other->getWidget ()); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "collides", "#%d [%p], #%d [%p], ...", + vloat->getIndex (type), vloat->getWidget (), + other->getIndex (type), other->getWidget ()); bool result; @@ -1551,7 +1589,7 @@ bool OutOfFlowMgr::collidesV (Float *vloat, Float *other, SFVType type, else DBG_OBJ_MSG ("resize.oofm", 1, "does not collide"); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return result; } @@ -1565,7 +1603,7 @@ bool OutOfFlowMgr::collidesH (Float *vloat, Float *other, SFVType type) if (vloat->generatingBlock == other->generatingBlock) collidesH = vloat->size.width + other->size.width + vloat->generatingBlock->getStyle()->boxDiffWidth() - > vloat->generatingBlock->getAvailWidth(); + > vloat->generatingBlock->getLineBreakWidth(); else { assert (wasAllocated (vloat->generatingBlock)); assert (wasAllocated (other->generatingBlock)); @@ -1582,7 +1620,7 @@ bool OutOfFlowMgr::collidesH (Float *vloat, Float *other, SFVType type) vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT ? LEFT : RIGHT, gba->x, gba->width, - vloat->generatingBlock->getAvailWidth ()); + vloat->generatingBlock->getLineBreakWidth ()); // Generally: right border of the left float > left border of // the right float (all in canvas coordinates). @@ -1638,8 +1676,7 @@ void OutOfFlowMgr::getFloatsListsAndSide (Float *vloat, void OutOfFlowMgr::getSize (Requisition *cbReq, int *oofWidth, int *oofHeight) { - DBG_OBJ_MSG ("resize.oofm", 0, "<b>getSize</b> ()"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize.oofm", 0, "getSize"); int oofWidthAbsPos, oofHeightAbsPos; getAbsolutelyPositionedSize (cbReq, &oofWidthAbsPos, &oofHeightAbsPos); @@ -1655,22 +1692,22 @@ void OutOfFlowMgr::getSize (Requisition *cbReq, int *oofWidth, int *oofHeight) "=> (a: %d, l: %d, r: %d => %d) * (a: %d, l: %d, r: %d => %d)", oofWidthAbsPos, oofWidthtLeft, oofWidthRight, *oofWidth, oofHeightAbsPos, oofHeightLeft, oofHeightRight, *oofHeight); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void OutOfFlowMgr::getFloatsSize (Requisition *cbReq, Side side, int *width, int *height) { - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>getFloatsSize</b> ((%d * (%d + %d), %s, ...)", - cbReq->width, cbReq->ascent, cbReq->descent, - side == LEFT ? "LEFT" : "RIGHT"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "getFloatsSize", "(%d * (%d + %d), %s, ...", + cbReq->width, cbReq->ascent, cbReq->descent, + side == LEFT ? "LEFT" : "RIGHT"); SortedFloatsVector *list = getFloatsListForTextblock (containingBlock, side); *width = *height = 0; + DBG_OBJ_MSGF ("resize.oofm", 1, "%d floats on this side", list->size()); + for (int i = 0; i < list->size(); i++) { Float *vloat = list->get(i); @@ -1681,13 +1718,13 @@ void OutOfFlowMgr::getFloatsSize (Requisition *cbReq, Side side, int *width, if (vloat->generatingBlock == containingBlock) { x = calcFloatX (vloat, side, 0, cbReq->width, - vloat->generatingBlock->getAvailWidth ()); + vloat->generatingBlock->getLineBreakWidth ()); y = vloat->yReal; } else { Allocation *gba = getAllocation(vloat->generatingBlock); x = calcFloatX (vloat, side, gba->x - containingBlockAllocation.x, gba->width, - vloat->generatingBlock->getAvailWidth ()); + vloat->generatingBlock->getLineBreakWidth ()); y = gba->y - containingBlockAllocation.y + vloat->yReal; } @@ -1707,15 +1744,14 @@ void OutOfFlowMgr::getFloatsSize (Requisition *cbReq, Side side, int *width, vloat->getWidget (), vloat->generatingBlock); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void OutOfFlowMgr::getExtremes (Extremes *cbExtr, int *oofMinWidth, int *oofMaxWidth) { - DBG_OBJ_MSGF ("resize.oofm", 0, "<b>getExtremes</b> ((%d / %d), ...)", - cbExtr->minWidth, cbExtr->maxWidth); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "getExtremes", "(%d / %d), ...", + cbExtr->minWidth, cbExtr->maxWidth); int oofMinWidthAbsPos, oofMaxWidthAbsPos; getAbsolutelyPositionedExtremes (cbExtr, &oofMinWidthAbsPos, @@ -1733,17 +1769,15 @@ void OutOfFlowMgr::getExtremes (Extremes *cbExtr, int *oofMinWidth, oofMinWidthAbsPos, oofMinWidthtLeft, oofMinWidthRight, *oofMinWidth, oofMaxWidthAbsPos, oofMaxWidthLeft, oofMaxWidthRight, *oofMaxWidth); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void OutOfFlowMgr::getFloatsExtremes (Extremes *cbExtr, Side side, int *minWidth, int *maxWidth) { - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>getFloatsExtremes</b> ((%d / %d), %s, ...)", - cbExtr->minWidth, cbExtr->maxWidth, - side == LEFT ? "LEFT" : "RIGHT"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "getFloatsExtremes", "(%d / %d), %s, ...", + cbExtr->minWidth, cbExtr->maxWidth, + side == LEFT ? "LEFT" : "RIGHT"); *minWidth = *maxWidth = 0; @@ -1763,19 +1797,6 @@ void OutOfFlowMgr::getFloatsExtremes (Extremes *cbExtr, Side side, vloat->getWidget (), vloat->generatingBlock, extr.minWidth, extr.maxWidth); - if (isAbsLength (vloat->getWidget()->getStyle()->width)) { - int width = absLengthVal (vloat->getWidget()->getStyle()->width); - if (extr.minWidth < width) - extr.minWidth = width; - if (extr.maxWidth > width) - // maxWidth not smaller than minWidth - extr.maxWidth = max (width, extr.minWidth); - - DBG_OBJ_MSGF ("resize.oofm", 1, - "corrected by absolute width %d: %d / %d", - width, extr.minWidth, extr.maxWidth); - } - // TODO: Or zero (instead of rightDiff) for right floats? *minWidth = max (*minWidth, @@ -1789,7 +1810,7 @@ void OutOfFlowMgr::getFloatsExtremes (Extremes *cbExtr, Side side, vloat->getWidget (), vloat->generatingBlock); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } // Returns "false" when borders cannot yet determined; *leftDiff and @@ -1797,10 +1818,9 @@ void OutOfFlowMgr::getFloatsExtremes (Extremes *cbExtr, Side side, bool OutOfFlowMgr::getFloatDiffToCB (Float *vloat, int *leftDiff, int *rightDiff) { - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>getDiffToCB</b> (float %p [generated by %p], ...)", - vloat->getWidget (), vloat->generatingBlock); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "getDiffToCB", + "float %p [generated by %p], ...", + vloat->getWidget (), vloat->generatingBlock); bool result; @@ -1834,7 +1854,7 @@ bool OutOfFlowMgr::getFloatDiffToCB (Float *vloat, int *leftDiff, result = false; } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return result; } @@ -1882,10 +1902,9 @@ int OutOfFlowMgr::getRightBorder (Textblock *textblock, int y, int h, int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h, Textblock *lastGB, int lastExtIndex) { - DBG_OBJ_MSGF ("border", 0, "<b>getBorder</b> (%p, %s, %d, %d, %p, %d)", - textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, - lastGB, lastExtIndex); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("border", 0, "getBorder", "%p, %s, %d, %d, %p, %d", + textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, + lastGB, lastExtIndex); SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex); @@ -1894,7 +1913,7 @@ int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h, if (first == -1) { // No float. - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return 0; } else { // It is not sufficient to find the first float, since a line @@ -1931,12 +1950,12 @@ int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h, fla->x, fla->width, tba->x, thisBorder); } else { // See also calcFloatX. - int tbAvWidth = textblock->getAvailWidth (); - thisBorder = tba->x + min (tba->width, tbAvWidth) - fla->x; + thisBorder = + tba->x + textblock->getLineBreakWidth () - fla->x; DBG_OBJ_MSGF ("border", 1, - "not GB: thisBorder = %d + min (%d, %d) - %d " + "not GB: thisBorder = %d + %d - %d " "= %d", - tba->x, tba->width, tbAvWidth, fla->x, + tba->x, textblock->getLineBreakWidth (), fla->x, thisBorder); } } @@ -1946,7 +1965,7 @@ int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h, } } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return border; } } @@ -1955,12 +1974,22 @@ int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h, OutOfFlowMgr::SortedFloatsVector *OutOfFlowMgr::getFloatsListForTextblock (Textblock *textblock, Side side) { - if (wasAllocated (textblock)) - return side == LEFT ? leftFloatsCB : rightFloatsCB; - else { + DBG_OBJ_ENTER ("oofm.common", 1, "getFloatsListForTextblock", "%p, %s", + textblock, side == LEFT ? "LEFT" : "RIGHT"); + + OutOfFlowMgr::SortedFloatsVector *list; + + if (wasAllocated (textblock)) { + DBG_OBJ_MSG ("oofm.common", 2, "returning <b>CB</b> list"); + list = side == LEFT ? leftFloatsCB : rightFloatsCB; + } else { + DBG_OBJ_MSG ("oofm.common", 2, "returning <b>GB</b> list"); TBInfo *tbInfo = getTextblock (textblock); - return side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; + list = side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; } + + DBG_OBJ_LEAVE (); + return list; } @@ -1985,16 +2014,15 @@ bool OutOfFlowMgr::hasFloatRight (Textblock *textblock, int y, int h, bool OutOfFlowMgr::hasFloat (Textblock *textblock, Side side, int y, int h, Textblock *lastGB, int lastExtIndex) { - DBG_OBJ_MSGF ("border", 0, "<b>hasFloat</b> (%p, %s, %d, %d, %p, %d)", - textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, - lastGB, lastExtIndex); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("border", 0, "hasFloat", "%p, %s, %d, %d, %p, %d", + textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, + lastGB, lastExtIndex); SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex); DBG_OBJ_MSGF ("border", 1, "first = %d", first); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return first != -1; } @@ -2003,8 +2031,7 @@ bool OutOfFlowMgr::hasFloat (Textblock *textblock, Side side, int y, int h, */ int OutOfFlowMgr::getClearPosition (Textblock *tb) { - DBG_OBJ_MSGF ("resize.oofm", 0, "<b>getClearPosition</b> (%p)", tb); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p", tb); int pos; @@ -2024,16 +2051,15 @@ int OutOfFlowMgr::getClearPosition (Textblock *tb) pos = 0; DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return pos; } int OutOfFlowMgr::getClearPosition (Textblock *tb, Side side) { - DBG_OBJ_MSGF ("resize.oofm", 0, "<b>getClearPosition</b> (%p, %s)", - tb, side == LEFT ? "LEFT" : "RIGHT"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p, %s", + tb, side == LEFT ? "LEFT" : "RIGHT"); int pos; @@ -2063,127 +2089,29 @@ int OutOfFlowMgr::getClearPosition (Textblock *tb, Side side) } DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return pos; } void OutOfFlowMgr::ensureFloatSize (Float *vloat) { - if (vloat->dirty || - (vloat->cbAvailWidth != containingBlock->getAvailWidth () && - (// If the size of the containing block has changed (represented - // currently by the available width), a recalculation of a relative - // float width may also be necessary. - isPerLength (vloat->getWidget()->getStyle()->width) || - // Similar for "auto" widths of textblocks etc. - (vloat->getWidget()->usesHints () && - vloat->getWidget()->getStyle()->width == LENGTH_AUTO)))) { - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>ensureFloatSize</b> (%p): recalculation", - vloat->getWidget ()); - DBG_OBJ_MSG_START (); + // Historical note: relative sizes (e. g. percentages) are already + // handled by (at this time) Layout::containerSizeChanged, so + // Float::dirty will be set. - Extremes extremes; - vloat->getWidget()->getExtremes (&extremes); - DBG_OBJ_MSGF ("resize.oofm", 1, "getExtremes => %d / %d", - extremes.minWidth, extremes.maxWidth); - - // TODO Ugly. Soon to be replaced by cleaner code? See also - // comment in Textblock::calcWidgetSize. - - if (vloat->getWidget()->usesHints ()) { - // For widths defined by CSS, similar adjustments (extremes - // etc.) like below are necessary, to prevent CPU hogging. - - Length cssWidth = vloat->getWidget()->getStyle()->width; - if (isAbsLength (cssWidth)) { - int width = absLengthVal (cssWidth); - DBG_OBJ_MSGF ("resize.oofm", 1, "about to set absolute width: %d", - width); - width = adjustFloatWidth (width, &extremes); - vloat->getWidget()->setWidth (width); - } else if (cssWidth == LENGTH_AUTO || isPerLength (cssWidth)) { - // It is important that the width of the *CB* is not - // larger than its minimal width, when the latter is set - // as size hint; otherwise we have an endless queueResize - // cycle (resulting in CPU hogging) when the CB is part of - // a narrow table column. To prevent this, the width of - // the *float* has to be limited (cf. also getExtremes). - - int availWidth, leftDiff, rightDiff; - if (getFloatDiffToCB (vloat, &leftDiff, &rightDiff)) - availWidth = containingBlock->getAvailWidth() - - (vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT ? - leftDiff : rightDiff); - else - // Not allocated: next allocation will take care. - availWidth = containingBlock->getAvailWidth(); - - int width; - - if (cssWidth == LENGTH_AUTO) { - width = availWidth; - DBG_OBJ_MSGF ("resize.oofm", 1, "setting width 'auto': %d", - width); - } else { - width = multiplyWithPerLength (availWidth, cssWidth); - - // Some more corrections (nonsense percentage values): - if (width < 1) - width = 1; - if (width > availWidth) - width = availWidth; - - DBG_OBJ_MSGF ("resize.oofm", 1, - "about to set percentage width: %d * %g -> %d", - availWidth, perLengthVal (cssWidth), width); - width = adjustFloatWidth (width, &extremes); - } + DBG_OBJ_ENTER ("resize.oofm", 0, "ensureFloatSize", "%p", + vloat->getWidget ()); - vloat->getWidget()->setWidth (width); - } else - DBG_OBJ_MSG ("resize.oofm", 1, - "setting width: <b>relative length? may be a bug</b>"); - } else - DBG_OBJ_MSG ("resize.oofm", 1, "setting no width: uses no hints"); - - // This is a bit hackish: We first request the size, then set - // the available width (also considering the one of the - // containing block, and the extremes of the float), then - // request the size again, which may of course have a different - // result. This is a fix for the bug: - // - // Text in floats, which are wider because of an image, are - // broken at a too narrow width. Reproduce: - // test/floats2.html. After the image has been loaded, the - // text "Some text in a float." should not be broken - // anymore. - // - // If the call of setWidth not is neccessary, the second call - // will read the size from the cache, so no redundant - // calculation is necessary. - // - // Furthermore, extremes are considered; especially, floats are too - // wide, sometimes. - - vloat->getWidget()->sizeRequest (&vloat->size); - DBG_OBJ_MSGF ("resize.oofm", 1, "sizeRequest (1) => %d * (%d + %d)", - vloat->size.width, vloat->size.ascent, vloat->size.descent); + if (vloat->dirty) { + DBG_OBJ_MSG ("resize.oofm", 1, "dirty: recalculation"); - // Set width ... - int width = vloat->size.width; - DBG_OBJ_MSGF ("resize.oofm", 1, "new width: %d", width); - width = adjustFloatWidth (width, &extremes); - vloat->getWidget()->setWidth (width); vloat->getWidget()->sizeRequest (&vloat->size); - DBG_OBJ_MSGF ("resize.oofm", 1, "sizeRequest (2) => %d * (%d + %d)", - vloat->size.width, vloat->size.ascent, vloat->size.descent); - - vloat->cbAvailWidth = containingBlock->getAvailWidth (); + vloat->cbLineBreakWidth = containingBlock->getLineBreakWidth (); vloat->dirty = false; + DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.dirty", vloat->dirty); - DBG_OBJ_MSGF ("resize.oofm", 1, "final size: %d * (%d + %d)", + DBG_OBJ_MSGF ("resize.oofm", 1, "size: %d * (%d + %d)", vloat->size.width, vloat->size.ascent, vloat->size.descent); DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.width", @@ -2194,42 +2122,9 @@ void OutOfFlowMgr::ensureFloatSize (Float *vloat) vloat->size.descent); // "sizeChangedSinceLastAllocation" is reset in sizeAllocateEnd() - - DBG_OBJ_MSG_END (); } -} - -int OutOfFlowMgr::adjustFloatWidth (int width, Extremes *extremes) -{ - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>adjustFloatWidth</b> (%d, (%d, %d)) [CB->availWidth = %d]", - width, extremes->minWidth, extremes->maxWidth, - containingBlock->getAvailWidth()); - DBG_OBJ_MSG_START (); - - // Consider extremes (as described above). - if (width < extremes->minWidth) { - width = extremes->minWidth; - DBG_OBJ_MSGF ("resize.oofm", 1, "adjusted to minWidth: %d", width); - } - if (width > extremes->maxWidth) { - width = extremes->maxWidth; - DBG_OBJ_MSGF ("resize.oofm", 1, "adjusted to maxWidth: %d", width); - } - // Finally, consider the available width of the containing - // block. Order is important: to prevent problems, the available - // width of the float must never be larger than the one of the - // containing block. (Somewhat hackish, will be solved cleaner with - // GROWS.) - if (width > containingBlock->getAvailWidth()) { - width = containingBlock->getAvailWidth(); - DBG_OBJ_MSGF ("resize.oofm", 1, "adjusted to CB::availWidth: %d", width); - } - - DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", width); - DBG_OBJ_MSG_END (); - - return width; + + DBG_OBJ_LEAVE (); } void OutOfFlowMgr::getAbsolutelyPositionedSize (Requisition *cbReq, int *width, @@ -2250,69 +2145,8 @@ void OutOfFlowMgr::getAbsolutelyPositionedExtremes (Extremes *cbExtr, void OutOfFlowMgr::ensureAbsolutelyPositionedSizeAndPosition (AbsolutelyPositioned *abspos) { - // No work is done anymore on this, since widget sizes will be - // redesigned before absolute positions are finished. - - if (abspos->dirty) { - Style *style = abspos->widget->getStyle(); - int availWidth = containingBlock->getAvailWidth(); - int availHeight = - containingBlock->getAvailAscent() + containingBlock->getAvailDescent(); - - if (style->left == LENGTH_AUTO) - abspos->xCB = 0; - else - abspos->xCB = - calcValueForAbsolutelyPositioned (abspos, style->left, availWidth); - - if (style->top == LENGTH_AUTO) - abspos->yCB = 0; - else - abspos->yCB = - calcValueForAbsolutelyPositioned (abspos, style->top, availHeight); - - abspos->width = -1; // undefined - if (style->width != LENGTH_AUTO) - abspos->width = calcValueForAbsolutelyPositioned (abspos, style->width, - availWidth); - else if (style->right != LENGTH_AUTO) { - int right = calcValueForAbsolutelyPositioned (abspos, style->right, - availWidth); - abspos->width = max (0, availWidth - (abspos->xCB + right)); - } - - abspos->height = -1; // undefined - if (style->height != LENGTH_AUTO) - abspos->height = calcValueForAbsolutelyPositioned (abspos, - style->height, - availHeight); - else if (style->bottom != LENGTH_AUTO) { - int bottom = calcValueForAbsolutelyPositioned (abspos, style->bottom, - availHeight); - abspos->height = max (0, availHeight - (abspos->yCB + bottom)); - } - - if (abspos->width != -1) - abspos->widget->setWidth (abspos->width); - - if (abspos->height != -1) { - abspos->widget->setAscent (abspos->height); - abspos->widget->setDescent (0); // TODO - } - - if (abspos->width == -1 || abspos->height == -1) { - Requisition req; - abspos->widget->sizeRequest (&req); - - if (abspos->width == -1) - abspos->width = req.width; - - if (abspos->height == -1) - abspos->height = req.ascent + req.descent; - } - - abspos->dirty = false; - } + // TODO + assertNotReached (); } int OutOfFlowMgr::calcValueForAbsolutelyPositioned @@ -2332,7 +2166,7 @@ int OutOfFlowMgr::calcValueForAbsolutelyPositioned void OutOfFlowMgr::sizeAllocateAbsolutelyPositioned () { for (int i = 0; i < absolutelyPositioned->size(); i++) { - Allocation *cbAllocation = getAllocation(containingBlock); + Allocation *cbAllocation = getAllocation (containingBlock); AbsolutelyPositioned *abspos = absolutelyPositioned->get (i); ensureAbsolutelyPositionedSizeAndPosition (abspos); diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh index 429482fd..19205717 100644 --- a/dw/outofflowmgr.hh +++ b/dw/outofflowmgr.hh @@ -97,9 +97,9 @@ private: respectively. -1 initially. */ int sideSpanningIndex, mark; core::Requisition size; - int cbAvailWidth; /* On which the calculation of relative sizes - is based. Height not yet used, and probably - not added before size redesign. */ + int cbLineBreakWidth; /* On which the calculation of relative sizes + is based. Height not yet used, and probably + not added before size redesign. */ bool dirty, sizeChangedSinceLastAllocation; Float (OutOfFlowMgr *oofm, core::Widget *widget, @@ -172,7 +172,7 @@ private: class TBInfo: public WidgetInfo { public: - int availWidth; + int lineBreakWidth; int index; // position within "tbInfos" TBInfo *parent; @@ -272,7 +272,7 @@ private: void moveFromGBToCB (Side side); void sizeAllocateFloats (Side side, int newLastAllocatedFloat); int calcFloatX (Float *vloat, Side side, int gbX, int gbWidth, - int gbAvailWidth); + int gbLineBreakWidth); bool hasRelationChanged (TBInfo *tbInfo,int *minFloatPos, core::Widget **minFloat); @@ -319,7 +319,6 @@ private: int getClearPosition (Textblock *tb, Side side); void ensureFloatSize (Float *vloat); - int adjustFloatWidth (int width, core::Extremes *extremes); void tellFloatPosition (core::Widget *widget, int yReq); @@ -395,6 +394,7 @@ public: void sizeAllocateStart (Textblock *caller, core::Allocation *allocation); void sizeAllocateEnd (Textblock *caller); + void containerSizeChangedForChildren (); void draw (core::View *view, core::Rectangle *area); void markSizeChange (int ref); diff --git a/dw/ruler.cc b/dw/ruler.cc index 2b5288c2..3fdbfb6d 100644 --- a/dw/ruler.cc +++ b/dw/ruler.cc @@ -28,26 +28,39 @@ namespace dw { Ruler::Ruler () { - setFlags (USES_HINTS); - setFlags (BLOCK_LEVEL); - unsetFlags (HAS_CONTENTS); - availWidth = 0; } void Ruler::sizeRequestImpl (core::Requisition *requisition) { requisition->width = - lout::misc::max (availWidth, getStyle()->boxDiffWidth ()); + lout::misc::max (getAvailWidth (true), getStyle()->boxDiffWidth ()); requisition->ascent = getStyle()->boxOffsetY (); requisition->descent = getStyle()->boxRestHeight (); } -void Ruler::setWidth (int width) +void Ruler::getExtremesImpl (core::Extremes *extremes) { - if (availWidth != width) { - availWidth = width; - queueResize (0, false); - } + extremes->minWidth = extremes->maxWidth = getStyle()->boxDiffWidth (); + extremes->minWidthIntrinsic = extremes->minWidth; + extremes->maxWidthIntrinsic = extremes->maxWidth; + correctExtremes (extremes); +} + +bool Ruler::isBlockLevel () +{ + return true; +} + +void Ruler::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + // Nothing to do. + DBG_OBJ_LEAVE (); +} + +bool Ruler::usesAvailWidth () +{ + return true; } void Ruler::draw (core::View *view, core::Rectangle *area) diff --git a/dw/ruler.hh b/dw/ruler.hh index 863792dd..1f3491bc 100644 --- a/dw/ruler.hh +++ b/dw/ruler.hh @@ -15,17 +15,18 @@ namespace dw { */ class Ruler: public core::Widget { -private: - int availWidth; - protected: void sizeRequestImpl (core::Requisition *requisition); - void setWidth (int width); + void getExtremesImpl (core::Extremes *extremes); + void containerSizeChangedForChildren (); + bool usesAvailWidth (); void draw (core::View *view, core::Rectangle *area); public: Ruler (); + bool isBlockLevel (); + core::Iterator *iterator (core::Content::Type mask, bool atEnd); }; diff --git a/dw/simpletablecell.cc b/dw/simpletablecell.cc new file mode 100644 index 00000000..083deaed --- /dev/null +++ b/dw/simpletablecell.cc @@ -0,0 +1,58 @@ +/* + * Dillo Widget + * + * Copyright 2014 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include "simpletablecell.hh" +#include "../lout/debug.hh" + +namespace dw { + +int SimpleTableCell::CLASS_ID = -1; + +SimpleTableCell::SimpleTableCell (bool limitTextWidth): + Textblock (limitTextWidth) +{ + DBG_OBJ_CREATE ("dw::SimpleTableCell"); + registerName ("dw::SimpleTableCell", &CLASS_ID); +} + +SimpleTableCell::~SimpleTableCell() +{ + DBG_OBJ_DELETE (); +} + +bool SimpleTableCell::isBlockLevel () +{ + return false; +} + +int SimpleTableCell::applyPerWidth (int containerWidth, + core::style::Length perWidth) +{ + return core::style::multiplyWithPerLength (containerWidth, perWidth); +} + +int SimpleTableCell::applyPerHeight (int containerHeight, + core::style::Length perHeight) +{ + return core::style::multiplyWithPerLength (containerHeight, perHeight); +} + +} // namespace dw diff --git a/dw/simpletablecell.hh b/dw/simpletablecell.hh new file mode 100644 index 00000000..a452add9 --- /dev/null +++ b/dw/simpletablecell.hh @@ -0,0 +1,24 @@ +#ifndef __DW_SIMPLETABLECELL_HH__ +#define __DW_SIMPLETABLECELL_HH__ + +#include "textblock.hh" + +namespace dw { + +class SimpleTableCell: public Textblock +{ +public: + static int CLASS_ID; + + SimpleTableCell (bool limitTextWidth); + ~SimpleTableCell (); + + int applyPerWidth (int containerWidth, core::style::Length perWidth); + int applyPerHeight (int containerHeight, core::style::Length perHeight); + + bool isBlockLevel (); +}; + +} // namespace dw + +#endif // __DW_SIMPLETABLECELL_HH__ diff --git a/dw/style.hh b/dw/style.hh index 2cc258bf..578e65c0 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -435,7 +435,8 @@ inline int absLengthVal(Length l) { return l >> 2; } * When possible, do not use this function directly; it may be removed * soon. Instead, use multiplyWithPerLength or multiplyWithPerLengthRounded. */ -inline double perLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); } +inline double perLengthVal_useThisOnlyForDebugging(Length l) +{ return (double)(l & ~3) / (1 << 18); } /** \brief Returns the value of a relative length, as a float. * @@ -450,7 +451,7 @@ inline double relLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); } * Use this instead of perLengthVal, when possible. */ inline int multiplyWithPerLength(int x, Length l) { - return x * perLengthVal(l); + return x * perLengthVal_useThisOnlyForDebugging (l); } /** @@ -459,8 +460,8 @@ inline int multiplyWithPerLength(int x, Length l) { * * (This function exists for backward compatibility.) */ -inline int multiplyWithPerLengthRounded (int x, Length l) { - return lout::misc::roundInt (x * perLengthVal(l)); +inline int multiplyWithPerLengthRounded(int x, Length l) { + return lout::misc::roundInt (x * perLengthVal_useThisOnlyForDebugging (l)); } inline int multiplyWithRelLength(int x, Length l) { @@ -564,22 +565,14 @@ public: = borderStyle.left = val; } inline int boxOffsetX () - { - return margin.left + borderWidth.left + padding.left; - } + { return margin.left + borderWidth.left + padding.left; } inline int boxRestWidth () - { - return margin.right + borderWidth.right + padding.right; - } + { return margin.right + borderWidth.right + padding.right; } inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); } inline int boxOffsetY () - { - return margin.top + borderWidth.top + padding.top; - } + { return margin.top + borderWidth.top + padding.top; } inline int boxRestHeight () - { - return margin.bottom + borderWidth.bottom + padding.bottom; - } + { return margin.bottom + borderWidth.bottom + padding.bottom; } inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } inline bool hasBackground () diff --git a/dw/table.cc b/dw/table.cc index 565dfc9e..edd83f88 100644 --- a/dw/table.cc +++ b/dw/table.cc @@ -1,7 +1,7 @@ /* * Dillo Widget * - * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * Copyright 2005-2007, 2014 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 @@ -34,41 +34,36 @@ Table::Table(bool limitTextWidth) { DBG_OBJ_CREATE ("dw::Table"); registerName ("dw::Table", &CLASS_ID); - setFlags (BLOCK_LEVEL); - setFlags (USES_HINTS); setButtonSensitive(false); this->limitTextWidth = limitTextWidth; rowClosed = false; - // random values - availWidth = 100; - availAscent = 100; - availDescent = 0; - numRows = 0; numCols = 0; curRow = -1; curCol = 0; + DBG_OBJ_SET_NUM ("numCols", numCols); + DBG_OBJ_SET_NUM ("numRows", numCols); + 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 <core::style::Length> (8); + colWidthsUpToDateWidthColExtremes = true; + DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes", + colWidthsUpToDateWidthColExtremes); redrawX = 0; redrawY = 0; } - Table::~Table() { for (int i = 0; i < children->size (); i++) { @@ -94,17 +89,17 @@ Table::~Table() delete colWidths; delete cumHeight; delete rowSpanCells; - delete colSpanCells; delete baseline; delete rowStyle; - delete colPercents; DBG_OBJ_DELETE (); } void Table::sizeRequestImpl (core::Requisition *requisition) { - forceCalcCellSizes (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl"); + + forceCalcCellSizes (true); /** * \bug Baselines are not regarded here. @@ -119,40 +114,44 @@ void Table::sizeRequestImpl (core::Requisition *requisition) + getStyle()->vBorderSpacing; requisition->descent = 0; + correctRequisition (requisition, core::splitHeightPreserveDescent); + + DBG_OBJ_LEAVE (); } void Table::getExtremesImpl (core::Extremes *extremes) { - if (numCols == 0) { - extremes->minWidth = extremes->maxWidth = 0; - return; - } - - forceCalcColumnExtremes (); + DBG_OBJ_ENTER0 ("resize", 0, "getExtremesImpl"); - 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 = - misc::max (extremes->minWidth, - core::style::absLengthVal(getStyle()->width)); - extremes->maxWidth = - misc::max (extremes->maxWidth, - core::style::absLengthVal(getStyle()->width)); + if (numCols == 0) + extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth = + extremes->maxWidthIntrinsic = boxDiffWidth (); + else { + forceCalcColumnExtremes (); + + extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth = + extremes->maxWidthIntrinsic = + (numCols + 1) * getStyle()->hBorderSpacing + boxDiffWidth (); + for (int col = 0; col < numCols; col++) { + extremes->minWidth += colExtremes->getRef(col)->minWidth; + extremes->minWidthIntrinsic += + colExtremes->getRef(col)->minWidthIntrinsic; + extremes->maxWidth += colExtremes->getRef(col)->maxWidth; + extremes->maxWidthIntrinsic += + colExtremes->getRef(col)->maxWidthIntrinsic; + } } - _MSG(" Table::getExtremesImpl, {%d, %d} numCols=%d\n", - extremes->minWidth, extremes->maxWidth, numCols); + correctExtremes (extremes); + + DBG_OBJ_LEAVE (); } void Table::sizeAllocateImpl (core::Allocation *allocation) { - calcCellSizes (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeAllocateImpl"); + + calcCellSizes (true); /** * \bug Baselines are not regarded here. @@ -167,8 +166,7 @@ void Table::sizeAllocateImpl (core::Allocation *allocation) for (int row = 0; row < numRows; row++) { int n = row * numCols + col; if (childDefined (n)) { - int width = - (children->get(n)->cell.colspanEff - 1) + 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); @@ -192,6 +190,8 @@ void Table::sizeAllocateImpl (core::Allocation *allocation) x += colWidths->get (col) + getStyle()->hBorderSpacing; } + + DBG_OBJ_LEAVE (); } void Table::resizeDrawImpl () @@ -202,30 +202,147 @@ void Table::resizeDrawImpl () redrawY = getHeight (); } -void Table::setWidth (int width) +int Table::getAvailWidthOfChild (Widget *child, bool forceValue) { - // If limitTextWidth is set, a queueResize may also be necessary. - if (availWidth != width || limitTextWidth) { - _MSG(" Table::setWidth %d\n", width); - availWidth = width; - queueResize (0, false); + DBG_OBJ_ENTER ("resize", 0, "getAvailWidthOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int width; + + // Unlike other containers, the table widget sometimes narrows + // columns to a width less than specified by CSS (see + // forceCalcCellSizes). For this reason, the column widths have to + // be calculated in all cases. + if (forceValue) { + calcCellSizes (false); + width = calcAvailWidthForDescendant (child); + } else + width = -1; + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + return width; +} + +void Table::correctRequisitionOfChild (core::Widget *child, + core::Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)) +{ + // TODO Same comments as in Widget::correctRequisitionOfChild may + // apply here. + + DBG_OBJ_ENTER ("resize", 0, "correctRequisitionOfChild", + "%p, %d * (%d + %d), ...)", + child, requisition->width, requisition->ascent, + requisition->descent); + + if (child->getStyle()->width != core::style::LENGTH_AUTO) { + calcCellSizes (false); + // The requisition is always the width of the column; the table + // widget is special in inforcing this, based on extremes, which + // may again be corrected again by CSS attributes. + requisition->width = calcAvailWidthForDescendant (child); } + + correctReqHeightOfChild (child, requisition, splitHeightFun); + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); } -void Table::setAscent (int ascent) +int Table::calcAvailWidthForDescendant (Widget *child) { - if (availAscent != ascent) { - availAscent = ascent; - queueResize (0, false); + DBG_OBJ_ENTER ("resize", 0, "calcAvailWidthForDescendant", "%p", child); + + // "child" is not a direct child, but a direct descendant. Search + // for the actual childs. + Widget *actualChild = child; + while (actualChild != NULL && actualChild->getParent () != this) + actualChild = actualChild->getParent (); + + assert (actualChild != NULL); + + // TODO This is inefficient. (Use parentRef?) + int width = -1; + for (int row = numRows - 1; width == -1 && row >= 0; row--) { + for (int col = 0; width == -1 && col < numCols; col++) { + int n = row * numCols + col; + if (childDefined (n) && + children->get(n)->cell.widget == actualChild) { + DBG_OBJ_MSGF ("resize", 1, "calculated from column %d", col); + 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); + width = misc::max (width, 0); + } + } } + + assert (width != -1); + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + return width; +} + +int Table::applyPerWidth (int containerWidth, core::style::Length perWidth) +{ + return core::style::multiplyWithPerLength (containerWidth, perWidth); } -void Table::setDescent (int descent) +int Table::applyPerHeight (int containerHeight, core::style::Length perHeight) { - if (availDescent != descent) { - availDescent = descent; - queueResize (0, false); + return core::style::multiplyWithPerLength (containerHeight, perHeight); +} + +void Table::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + for (int col = 0; col < numCols; col++) { + for (int row = 0; row < numRows; row++) { + int n = row * numCols + col; + if (childDefined (n)) + children->get(n)->cell.widget->containerSizeChanged (); + } } + + DBG_OBJ_LEAVE (); +} + +bool Table::affectsSizeChangeContainerChild (core::Widget *child) +{ + DBG_OBJ_ENTER ("resize", 0, "affectsSizeChangeContainerChild", "%p", child); + + bool ret; + + // This is a bit more complicated, as compared to the standard + // implementation (Widget::affectsSizeChangeContainerChild). + // Height would handled the same way, but width is more + // complicated: we would have to track numerous values here. Always + // returning true is correct in all cases, but generally + // inefficient. + + // TODO Better solution? + + ret = true; + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); + return ret; +} + +bool Table::usesAvailWidth () +{ + return true; +} + +bool Table::isBlockLevel () +{ + return true; } void Table::draw (core::View *view, core::Rectangle *area) @@ -272,6 +389,9 @@ core::Iterator *Table::iterator (core::Content::Type mask, bool atEnd) void Table::addCell (Widget *widget, int colspan, int rowspan) { + DBG_OBJ_ENTER ("resize", 0, "addCell", "%p, %d, %d", + widget, colspan, rowspan); + const int maxspan = 100; Child *child; int colspanEff; @@ -373,6 +493,8 @@ void Table::addCell (Widget *widget, int colspan, int rowspan) } MSG("\n"); #endif + + DBG_OBJ_LEAVE (); } void Table::addRow (core::style::Style *style) @@ -393,7 +515,7 @@ void Table::addRow (core::style::Style *style) rowClosed = false; } -TableCell *Table::getCellRef () +AlignedTableCell *Table::getCellRef () { core::Widget *child; @@ -401,14 +523,93 @@ TableCell *Table::getCellRef () int n = curCol + row * numCols; if (childDefined (n)) { child = children->get(n)->cell.widget; - if (child->instanceOf (TableCell::CLASS_ID)) - return (TableCell*)child; + if (child->instanceOf (AlignedTableCell::CLASS_ID)) + return (AlignedTableCell*)child; } } return NULL; } +const char *Table::getExtrModName (ExtrMod mod) +{ + switch (mod) { + case MIN: + return "MIN"; + + case MIN_INTR: + return "MIN_INTR"; + + case MIN_MIN: + return "MIN_MIN"; + + case MAX_MIN: + return "MAX_MIN"; + + case MAX: + return "MAX"; + + case MAX_INTR: + return "MAX_INTR"; + + default: + misc::assertNotReached (); + return NULL; + } +} + +int Table::getExtreme (core::Extremes *extremes, ExtrMod mod) +{ + switch (mod) { + case MIN: + return extremes->minWidth; + + case MIN_INTR: + return extremes->minWidthIntrinsic; + + case MIN_MIN: + return misc::min (extremes->minWidth, extremes->minWidthIntrinsic); + + case MAX_MIN: + return misc::max (extremes->minWidth, extremes->minWidthIntrinsic); + + case MAX: + return extremes->maxWidth; + + case MAX_INTR: + return extremes->maxWidthIntrinsic; + + default: + misc::assertNotReached (); return 0; + } +} + +void Table::setExtreme (core::Extremes *extremes, ExtrMod mod, int value) +{ + switch (mod) { + case MIN: + extremes->minWidth = value; + break; + + case MIN_INTR: + extremes->minWidthIntrinsic = value; + break; + + // MIN_MIN and MAX_MIN not supported here. + + case MAX: + extremes->maxWidth = value; + break; + + case MAX_INTR: + extremes->maxWidthIntrinsic = value; + break; + + default: + misc::assertNotReached (); + } +} + void Table::reallocChildren (int newNumCols, int newNumRows) { assert (newNumCols >= numCols); @@ -476,107 +677,171 @@ void Table::reallocChildren (int newNumCols, int newNumRows) numCols = newNumCols; numRows = newNumRows; + + DBG_OBJ_SET_NUM ("numCols", numCols); + DBG_OBJ_SET_NUM ("numRows", numCols); } // ---------------------------------------------------------------------- -void Table::calcCellSizes () +void Table::calcCellSizes (bool calcHeights) { - if (needsResize () || resizeQueued ()) - forceCalcCellSizes (); + DBG_OBJ_ENTER ("resize", 0, "calcCellSizes", "%s", + calcHeights ? "true" : "false"); + + bool sizeChanged = needsResize () || resizeQueued (); + bool extremesChanges = extremesChanged () || extremesQueued (); + + if (calcHeights ? (extremesChanges || sizeChanged) : + (extremesChanges || !colWidthsUpToDateWidthColExtremes)) + forceCalcCellSizes (calcHeights); + + DBG_OBJ_LEAVE (); } -void Table::forceCalcCellSizes () +void Table::forceCalcCellSizes (bool calcHeights) { - int totalWidth = 0, childHeight, forceTotalWidth = 1; + DBG_OBJ_ENTER0 ("resize", 0, "forceCalcCellSizes"); + + int childHeight; 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 = - misc::min (core::style::multiplyWithPerLength (availWidth, - getStyle()->width), - availWidth); - } else if (getStyle()->width == core::style::LENGTH_AUTO) { - totalWidth = availWidth; - forceTotalWidth = 0; - } - - _MSG(" availWidth = %d\n", availWidth); - _MSG(" totalWidth1 = %d\n", totalWidth); - - if (totalWidth < extremes.minWidth) - totalWidth = extremes.minWidth; - totalWidth = totalWidth - - (numCols + 1) * getStyle()->hBorderSpacing - - getStyle()->boxDiffWidth (); - - _MSG(" totalWidth2 = %d curCol=%d\n", totalWidth,curCol); + int availWidth = getAvailWidth (true); + int totalWidth = availWidth - + ((numCols + 1) * getStyle()->hBorderSpacing + boxDiffWidth ()); + DBG_OBJ_MSGF ("resize", 1, + "totalWidth = %d - ((%d - 1) * %d + %d) = <b>%d</b>", + availWidth, numCols, getStyle()->hBorderSpacing, + boxDiffWidth (), totalWidth); colWidths->setSize (numCols, 0); cumHeight->setSize (numRows + 1, 0); rowSpanCells->setSize (0); baseline->setSize (numRows); - _MSG(" extremes = %d,%d\n", extremes.minWidth, extremes.maxWidth); - _MSG(" getStyle()->boxDiffWidth() = %d\n", getStyle()->boxDiffWidth()); - _MSG(" getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing); + misc::SimpleVector<int> *oldColWidths = colWidths; + colWidths = new misc::SimpleVector <int> (8); + int minWidth = 0; + for (int col = 0; col < colExtremes->size(); col++) + minWidth += getColExtreme (col, MIN); + + DBG_OBJ_MSGF ("resize", 1, "minWidth (= %d) > totalWidth (= %d)?", + minWidth, totalWidth); + + if (minWidth > totalWidth) + // The sum of all column minima is larger than the available + // width, so we narrow the columns (see also CSS2 spec, + // section 17.5, #6). We use a similar apportioning, but not + // bases on minimal and maximal widths, but on intrinsic minimal + // widths and corrected minimal widths. This way, intrinsic + // extremes are preferred (so avoiding columns too narrow for + // the actual contents), at the expenses of corrected ones + // (which means that sometimes CSS values are handled + // incorrectly). + + apportion2 (totalWidth, 0, colExtremes->size() - 1, MIN_MIN, MAX_MIN, + colWidths, 0); + else { + // Normal apportioning. + int width; + if (getStyle()->width == core::style::LENGTH_AUTO) { + // Do not force width, when maximal width is smaller. + int maxWidth = 0; + for (int col = 0; col < colExtremes->size(); col++) + maxWidth += getColExtreme (col, MAX); + width = misc::min (totalWidth, maxWidth); + DBG_OBJ_MSGF ("resize", 1, "width = min (%d, %d) = %d", + totalWidth, maxWidth, width); + } else + // CSS 'width' defined: force this width. + width = totalWidth; - apportion_percentages2 (totalWidth, forceTotalWidth); - if (!hasColPercent) - apportion2 (totalWidth, forceTotalWidth); + apportion2 (width, 0, colExtremes->size() - 1, MIN, MAX, colWidths, 0); + } - setCumHeight (0, 0); - for (int row = 0; row < numRows; row++) { - /** - * \bug dw::Table::baseline is not filled. - */ - int rowHeight = 0; + // TODO: Adapted from old inline function "setColWidth". But (i) is + // this anyway correct (col width is is not x)? And does the + // performance gain actually play a role? + for (int col = 0; col < colExtremes->size(); col++) { + if (colWidths->get (col) != oldColWidths->get (col)) + redrawX = lout::misc::min (redrawX, colWidths->get (col)); + } + + DBG_IF_RTFL { + DBG_OBJ_SET_NUM ("colWidths.size", colWidths->size ()); + for (int i = 0; i < colWidths->size (); i++) + DBG_OBJ_ARRSET_NUM ("colWidths", i, colWidths->get (i)); + } - 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); + colWidthsUpToDateWidthColExtremes = true; + DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes", + colWidthsUpToDateWidthColExtremes); - 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 = misc::max (rowHeight, childHeight); - } else { - rowSpanCells->increase(); - rowSpanCells->set(rowSpanCells->size()-1, n); - } + for (int col = 0; col < numCols; col++) { + if (col >= oldColWidths->size () || col >= colWidths->size () || + oldColWidths->get (col) != colWidths->get (col)) { + // Column width has changed, tell children about this. + for (int row = 0; row < numRows; row++) { + int n = row * numCols + col; + // TODO: Columns spanning several rows are only regarded + // when the first column is affected. + if (childDefined (n)) + children->get(n)->cell.widget->containerSizeChanged (); } - }/*for col*/ + } + } + + delete oldColWidths; - setCumHeight (row + 1, - cumHeight->get (row) + rowHeight + getStyle()->vBorderSpacing); + if (calcHeights) { + 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 = misc::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 - }/*for row*/ + apportionRowSpan (); + } - apportionRowSpan (); + DBG_OBJ_LEAVE (); } void Table::apportionRowSpan () { + DBG_OBJ_ENTER0 ("resize", 0, "apportionRowSpan"); + int *rowHeight = NULL; for (int c = 0; c < rowSpanCells->size(); ++c) { @@ -630,6 +895,8 @@ void Table::apportionRowSpan () setCumHeight (i+1, cumHeight->get(i) + rowHeight[i]); } delete[] rowHeight; + + DBG_OBJ_LEAVE (); } @@ -640,8 +907,12 @@ void Table::apportionRowSpan () */ void Table::calcColumnExtremes () { + DBG_OBJ_ENTER0 ("resize", 0, "calcColumnExtremes"); + if (extremesChanged () || extremesQueued ()) forceCalcColumnExtremes (); + + DBG_OBJ_LEAVE (); } @@ -650,570 +921,282 @@ void Table::calcColumnExtremes () */ void Table::forceCalcColumnExtremes () { - _MSG(" Table::forceCalcColumnExtremes numCols=%d\n", numCols); - - if (numCols == 0) - return; + DBG_OBJ_ENTER0 ("resize", 0, "forceCalcColumnExtremes"); - 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, core::style::LENGTH_AUTO); + if (numCols > 0) { + lout::misc::SimpleVector<int> colSpanCells (8); + colExtremes->setSize (numCols); - 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 = misc::max (cellMinW, - core::style::absLengthVal(width) - pbm); - } else { - cellMinW = cellExtremes.minWidth; - cellMaxW = cellExtremes.maxWidth; + // 1. cells with colspan = 1 + for (int col = 0; col < numCols; col++) { + DBG_OBJ_MSGF ("resize", 1, "column %d", col); + DBG_OBJ_MSG_START (); + + colExtremes->getRef(col)->minWidth = 0; + colExtremes->getRef(col)->minWidthIntrinsic = 0; + colExtremes->getRef(col)->maxWidth = 0; + colExtremes->getRef(col)->maxWidthIntrinsic = 0; + + for (int row = 0; row < numRows; row++) { + DBG_OBJ_MSGF ("resize", 1, "row %d", row); + DBG_OBJ_MSG_START (); + + int n = row * numCols + col; + + if (childDefined (n)) { + if (children->get(n)->cell.colspanEff == 1) { + core::Extremes cellExtremes; + children->get(n)->cell.widget->getExtremes (&cellExtremes); + + DBG_OBJ_MSGF ("resize", 1, "child: %d / %d", + cellExtremes.minWidth, cellExtremes.maxWidth); + + colExtremes->getRef(col)->minWidthIntrinsic = + misc::max (colExtremes->getRef(col)->minWidthIntrinsic, + cellExtremes.minWidthIntrinsic); + colExtremes->getRef(col)->maxWidthIntrinsic = + misc::max (colExtremes->getRef(col)->minWidthIntrinsic, + colExtremes->getRef(col)->maxWidthIntrinsic, + cellExtremes.maxWidthIntrinsic); + + colExtremes->getRef(col)->minWidth = + misc::max (colExtremes->getRef(col)->minWidth, + cellExtremes.minWidth); + colExtremes->getRef(col)->maxWidth = + misc::max (colExtremes->getRef(col)->minWidth, + colExtremes->getRef(col)->maxWidth, + cellExtremes.maxWidth); + + DBG_OBJ_MSGF ("resize", 1, "column: %d / %d (%d / %d)", + colExtremes->getRef(col)->minWidth, + colExtremes->getRef(col)->maxWidth, + colExtremes->getRef(col)->minWidthIntrinsic, + colExtremes->getRef(col)->maxWidthIntrinsic); + } else { + colSpanCells.increase (); + colSpanCells.setLast (n); + } } - _MSG("FCCE, col%d colMin,colMax,cellMin,cellMax = %d,%d,%d,%d\n", - col, - colExtremes->getRef(col)->minWidth, - colExtremes->getRef(col)->maxWidth, - cellMinW, cellMaxW); - - colExtremes->getRef(col)->minWidth = - misc::max (colExtremes->getRef(col)->minWidth, cellMinW); - colExtremes->getRef(col)->maxWidth = - misc::max (colExtremes->getRef(col)->minWidth, misc::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) == core::style::LENGTH_AUTO) - colPercents->set(col, 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); - * - * (Hint: that's old code!) - */ - } - } else { - colSpanCells->increase(); - colSpanCells->set(colSpanCells->size()-1, n); + DBG_OBJ_MSG_END (); } - } - } - /* 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 = - misc::max (cellMinW, core::style::absLengthVal(width) - pbm); - } else { - cellMinW = cellExtremes.minWidth; - cellMaxW = cellExtremes.maxWidth; + DBG_OBJ_MSG_END (); } - int minSumCols = 0, maxSumCols = 0; - for (int i = 0; i < cs; ++i) { - minSumCols += colExtremes->getRef(col+i)->minWidth; - maxSumCols += colExtremes->getRef(col+i)->maxWidth; - } - - _MSG("cs=%d spanWidth=%d,%d sumCols=%d,%d\n", - cs,cellMinW,cellMaxW,minSumCols,maxSumCols); - if (minSumCols >= cellMinW && maxSumCols >= cellMaxW) - continue; + // 2. cells with colspan > 1 - // Cell size is too small; apportion {min,max} for this colspan. - int spanMinW = misc::max (misc::max (cs, minSumCols), - cellMinW - (cs-1) * getStyle()->hBorderSpacing), - spanMaxW = misc::max (misc::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; - } - } - - // These values 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 (core::style::isPerLength (colPercents->get(i))) { - cumSpanPercent += core::style::perLengthVal (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) { - MSG("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 (core::style::isPerLength (colPercents->get(i))) { - // multiplyWithPerLength would cause rounding errors, - // therefore the deprecated way, using perLengthVal: - wMin = misc::max (colExtremes->getRef(i)->minWidth, - (int)(availSpanMinW * - core::style::perLengthVal - (colPercents->get (i)) - / cumSpanPercent)); - colExtremes->getRef(i)->minWidth = wMin; - } - } + // TODO: Is this old comment still relevant? "If needed, here we + // set proportionally apportioned col maximums." - wMax = (goalMaxW-cumMaxWnew <= 0) ? 0 : - (int)((float)(goalMaxW-cumMaxWnew) - * colExtremes->getRef(i)->maxWidth - / (maxSumCols-cumMaxWold)); - wMax = misc::max (wMin, wMax); - cumMaxWnew += wMax; - cumMaxWold += colExtremes->getRef(i)->maxWidth; - colExtremes->getRef(i)->maxWidth = wMax; + for (int i = 0; i < colSpanCells.size(); i++) { + int n = colSpanCells.get (i); + int col = n % numCols; + int cs = children->get(n)->cell.colspanEff; - _MSG("i=%d, wMin=%d wMax=%d cumMaxWold=%d\n", - i,wMin,wMax,cumMaxWold); + core::Extremes cellExtremes; + children->get(n)->cell.widget->getExtremes (&cellExtremes); + calcExtremesSpanMulteCols (col, cs, &cellExtremes, MIN, MAX); + calcExtremesSpanMulteCols (col, cs, &cellExtremes, MIN_INTR, MAX_INTR); } -#ifdef DBG - MSG("col min,max: ["); - for (int i = 0; i < numCols; i++) - MSG("%d,%d ", - colExtremes->getRef(i)->minWidth, - colExtremes->getRef(i)->maxWidth); - MSG("]\n"); - MSG("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 - MSG("app2, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n", - availWidth, totalWidth, forceTotalWidth); - MSG("app2, extremes: ( "); - for (int i = 0; i < colExtremes->size (); i++) - MSG("%d,%d ", - colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth); - MSG(")\n"); -#endif - int minAutoWidth = 0, maxAutoWidth = 0, availAutoWidth = totalWidth; - for (int col = 0; col < numCols; col++) { - if (core::style::isAbsLength (colPercents->get(col))) { - // set absolute lengths - setColWidth (col, colExtremes->get(col).minWidth); + DBG_IF_RTFL { + DBG_OBJ_SET_NUM ("colExtremes.size", colExtremes->size ()); + for (int i = 0; i < colExtremes->size (); i++) { + DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "minWidth", + colExtremes->get(i).minWidth); + DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "minWidthIntrinsic", + colExtremes->get(i).minWidthIntrinsic); + DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "maxWidth", + colExtremes->get(i).maxWidth); + DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "maxWidthIntrinsic", + colExtremes->get(i).maxWidthIntrinsic); } - if (colPercents->get(col) == core::style::LENGTH_AUTO) { - maxAutoWidth += colExtremes->get(col).maxWidth; - minAutoWidth += colExtremes->get(col).minWidth; - } else - availAutoWidth -= colWidths->get(col); - } - - if (!maxAutoWidth) // no core::style::LENGTH_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 = misc::max (availAutoWidth, minAutoWidth); - int curExtraWidth = curTargetWidth - minAutoWidth; - int curMaxWidth = maxAutoWidth; - int curNewWidth = minAutoWidth; - for (int col = 0; col < numCols; col++) { - _MSG("app2, col %d, minWidth=%d maxWidth=%d\n", - col, colExtremes->getRef(col)->minWidth, - colExtremes->get(col).maxWidth); - - if (colPercents->get(col) != core::style::LENGTH_AUTO) - continue; - - int colMinWidth = colExtremes->getRef(col)->minWidth; - int colMaxWidth = colExtremes->getRef(col)->maxWidth; - int w = (curMaxWidth <= 0) ? 0 : - (int)((float)curTargetWidth * colMaxWidth/curMaxWidth); - - _MSG("app2, curTargetWidth=%d colMaxWidth=%d curMaxWidth=%d " - "curNewWidth=%d ", - curTargetWidth, colMaxWidth,curMaxWidth,curNewWidth); - _MSG("w = %d, ", w); - - if (w <= colMinWidth) - w = colMinWidth; - else if (curNewWidth - colMinWidth + w > curTargetWidth) - w = colMinWidth + curExtraWidth; - - _MSG("w = %d\n", w); + colWidthsUpToDateWidthColExtremes = false; + DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes", + colWidthsUpToDateWidthColExtremes); - curNewWidth -= colMinWidth; - curMaxWidth -= colMaxWidth; - curExtraWidth -= (w - colMinWidth); - curTargetWidth -= w; - setColWidth (col, w); - } -#ifdef DBG - MSG("app2, result: ( "); - for (int i = 0; i < colWidths->size (); i++) - MSG("%d ", colWidths->get (i)); - MSG(")\n"); -#endif + DBG_OBJ_LEAVE (); } -void Table::apportion_percentages2(int totalWidth, int forceTotalWidth) +void Table::calcExtremesSpanMulteCols (int col, int cs, + core::Extremes *cellExtremes, + ExtrMod minExtrMod, ExtrMod maxExtrMod) { - 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. - _MSG("APP_P, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n", - availWidth, totalWidth, forceTotalWidth); + DBG_OBJ_ENTER ("resize", 0, "calcExtremesSpanMulteCols", + "%d, %d, ..., %s, %s", + col, cs, getExtrModName (minExtrMod), + getExtrModName (maxExtrMod)); + + int cellMin = getExtreme (cellExtremes, minExtrMod); + int cellMax = getExtreme (cellExtremes, maxExtrMod); + + int minSumCols = 0, maxSumCols = 0; + + for (int j = 0; j < cs; j++) { + minSumCols += getColExtreme (col + j, minExtrMod); + maxSumCols += getColExtreme (col + j, maxExtrMod); + } - if (!hasColPercent) { -#ifdef DBG - MSG("APP_P, only a table-wide percentage\n"); - MSG("APP_P, extremes = { "); - for (int col = 0; col < numCols; col++) - MSG("%d,%d ", colExtremes->getRef(col)->minWidth, - colExtremes->getRef(col)->maxWidth); - MSG("}\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 (core::style::isAbsLength (colPercents->get(col))) - perAvailWidth -= colExtremes->getRef(col)->maxWidth; - else - sumMaxWidth += colExtremes->getRef(col)->maxWidth; + DBG_OBJ_MSGF ("resize", 1, "cs = %d, cell: %d / %d, sum: %d / %d\n", + cs, cellMin, cellMax, minSumCols, maxSumCols); + + bool changeMin = cellMin > minSumCols; + bool changeMax = cellMax > maxSumCols; + if (changeMin || changeMax) { + // TODO This differs from the documentation? Should work, anyway. + misc::SimpleVector<int> newMin, newMax; + if (changeMin) + apportion2 (cellMin, col, col + cs - 1, MIN, MAX, &newMin, 0); + if (changeMax) + apportion2 (cellMax, col, col + cs - 1, MIN, MAX, &newMax, 0); + + for (int j = 0; j < cs; j++) { + if (changeMin) + setColExtreme (col + j, minExtrMod, newMin.get (j)); + if (changeMax) + setColExtreme (col + j, maxExtrMod, newMax.get (j)); + + // For cases where min and max are somewhat confused: + setColExtreme (col + j, maxExtrMod, + misc::max (getColExtreme (col + j, minExtrMod), + getColExtreme (col + j, maxExtrMod))); } + } - _MSG("APP_P, perAvailWidth=%d, sumMaxWidth=%d\n", - perAvailWidth, sumMaxWidth); - - for (int col = 0; col < numCols; col++) { - int max_wi = colExtremes->getRef(col)->maxWidth, new_wi; - if (!core::style::isAbsLength (colPercents->get(col))) { - new_wi = - misc::max (colExtremes->getRef(col)->minWidth, - (int)((float)max_wi * perAvailWidth/sumMaxWidth)); - setColWidth (col, new_wi); - perAvailWidth -= new_wi; - sumMaxWidth -= max_wi; - } - } -#ifdef DBG - MSG("APP_P, result = { "); - for (int col = 0; col < numCols; col++) - MSG("%d ", colWidths->get(col)); - MSG("}\n"); -#endif + DBG_OBJ_LEAVE (); +} - } else { - // we'll have to apportion... - _MSG("APP_P, we'll have to apportion...\n"); +/** + * \brief Actual apportionment function. + */ +void Table::apportion2 (int totalWidth, int firstCol, int lastCol, + ExtrMod minExtrMod, ExtrMod maxExtrMod, + misc::SimpleVector<int> *dest, int destOffset) +{ + DBG_OBJ_ENTER ("resize", 0, "apportion2", "%d, %d, %d, %s, %s, ..., %d", + totalWidth, firstCol, lastCol, getExtrModName (minExtrMod), + getExtrModName (maxExtrMod), destOffset); - // 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 (core::style::isPerLength (colPercents->get(col))) { - cumPercent += core::style::perLengthVal (colPercents->get(col)); - } else { - sumMinNonPer += colExtremes->getRef(col)->minWidth; - sumMaxNonPer += colExtremes->getRef(col)->maxWidth; - if (colPercents->get(col) == core::style::LENGTH_AUTO) - hasAutoCol++; - } - sumMinWidth += colExtremes->getRef(col)->minWidth; - sumMaxWidth += colExtremes->getRef(col)->maxWidth; + if (lastCol >= firstCol) { + dest->setSize (destOffset + lastCol - firstCol + 1, 0); - _MSG("APP_P, col %d minWidth=%d maxWidth=%d\n", col, - colExtremes->getRef(col)->minWidth, - colExtremes->getRef(col)->maxWidth); + int totalMin = 0, totalMax = 0; + for (int col = firstCol; col <= lastCol; col++) { + totalMin += getColExtreme (col, minExtrMod); + totalMax += getColExtreme (col, maxExtrMod); } - 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 = misc::max - (totW, - (int)(colExtremes->getRef(col)->maxWidth - / core::style::perLengthVal (colPercents->get(col)))); - } - totalWidth = misc::min (totW, totalWidth); + + DBG_OBJ_MSGF ("resize", 1, + "totalWidth = %d, totalMin = %d, totalMax = %d", + totalWidth, totalMin, totalMax); + + // The actual calculation is rather simple, the ith value is: + // + // + // (max[i] - min[i]) * (totalMax - totalMin) + // width[i] = totalMin + ----------------------------------------- + // (totalWidth - totalMin) + // + // (Regard "total" as "sum".) With the following general + // definitions (for both the list and sums): + // + // diffExtr = max - min + // diffWidth = width - min + // + // it is simplified to: + // + // diffExtr[i] * totalDiffWidth + // diffWidth[i] = ---------------------------- + // totalDiffExtr + // + // Of course, if totalDiffExtr is 0, this is not defined; + // instead, we apportion according to the minima: + // + // min[i] * totalWidth + // width[i] = ------------------- + // totalMin + // + // Since min[i] <= max[i] for all i, totalMin == totalMax + // implies that min[i] == max[i] for all i. + // + // Third, it totalMin == 0 (which also implies min[i] = max[i] = 0), + // the result is + // + // width[i] = totalWidth / n + + int totalDiffExtr = totalMax - totalMin; + if (totalDiffExtr != 0) { + // Normal case. The algorithm described in + // "rounding-errors.doc" is used, with: + // + // x[i] = diffExtr[i] + // y[i] = diffWidth[i] + // a = totalDiffWidth + // b = totalDiffExtr + + DBG_OBJ_MSG ("resize", 1, "normal case"); + + int totalDiffWidth = totalWidth - totalMin; + int cumDiffExtr = 0, cumDiffWidth = 0; + + for (int col = firstCol; col <= lastCol; col++) { + int min = getColExtreme (col, minExtrMod); + int max = getColExtreme (col, maxExtrMod); + int diffExtr = max - min; + + cumDiffExtr += diffExtr; + int diffWidth = + (cumDiffExtr * totalDiffWidth) / totalDiffExtr - cumDiffWidth; + cumDiffWidth += diffWidth; + + dest->set (destOffset - firstCol + col, diffWidth + min); } - } - - // make sure there's enough space - totalWidth = misc::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; - } - - _MSG("APP_P, oldTotalWidth=%d totalWidth=%d" - " workingWidth=%d extraWidth=%d sumMinNonPer=%d\n", - oldTotalWidth,totalWidth,workingWidth,extraWidth,sumMinNonPer); - - for (int col = 0; col < numCols; col++) { - int colMinWidth = colExtremes->getRef(col)->minWidth; - if (core::style::isPerLength (colPercents->get(col))) { - int w = core::style::multiplyWithPerLength (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); + } else if (totalMin != 0) { + // Special case. Again, same algorithm, with + // + // x[i] = min[i] + // y[i] = width[i] + // a = totalWidth + // b = totalMin + + DBG_OBJ_MSG ("resize", 1, "special case 1"); + + int cumMin = 0, cumWidth = 0; + for (int col = firstCol; col <= lastCol; col++) { + int min = getColExtreme (col, minExtrMod); + cumMin += min; + int width = (cumMin * totalWidth) / totalMin - cumWidth; + cumWidth += width; + + dest->set (destOffset - firstCol + col, width); } - } - - if (cumPercent < 0.99f) { - // Will have to apportion the other columns -#ifdef DBG - MSG("APP_P, extremes: ( "); - for (int i = 0; i < colExtremes->size (); i++) - MSG("%d,%d ", - colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth); - MSG(")\n"); -#endif - curPerWidth -= sumMinNonPer; - int perWidth = (int)(curPerWidth/cumPercent); - totalWidth = misc::max (totalWidth, perWidth); - totalWidth = misc::min (totalWidth, oldTotalWidth); - - _MSG("APP_P, curPerWidth=%d perWidth=%d, totalWidth=%d\n", - curPerWidth, perWidth, totalWidth); - - 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 (core::style::isPerLength (colPercents->get(col))) { - // This could cause rounding errors: - // - // int d = - // core::dw::multiplyWithPerLength (extraWidth, - // colPercents->get(col)) - // / cumPercent; - // - // Thus the "old" way: - int d = - (int)(extraWidth * - core::style::perLengthVal (colPercents->get(col)) - / cumPercent); - setColWidth (col, colWidths->get(col) + d); - } - } + } else { // if (totalMin == 0) + // Last special case. Ssame algorithm, with + // + // x[i] = 1 (so cumX = i = col - firstCol + 1) + // y[i] = width[i] + // a = totalWidth + // b = n = lastCol - firstCol + 1 + + DBG_OBJ_MSG ("resize", 1, "special case 2"); + + int cumWidth = 0, n = (lastCol - firstCol + 1); + for (int col = firstCol; col <= lastCol; col++) { + int i = (col - firstCol + 1); + int width = (i * totalWidth) / n - cumWidth; + cumWidth += width; + + dest->set (destOffset - firstCol + col, width); } } -#ifdef DBG - MSG("APP_P, result ={ "); - for (int col = 0; col < numCols; col++) - MSG("%d ", colWidths->get(col)); - MSG("}\n"); -#endif - apportion2 (totalWidth, 2); - -#ifdef DBG - MSG("APP_P, percent={"); - for (int col = 0; col < numCols; col++) - MSG("%f ", core::dw::perLengthVal (colPercents->get(col))); - MSG("}\n"); - MSG("APP_P, result ={ "); - for (int col = 0; col < numCols; col++) - MSG("%d ", colWidths->get(col)); - MSG("}\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_IN_FLOW; - content.widget = table->children->get(index)->cell.widget; - } -} - -object::Object *Table::TableIterator::clone() -{ - return new TableIterator ((Table*)getWidget(), getMask(), index); -} - -int Table::TableIterator::compareTo(object::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 (in flow): - if ((getMask() & core::Content::WIDGET_IN_FLOW) == 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_IN_FLOW; - 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 (in flow): - if ((getMask() & core::Content::WIDGET_IN_FLOW) == 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_IN_FLOW; - 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. */ + + DBG_OBJ_LEAVE (); } } // namespace dw diff --git a/dw/table.hh b/dw/table.hh index 6966a163..b9dae75d 100644 --- a/dw/table.hh +++ b/dw/table.hh @@ -2,7 +2,7 @@ #define __DW_TABLE_HH__ #include "core.hh" -#include "tablecell.hh" +#include "alignedtablecell.hh" #include "../lout/misc.hh" namespace dw { @@ -10,6 +10,11 @@ namespace dw { /** * \brief A Widget for rendering tables. * + * <div style="border: 2px solid #ff0000; margin-top: 0.5em; + * margin-bottom: 0.5em; padding: 0.5em 1em; + * background-color: #ffefe0"><b>Warning:</b> Some parts of this + * description are outdated since \ref dw-grows.</div> + * * <h3>Introduction</h3> * * The dw::Table widget is used to render HTML tables. @@ -191,8 +196,9 @@ namespace dw { * * <ul> * <li> the specified absolute width of the table, when given, or - * <li> the available width (set by dw::Table::setWidth) times the specified - * percentage width of t(at max 100%), if the latter is given, or + * <li> the available width (set by dw::Table::setWidth [TODO outdated]) times + * the specified percentage width of t(at max 100%), if the latter is + * given, or * <li> otherwise the available width. * </ul> * @@ -356,7 +362,6 @@ private: friend class TableIterator; bool limitTextWidth, rowClosed; - int availWidth, availAscent, availDescent; // set by set... int numRows, numCols, curRow, curCol; lout::misc::SimpleVector<Child*> *children; @@ -383,19 +388,22 @@ private: * If a Cell has rowspan > 1, it goes into this array */ lout::misc::SimpleVector<int> *rowSpanCells; - /** - * If a Cell has colspan > 1, it goes into this array - */ - lout::misc::SimpleVector<int> *colSpanCells; lout::misc::SimpleVector<int> *baseline; lout::misc::SimpleVector<core::style::Style*> *rowStyle; - /** - * hasColPercent becomes true when any cell specifies a percentage width. - */ - int hasColPercent; - lout::misc::SimpleVector<core::style::Length> *colPercents; + bool colWidthsUpToDateWidthColExtremes; + + enum ExtrMod { MIN, MIN_INTR, MIN_MIN, MAX_MIN, MAX, MAX_INTR }; + + const char *getExtrModName (ExtrMod mod); + int getExtreme (core::Extremes *extremes, ExtrMod mod); + void setExtreme (core::Extremes *extremes, ExtrMod mod, int value); + + inline int getColExtreme (int col, ExtrMod mod) { + return getExtreme (colExtremes->getRef(col), mod); } + inline void setColExtreme (int col, ExtrMod mod, int value) { + setExtreme (colExtremes->getRef(col), mod, value); } inline bool childDefined(int n) { @@ -403,17 +411,23 @@ private: children->get(n)->type != Child::SPAN_SPACE; } + int calcAvailWidthForDescendant (Widget *child); + void reallocChildren (int newNumCols, int newNumRows); - void calcCellSizes (); - void forceCalcCellSizes (); + void calcCellSizes (bool calcHeights); + void forceCalcCellSizes (bool calcHeights); void apportionRowSpan (); void calcColumnExtremes (); void forceCalcColumnExtremes (); + void calcExtremesSpanMulteCols (int col, int cs, + core::Extremes *cellExtremes, + ExtrMod minExtrMod, ExtrMod maxExtrMod); - void apportion2 (int totalWidth, int forceTotalWidth); - void apportion_percentages2 (int totalWidth, int forceTotalWidth); + void apportion2 (int totalWidth, int firstCol, int lastCol, + ExtrMod minExtrMod, ExtrMod maxExtrMod, + lout::misc::SimpleVector<int> *dest, int destOffset); void setCumHeight (int row, int value) { @@ -423,23 +437,23 @@ private: } } - inline void setColWidth (int col, int value) - { - if (value != colWidths->get (col)) { - redrawX = lout::misc::min (redrawX, value); - colWidths->set (col, value); - } - } - protected: void sizeRequestImpl (core::Requisition *requisition); void getExtremesImpl (core::Extremes *extremes); void sizeAllocateImpl (core::Allocation *allocation); void resizeDrawImpl (); - void setWidth (int width); - void setAscent (int ascent); - void setDescent (int descent); + int getAvailWidthOfChild (Widget *child, bool forceValue); + void correctRequisitionOfChild (core::Widget *child, + core::Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + + void containerSizeChangedForChildren (); + bool affectsSizeChangeContainerChild (Widget *child); + bool usesAvailWidth (); + + bool isBlockLevel (); + void draw (core::View *view, core::Rectangle *area); //bool buttonPressImpl (core::EventButton *event); @@ -454,11 +468,14 @@ public: Table(bool limitTextWidth); ~Table(); + int applyPerWidth (int containerWidth, core::style::Length perWidth); + int applyPerHeight (int containerHeight, core::style::Length perHeight); + core::Iterator *iterator (core::Content::Type mask, bool atEnd); void addCell (Widget *widget, int colspan, int rowspan); void addRow (core::style::Style *style); - TableCell *getCellRef (); + AlignedTableCell *getCellRef (); }; } // namespace dw diff --git a/dw/table_iterator.cc b/dw/table_iterator.cc new file mode 100644 index 00000000..4da0ef4f --- /dev/null +++ b/dw/table_iterator.cc @@ -0,0 +1,134 @@ +/* + * Dillo Widget + * + * Copyright 2005-2007, 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * (This file was originally part of textblock.cc.) + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + +#include "table.hh" + +using namespace lout; + +namespace dw { + +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_IN_FLOW; + content.widget = table->children->get(index)->cell.widget; + } +} + +object::Object *Table::TableIterator::clone() +{ + return new TableIterator ((Table*)getWidget(), getMask(), index); +} + +int Table::TableIterator::compareTo(object::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 (in flow): + if ((getMask() & core::Content::WIDGET_IN_FLOW) == 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_IN_FLOW; + 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 (in flow): + if ((getMask() & core::Content::WIDGET_IN_FLOW) == 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_IN_FLOW; + 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 diff --git a/dw/tablecell.hh b/dw/tablecell.hh deleted file mode 100644 index 1e13abf9..00000000 --- a/dw/tablecell.hh +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __DW_TABLECELL_HH__ -#define __DW_TABLECELL_HH__ - -#include "core.hh" -#include "alignedtextblock.hh" - -namespace dw { - -class TableCell: public AlignedTextblock -{ -private: - int charWordIndex, charWordPos; - -protected: - int wordWrap (int wordIndex, bool wrapAll); - - int getValue (); - void setMaxValue (int maxValue, int value); - -public: - static int CLASS_ID; - - TableCell(TableCell *ref, bool limitTextWidth); - ~TableCell(); -}; - -} // namespace dw - -#endif // __DW_TABLECELL_HH__ diff --git a/dw/textblock.cc b/dw/textblock.cc index beb3f3f8..cfdb31e0 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -225,13 +225,11 @@ Textblock::Textblock (bool limitTextWidth) { DBG_OBJ_CREATE ("dw::Textblock"); registerName ("dw::Textblock", &CLASS_ID); - setFlags (BLOCK_LEVEL); - setFlags (USES_HINTS); setButtonSensitive(true); containingBlock = NULL; hasListitemValue = false; - innerPadding = 0; + leftInnerPadding = 0; line1Offset = 0; ignoreLine1OffsetSometimes = false; mustQueueResize = false; @@ -265,14 +263,9 @@ Textblock::Textblock (bool limitTextWidth) hoverLink = -1; - // random values - availWidth = 100; - availAscent = 100; - availDescent = 0; - - DBG_OBJ_SET_NUM ("availWidth", availWidth); - DBG_OBJ_SET_NUM ("availAscent", availAscent); - DBG_OBJ_SET_NUM ("availDescent", availDescent); + // -1 means undefined. + lineBreakWidth = -1; + DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth); verticalOffset = 0; DBG_OBJ_SET_NUM ("verticalOffset", verticalOffset); @@ -335,9 +328,16 @@ Textblock::~Textblock () */ void Textblock::sizeRequestImpl (core::Requisition *requisition) { - DBG_OBJ_MSG ("resize", 0, "<b>sizeRequestImpl</b> ()"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl"); + int newLineBreakWidth = getAvailWidth (true); + if (newLineBreakWidth != lineBreakWidth) { + lineBreakWidth = newLineBreakWidth; + wrapRefLines = 0; + DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth); + DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines); + } + rewrap (); showMissingLines (); @@ -369,18 +369,33 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) requisition->descent = 0; } - DBG_OBJ_MSGF ("resize", 1, "inner padding = %d, boxDiffWidth = %d", - innerPadding, getStyle()->boxDiffWidth ()); + DBG_OBJ_MSGF ("resize", 1, "left inner padding = %d, boxDiffWidth = %d", + leftInnerPadding, getStyle()->boxDiffWidth ()); - requisition->width += innerPadding + getStyle()->boxDiffWidth (); + requisition->width += leftInnerPadding + getStyle()->boxDiffWidth (); requisition->ascent += verticalOffset + getStyle()->boxOffsetY (); requisition->descent += getStyle()->boxRestHeight (); + DBG_OBJ_MSGF ("resize", 1, "before correction: %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + + correctRequisition (requisition, core::splitHeightPreserveAscent); + // Dealing with parts out of flow, which may overlap the borders of // the text block. Base lines are ignored here: they do not play a // role (currently) and caring about them (for the future) would // cause too much problems. + // Notice that the order is not typical: correctRequisition should + // be the last call. However, calling correctRequisition after + // outOfFlowMgr->getSize may result again in a size which is too + // small for floats, so triggering again (and again) the resize + // idle function resulting in CPU hogging. See also + // getExtremesImpl. + // + // Is this really what we want? An alternative could be that + // OutOfFlowMgr::getSize honours CSS attributes an corrected sizes. + DBG_OBJ_MSGF ("resize", 1, "before considering OOF widgets: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); @@ -392,21 +407,9 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) requisition->descent = oofHeight - requisition->ascent; } - DBG_OBJ_MSGF ("resize", 1, - "before considering availWidth (= %d): %d * (%d + %d)", - availWidth, requisition->width, requisition->ascent, - requisition->descent); - - if (requisition->width < availWidth) { - requisition->width = availWidth; - DBG_OBJ_MSGF ("resize", 1, "adjusting to availWidth => %d", - requisition->width); - } - - DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + DBG_OBJ_MSGF ("resize", 1, "final: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); - - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -414,88 +417,95 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) */ void Textblock::getWordExtremes (Word *word, core::Extremes *extremes) { - if (word->content.type == core::Content::WIDGET_IN_FLOW) { - if (word->content.widget->usesHints ()) { - word->content.widget->getExtremes (extremes); - - if (core::style::isAbsLength (word->content.widget - ->getStyle()->width)) { - int width = - core::style::absLengthVal (word->content.widget - ->getStyle()->width); - if (extremes->minWidth < width) - extremes->minWidth = width; - if (extremes->maxWidth > width) - // maxWidth not smaller than minWidth - extremes->maxWidth = misc::max (width, extremes->minWidth); - } - } else { - if (core::style::isPerLength - (word->content.widget->getStyle()->width)) { - extremes->minWidth = 0; - if (word->content.widget->hasContents ()) - extremes->maxWidth = 1000000; - else - extremes->maxWidth = 0; - } else if (core::style::isAbsLength - (word->content.widget->getStyle()->width)) { - /* Fixed lengths are only applied to the content, so we have to - * add padding, border and margin. */ - extremes->minWidth = extremes->maxWidth = - core::style::absLengthVal (word->content.widget->getStyle() - ->width) - + word->style->boxDiffWidth (); - } else - word->content.widget->getExtremes (extremes); - } - } else { - extremes->minWidth = word->size.width; - extremes->maxWidth = word->size.width; - } + if (word->content.type == core::Content::WIDGET_IN_FLOW) + word->content.widget->getExtremes (extremes); + else + extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth = + extremes->maxWidthIntrinsic = word->size.width; } void Textblock::getExtremesImpl (core::Extremes *extremes) { - DBG_OBJ_MSG ("resize", 0, "<b>getExtremesImpl</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "getExtremesImpl"); + + // TODO Can extremes depend on the available width? Should not; if + // they do, the following code must be reactivated, but it causes + // an endless recursion. +#if 0 + int newLineBreakWidth = getAvailWidth (true); + if (newLineBreakWidth != lineBreakWidth) { + lineBreakWidth = newLineBreakWidth; + wrapRefParagraphs = 0; + DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth); + DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefLines); + } +#endif fillParagraphs (); if (paragraphs->size () == 0) { /* empty page */ extremes->minWidth = 0; + extremes->minWidthIntrinsic = 0; extremes->maxWidth = 0; + extremes->maxWidthIntrinsic = 0; } else { Paragraph *lastPar = paragraphs->getLastRef (); extremes->minWidth = lastPar->maxParMin; + extremes->minWidthIntrinsic = lastPar->maxParMinIntrinsic; extremes->maxWidth = lastPar->maxParMax; + extremes->maxWidthIntrinsic = lastPar->maxParMaxIntrinsic; - DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMin = %d", - paragraphs->size () - 1, lastPar->maxParMin); - DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMax = %d", - paragraphs->size () - 1, lastPar->maxParMax); + DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMin = %d (%d)", + paragraphs->size () - 1, lastPar->maxParMin, + lastPar->maxParMinIntrinsic); + DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMax = %d (%d)", + paragraphs->size () - 1, lastPar->maxParMax, + lastPar->maxParMaxIntrinsic); } - int diff = innerPadding + getStyle()->boxDiffWidth (); + DBG_OBJ_MSGF ("resize", 0, "after considering paragraphs: %d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + int diff = leftInnerPadding + getStyle()->boxDiffWidth (); extremes->minWidth += diff; + extremes->minWidthIntrinsic += diff; extremes->maxWidth += diff; + extremes->maxWidthIntrinsic += diff; + + DBG_OBJ_MSGF ("resize", 0, "after adding diff: %d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + // For the order, see similar reasoning in sizeRequestImpl. + + correctExtremes (extremes); + + DBG_OBJ_MSGF ("resize", 0, "after correction: %d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); if (outOfFlowMgr) { int oofMinWidth, oofMaxWidth; outOfFlowMgr->getExtremes (extremes, &oofMinWidth, &oofMaxWidth); - DBG_OBJ_MSGF ("resize", 1, "extremes: %d / %d, corrected: %d / %d", - extremes->minWidth, extremes->maxWidth, + DBG_OBJ_MSGF ("resize", 1, "OOFM correction: %d / %d", oofMinWidth, oofMaxWidth); extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth); + extremes->minWidthIntrinsic = + misc::max (extremes->minWidthIntrinsic, oofMinWidth); extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth); + extremes->maxWidthIntrinsic = + misc::max (extremes->maxWidthIntrinsic, oofMinWidth); } - DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", - extremes->minWidth, extremes->maxWidth); - - DBG_OBJ_MSG_END (); + DBG_OBJ_MSGF ("resize", 0, + "finally, after considering OOFM: %d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + DBG_OBJ_LEAVE (); } @@ -523,8 +533,13 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) } for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) { + // Especially for floats, allocation->width may be different + // from the line break width, so that for centered and right + // text, the offsets have to be recalculated again. + calcTextOffset (lineIndex, allocation->width); + line = lines->getRef (lineIndex); - xCursor = line->offsetCompleteWidget; + xCursor = line->textOffset; for (wordIndex = line->firstWord; wordIndex <= line->lastWord; wordIndex++) { @@ -632,10 +647,30 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) } } +void Textblock::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + for (int i = 0; i < words->size (); i++) { + Word *word = words->getRef (i); + if (word->content.type == core::Content::WIDGET_IN_FLOW) + word->content.widget->containerSizeChanged (); + } + + if (outOfFlowMgr) + outOfFlowMgr->containerSizeChangedForChildren (); + + DBG_OBJ_LEAVE (); +} + +bool Textblock::usesAvailWidth () +{ + return true; +} + void Textblock::resizeDrawImpl () { - DBG_OBJ_MSG ("draw", 0, "<b>resizeDrawImpl</b> ()"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("draw", 0, "resizeDrawImpl"); queueDrawArea (0, redrawY, allocation.width, getHeight () - redrawY); if (lines->size () > 0) { @@ -650,13 +685,12 @@ void Textblock::resizeDrawImpl () redrawY = getHeight (); DBG_OBJ_SET_NUM ("redrawY", redrawY); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Textblock::markSizeChange (int ref) { - DBG_OBJ_MSGF ("resize", 0, "<b>markSizeChange</b> (%d)", ref); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 0, "markSizeChange", "%d", ref); if (OutOfFlowMgr::isRefOutOfFlow (ref)) { assert (outOfFlowMgr != NULL); @@ -688,13 +722,12 @@ void Textblock::markSizeChange (int ref) markExtremesChange (ref); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Textblock::markExtremesChange (int ref) { - DBG_OBJ_MSGF ("resize", 1, "<b>markExtremesChange</b> (%d)", ref); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 1, "markExtremesChange", "%d", ref); if (OutOfFlowMgr::isRefOutOfFlow (ref)) { assert (outOfFlowMgr != NULL); @@ -720,7 +753,7 @@ void Textblock::markExtremesChange (int ref) DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Textblock::notifySetAsTopLevel() @@ -773,53 +806,9 @@ void Textblock::notifySetParent () assert (containingBlock != NULL); } -void Textblock::setWidth (int width) +bool Textblock::isBlockLevel () { - /* If limitTextWidth is set to YES, a queueResize() may also be - * necessary. */ - if (availWidth != width || limitTextWidth) { - DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width); - DBG_OBJ_MSG_START (); - - availWidth = width; - DBG_OBJ_SET_NUM ("availWidth", availWidth); - queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); - mustQueueResize = false; - redrawY = 0; - DBG_OBJ_SET_NUM ("redrawY", redrawY); - - DBG_OBJ_MSG_END (); - } -} - -void Textblock::setAscent (int ascent) -{ - if (availAscent != ascent) { - DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent); - DBG_OBJ_MSG_START (); - - availAscent = ascent; - DBG_OBJ_SET_NUM ("availAscent", availAscent); - queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); - mustQueueResize = false; - - DBG_OBJ_MSG_END (); - } -} - -void Textblock::setDescent (int descent) -{ - if (availDescent != descent) { - DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent); - DBG_OBJ_MSG_START (); - - availDescent = descent; - DBG_OBJ_SET_NUM ("availDescent", availDescent); - queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); - mustQueueResize = false; - - DBG_OBJ_MSG_END (); - } + return true; } bool Textblock::buttonPressImpl (core::EventButton *event) @@ -935,11 +924,11 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, // Choose this break. wordIndex = line->lastWord; charPos = core::SelectionState::END_OF_WORD; - } else if (event->xWidget < line->offsetCompleteWidget) { + } else if (event->xWidget < line->textOffset) { // Left of the first word in the line. wordIndex = line->firstWord; } else { - int nextWordStartX = line->offsetCompleteWidget; + int nextWordStartX = line->textOffset; for (wordIndex = line->firstWord; wordIndex <= line->lastWord; @@ -1052,93 +1041,24 @@ core::Iterator *Textblock::iterator (core::Content::Type mask, bool atEnd) return new TextblockIterator (this, mask, atEnd); } - /** * Calculate the size of a widget within the page. - * (Subject of change in the near future!) */ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size) { - DBG_OBJ_MSGF ("resize", 0, "<b>calcWidgetSize</b> (%p, ...)", widget); - - core::Requisition requisition; - int availWidth, availAscent, availDescent; - core::style::Style *wstyle = widget->getStyle(); + DBG_OBJ_ENTER ("resize", 0, "calcWidgetSize", "%p, ...", widget); - /* We ignore line1_offset[_eff]. */ - availWidth = this->availWidth - getStyle()->boxDiffWidth () - innerPadding; - availAscent = this->availAscent - getStyle()->boxDiffHeight (); - availDescent = this->availDescent; - - if (widget->usesHints ()) { - // This is a simplified version of calcAvailWidth (see there for - // more details). Until recently, the *attribute* availWidth was - // used, widthout any corrections. To limit the damage, only - // includde left and right border (by floats), until the Great - // Redesign Of Widget Sizes (GROWS). - int corrAvailWidth; - // Textblocks keep track of borders themselves, so they get the - // total available width. (Should once replaced by something - // like OOFAware.) - if (widget->instanceOf (Textblock::CLASS_ID)) - corrAvailWidth = availWidth; - else - corrAvailWidth = - misc::max (availWidth - (newLineLeftBorder + newLineRightBorder), - 0); - - DBG_OBJ_MSGF ("resize", 1, "setting hints: %d, %d, %d", - corrAvailWidth, availAscent, availDescent); - widget->setWidth (corrAvailWidth); - widget->setAscent (availAscent); - widget->setDescent (availDescent); - widget->sizeRequest (size); - DBG_OBJ_MSGF ("resize", 1, "sizeRequest => %d * (%d + %d)", - size->width, size->ascent, size->descent); - } else { - if (wstyle->width == core::style::LENGTH_AUTO || - wstyle->height == core::style::LENGTH_AUTO) { - widget->sizeRequest (&requisition); - DBG_OBJ_MSGF ("resize", 1, "AUTO; sizeRequest => %d * (%d + %d)", - requisition.width, requisition.ascent, - requisition.descent); - } + widget->sizeRequest (size); - if (wstyle->width == core::style::LENGTH_AUTO) - size->width = requisition.width; - else if (core::style::isAbsLength (wstyle->width)) - /* Fixed lengths are only applied to the content, so we have to - * add padding, border and margin. */ - size->width = core::style::absLengthVal (wstyle->width) - + wstyle->boxDiffWidth (); - else - size->width = - core::style::multiplyWithPerLength (availWidth, wstyle->width); - - if (wstyle->height == core::style::LENGTH_AUTO) { - size->ascent = requisition.ascent; - size->descent = requisition.descent; - } else if (core::style::isAbsLength (wstyle->height)) { - /* Fixed lengths are only applied to the content, so we have to - * add padding, border and margin. */ - size->ascent = core::style::absLengthVal (wstyle->height) - + wstyle->boxDiffHeight (); - size->descent = 0; - } else { - size->ascent = - core::style::multiplyWithPerLength (wstyle->height, availAscent); - size->descent = - core::style::multiplyWithPerLength (wstyle->height, availDescent); - } - } - - /* ascent and descent in words do not contain margins. */ + // Ascent and descent in words do not contain margins. + // TODO: Re-evaluate (GROWS)! + core::style::Style *wstyle = widget->getStyle(); size->ascent -= wstyle->margin.top; size->descent -= wstyle->margin.bottom; DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", size->width, size->ascent, size->descent); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /* @@ -1450,11 +1370,10 @@ void Textblock::drawSpace(int wordIndex, core::View *view, */ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) { - DBG_OBJ_MSGF ("draw", 0, "<b>drawLine</b> (..., %d, %d, %d * %d)", - area->x, area->y, area->width, area->height); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("draw", 0, "drawLine", "..., %d, %d, %d * %d", + area->x, area->y, area->width, area->height); - int xWidget = line->offsetCompleteWidget; + int xWidget = line->textOffset; int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; DBG_OBJ_MSGF ("draw", 1, "line from %d to %d (%d words), at (%d, %d)", @@ -1516,7 +1435,7 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) xWidget += wordSize + word->effSpace; } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -1647,7 +1566,7 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) if (yWidgetBase + line->boxDescent <= y) return NULL; - xCursor = line->offsetCompleteWidget; + xCursor = line->textOffset; for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { word = words->getRef (wordIndex); lastXCursor = xCursor; @@ -1676,9 +1595,8 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) void Textblock::draw (core::View *view, core::Rectangle *area) { - DBG_OBJ_MSGF ("draw", 0, "<b>draw</b> (%d, %d, %d * %d)", - area->x, area->y, area->width, area->height); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); int lineIndex; Line *line; @@ -1707,7 +1625,7 @@ void Textblock::draw (core::View *view, core::Rectangle *area) if(outOfFlowMgr) outOfFlowMgr->draw(view, area); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -1716,9 +1634,13 @@ void Textblock::draw (core::View *view, core::Rectangle *area) Textblock::Word *Textblock::addWord (int width, int ascent, int descent, short flags, core::style::Style *style) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>addWord</b> (%d * (%d + %d), %d, %p)", - width, ascent, descent, flags, style); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "addWord", "%d * (%d + %d), %d, %p", + width, ascent, descent, flags, style); + + if (lineBreakWidth == -1) { + lineBreakWidth = getAvailWidth (true); + DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth); + } words->increase (); DBG_OBJ_SET_NUM ("words.size", words->size ()); @@ -1726,7 +1648,7 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent, initWord (wordNo); fillWord (wordNo, width, ascent, descent, flags, style); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return words->getRef (wordNo); } @@ -1957,9 +1879,8 @@ void Textblock::calcTextSize (const char *text, size_t len, void Textblock::addText (const char *text, size_t len, core::style::Style *style) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>addText</b> (..., %d, %p)", - (int)len, style); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "addText", "..., %d, %p", + (int)len, style); // Count dividing characters. int numParts = 1; @@ -2166,7 +2087,7 @@ void Textblock::addText (const char *text, size_t len, } } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Textblock::calcTextSizes (const char *text, size_t textLen, @@ -2209,20 +2130,18 @@ void Textblock::calcTextSizes (const char *text, size_t textLen, void Textblock::addText0 (const char *text, size_t len, short flags, core::style::Style *style, core::Requisition *size) { - DBG_OBJ_MSGF ("construct.word", 0, - "<b>addText0</b> (..., %d, %s:%s:%s:%s:%s:%s:%s, %p, " - "%d * (%d + %d)", - (int)len, - // Ugly copy&paste from printWordFlags: - (flags & Word::CAN_BE_HYPHENATED) ? "h?" : "--", - (flags & Word::DIV_CHAR_AT_EOL) ? "de" : "--", - (flags & Word::PERM_DIV_CHAR) ? "dp" : "--", - (flags & Word::DRAW_AS_ONE_TEXT) ? "t1" : "--", - (flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) ? "um" : "--", - (flags & Word::WORD_START) ? "st" : "--", - (flags & Word::WORD_END) ? "en" : "--", - style, size->width, size->ascent, size->descent); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "addText0", + "..., %d, %s:%s:%s:%s:%s:%s:%s, %p, %d * (%d + %d)", + (int)len, + // Ugly copy&paste from printWordFlags: + (flags & Word::CAN_BE_HYPHENATED) ? "h?" : "--", + (flags & Word::DIV_CHAR_AT_EOL) ? "de" : "--", + (flags & Word::PERM_DIV_CHAR) ? "dp" : "--", + (flags & Word::DRAW_AS_ONE_TEXT) ? "t1" : "--", + (flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) ? "um" : "--", + (flags & Word::WORD_START) ? "st" : "--", + (flags & Word::WORD_END) ? "en" : "--", + style, size->width, size->ascent, size->descent); //printf("[%p] addText0 ('", this); //for (size_t i = 0; i < len; i++) @@ -2247,7 +2166,7 @@ void Textblock::addText0 (const char *text, size_t len, short flags, processWord (words->size () - 1); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -2255,9 +2174,7 @@ void Textblock::addText0 (const char *text, size_t len, short flags, */ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>addWidget</b> (%p, %p)", - widget, style); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "addWidget", "%p, %p", widget, style); /* We first assign -1 as parent_ref, since the call of widget->size_request * will otherwise let this Textblock be rewrapped from the beginning. @@ -2317,7 +2234,7 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) // "in page with %d word(s)\n", // lines->size () - 1, words->size() - 1, words->size()); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -2329,9 +2246,7 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) */ bool Textblock::addAnchor (const char *name, core::style::Style *style) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>addAnchor</b> (\"%s\", %p)", - name, style); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "addAnchor", "\"%s\", %p", name, style); char *copy; int y; @@ -2365,7 +2280,7 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style) } DBG_OBJ_MSGF ("construct.word", 0, "=> %s", result ? "true" : "false"); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return result; } @@ -2375,8 +2290,7 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style) */ void Textblock::addSpace (core::style::Style *style) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>addSpace</b> (%p)", style); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "addSpace", "%p", style); int wordIndex = words->size () - 1; if (wordIndex >= 0) { @@ -2386,7 +2300,7 @@ void Textblock::addSpace (core::style::Style *style) correctLastWordExtremes (); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -2397,9 +2311,8 @@ void Textblock::addSpace (core::style::Style *style) */ void Textblock::addBreakOption (core::style::Style *style, bool forceBreak) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>addBreakOption</b> (%p, %s)", - style, forceBreak ? "true" : "false"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "addBreakOption", "%p, %s", + style, forceBreak ? "true" : "false"); int wordIndex = words->size () - 1; if (wordIndex >= 0) { @@ -2409,13 +2322,12 @@ void Textblock::addBreakOption (core::style::Style *style, bool forceBreak) correctLastWordExtremes (); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Textblock::fillSpace (int wordNo, core::style::Style *style) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>fillSpace</b> (%d, ...)", wordNo); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "fillSpace", "%d, ...", wordNo); DBG_OBJ_MSGF ("construct.word", 1, "style.white-space = %s", style->whiteSpace == core::style::WHITE_SPACE_NORMAL ? "normal" @@ -2464,7 +2376,7 @@ void Textblock::fillSpace (int wordNo, core::style::Style *style) setSpaceImgRenderer (wordNo); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -2476,9 +2388,8 @@ void Textblock::setBreakOption (Word *word, core::style::Style *style, int breakPenalty1, int breakPenalty2, bool forceBreak) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>setBreakOption</b> (..., %d, %d, %s)", - breakPenalty1, breakPenalty2, forceBreak ? "true" : "false"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "setBreakOption", "..., %d, %d, %s", + breakPenalty1, breakPenalty2, forceBreak ? "true" : "false"); // TODO: lineMustBeBroken should be independent of the penalty // index? Otherwise, examine the last line. @@ -2489,7 +2400,7 @@ void Textblock::setBreakOption (Word *word, core::style::Style *style, word->badnessAndPenalty.setPenalty (PENALTY_PROHIBIT_BREAK); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } bool Textblock::isBreakAllowed (core::style::Style *style) @@ -2517,9 +2428,11 @@ bool Textblock::isBreakAllowed (core::style::Style *style) */ void Textblock::addParbreak (int space, core::style::Style *style) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>addParbreak</b> (%d, %p)", - space, style); - DBG_OBJ_MSG ("construct.word", 0, "<i>no nesting!</i>"); + DBG_OBJ_ENTER ("construct.word", 0, "addParbreak", "%d, %p", + space, style); + DBG_OBJ_MSG ("construct.word", 0, + "<i>No nesting! Strack trace may be incomplete.</i>"); + DBG_OBJ_LEAVE (); Word *word; @@ -2604,8 +2517,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) */ void Textblock::addLinebreak (core::style::Style *style) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>addLinebreak</b> (%p)", style); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "addLinebreak", "%p", style); Word *word; @@ -2630,7 +2542,7 @@ void Textblock::addLinebreak (core::style::Style *style) breakAdded (); processWord (words->size () - 1); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -2753,15 +2665,16 @@ void Textblock::handOverBreak (core::style::Style *style) */ void Textblock::flush () { + DBG_OBJ_ENTER0 ("resize", 0, "flush"); + if (mustQueueResize) { - DBG_OBJ_MSG ("resize", 0, "<b>flush</b> (mustQueueResize set)"); - DBG_OBJ_MSG_START (); + DBG_OBJ_MSG ("resize", 0, "mustQueueResize set"); queueResize (-1, true); mustQueueResize = false; - - DBG_OBJ_MSG_END (); } + + DBG_OBJ_LEAVE (); } @@ -2850,8 +2763,7 @@ void Textblock::queueDrawRange (int index1, int index2) void Textblock::setVerticalOffset (int verticalOffset) { - DBG_OBJ_MSGF ("resize", 0, "<b>setVerticalOffset</b> (%d)", verticalOffset); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 0, "setVerticalOffset", "%d", verticalOffset); if (this->verticalOffset != verticalOffset) { this->verticalOffset = verticalOffset; @@ -2860,7 +2772,7 @@ void Textblock::setVerticalOffset (int verticalOffset) queueDraw (); // Could perhaps be optimized. } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -2873,8 +2785,7 @@ void Textblock::setVerticalOffset (int verticalOffset) */ void Textblock::borderChanged (int y, Widget *vloat) { - DBG_OBJ_MSGF ("resize", 0, "<b>borderChanged</b> (%d, %p)", y, vloat); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 0, "borderChanged", "%d, %p", y, vloat); int lineIndex = findLineIndex (y); DBG_OBJ_MSGF ("resize", 1, "Line index: %d (of %d).", @@ -2893,7 +2804,7 @@ void Textblock::borderChanged (int y, Widget *vloat) DBG_OBJ_MSGF ("resize", 1, "Rewrapping from line %d (of %d).", wrapLineIndex, lines->size ()); - if (vloat->getGenerator() == this) { + if (vloat->getGenerator() == this && lines->size () > 0) { bool found = false; // Sometimes, the respective word is not yet part of a // line. Nothing to do, but because of the assertion below @@ -2953,6 +2864,14 @@ void Textblock::borderChanged (int y, Widget *vloat) exceeds = exceedsBeginning = lineIndex2 < 0; } + DBG_OBJ_MSGF ("resize", 2, + "lineIndex2 = %d (of %d), exceeds = %s, " + "exceedsBeginning = %s, exceedsEnd = %s", + lineIndex2, lines->size (), + exceeds ? "true" : "false", + exceedsBeginning ? "true" : "false", + exceedsEnd ? "true" : "false"); + if (exceedsBeginning && exceedsEnd) break; @@ -2993,7 +2912,7 @@ void Textblock::borderChanged (int y, Widget *vloat) //DBG_OBJ_SET_NUM ("redrawY", redrawY); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Textblock::printBorderChangedErrorAndAbort (int y, Widget *vloat, @@ -3096,10 +3015,8 @@ Textblock *Textblock::getTextblockForLine (int firstWord, int lastWord) */ int Textblock::yOffsetOfPossiblyMissingLine (int lineNo) { - DBG_OBJ_MSGF ("line.yoffset", 0, - "<b>yOffsetOfPossiblyMissingLine</b> (%d <i>of %d</i>)", - lineNo, lines->size()); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("line.yoffset", 0, "yOffsetOfPossiblyMissingLine", + "%d <i>of %d</i>", lineNo, lines->size()); int result; @@ -3119,17 +3036,15 @@ int Textblock::yOffsetOfPossiblyMissingLine (int lineNo) prevLine->breakSpace, result); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return result; } int Textblock::heightOfPossiblyMissingLine (int lineNo) { - DBG_OBJ_MSGF ("line.height", 0, - "<b>heightOfPossiblyMissingLine</b> (%d <i>of %d</i>)", - lineNo, lines->size()); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("line.height", 0, "heightOfPossiblyMissingLine", + "%d <i>of %d</i>", lineNo, lines->size()); int result; @@ -3166,7 +3081,7 @@ int Textblock::heightOfPossiblyMissingLine (int lineNo) result = 1; DBG_OBJ_MSGF ("line.height", 0, "result = %d", result); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return result; } diff --git a/dw/textblock.hh b/dw/textblock.hh index 3254b843..5676f718 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -130,7 +130,7 @@ namespace dw { * widget: * * <ul> - * <li> The available size of the widget has changed, e.g., because the + * <li> The line break size of the widget has changed, e.g., because the * user has changed the size of the browser window. In this case, * it is necessary to rewrap all the lines. * @@ -306,14 +306,18 @@ protected: int parMin; /* The sum of all word minima (plus spaces, hyphen width etc.) since the last possible break within this paragraph. */ + int parMinIntrinsic; int parMax; /* The sum of all word maxima in this paragraph (plus spaces, hyphen width etc.). */ + int parMaxIntrinsic; int maxParMin; /* Maximum of all paragraph minima (value of "parMin"), including this paragraph. */ + int maxParMinIntrinsic; int maxParMax; /* Maximum of all paragraph maxima (value of "parMax""), including this paragraph. */ + int maxParMaxIntrinsic; }; struct Line @@ -334,8 +338,7 @@ protected: int contentAscent; /* ??? */ int contentDescent; /* ??? */ int breakSpace; /* Space between this line and the next one. */ - int leftOffset; /* ??? */ - int offsetCompleteWidget; /* ??? */ + int textOffset; /* ??? */ /* This is similar to descent, but includes the bottom margins of the * widgets within this line. */ @@ -361,6 +364,9 @@ protected: * even a following line, when positioned before (this is the * reason this attribute exists); see \ref dw-out-of-flow. */ int lastOofRefPositionedBeforeThisLine; + + int leftOffset, rightOffset; + enum { LEFT, RIGHT, CENTER } alignment; }; struct Word @@ -472,7 +478,7 @@ protected: /* These fields provide some ad-hoc-functionality, used by sub-classes. */ bool hasListitemValue; /* If true, the first word of the page is treated specially (search in source). */ - int innerPadding; /* This is an additional padding on the left side + int leftInnerPadding; /* This is an additional padding on the left side (used by ListItem). */ int line1Offset; /* This is an additional offset of the first line. May be negative (shift to left) or positive @@ -489,7 +495,7 @@ protected: * (which is used by DwTable!), and * (ii) line1_offset is ignored (line1_offset_eff is set to 0), * when line1_offset plus the width of the first word is - * greater than the the available witdh. + * greater than the the line break witdh. * * \todo Eliminate all these ad-hoc features by a new, simpler and * more elegant design. ;-) @@ -516,8 +522,8 @@ protected: int redrawY; int lastWordDrawn; - /* These values are set by set_... */ - int availWidth, availAscent, availDescent; + /* This value is (currently) set by setAscent(). */ + int lineBreakWidth; // Additional vertical offset, used for the "clear" attribute. int verticalOffset; @@ -702,8 +708,6 @@ protected: bool sendSelectionEvent (core::SelectionState::EventType eventType, core::MousePositionEvent *event); - void accumulateWordExtremes (int firstWord, int lastWord, - int *maxOfMinWidth, int *sumOfMaxWidth); void processWord (int wordIndex); virtual int wordWrap (int wordIndex, bool wrapAll); int wrapWordInFlow (int wordIndex, bool wrapAll); @@ -737,22 +741,26 @@ protected: void moveWordIndices (int wordIndex, int num, int *addIndex1 = NULL); void accumulateWordForLine (int lineIndex, int wordIndex); void accumulateWordData (int wordIndex); - int calcAvailWidth (int lineIndex); + int calcLineBreakWidth (int lineIndex); void initLine1Offset (int wordIndex); void alignLine (int lineIndex); + void calcTextOffset (int lineIndex, int totalWidth); void sizeRequestImpl (core::Requisition *requisition); void getExtremesImpl (core::Extremes *extremes); void sizeAllocateImpl (core::Allocation *allocation); + void containerSizeChangedForChildren (); + bool usesAvailWidth (); void resizeDrawImpl (); void markSizeChange (int ref); void markExtremesChange (int ref); + void notifySetAsTopLevel(); void notifySetParent(); - void setWidth (int width); - void setAscent (int ascent); - void setDescent (int descent); + + bool isBlockLevel (); + void draw (core::View *view, core::Rectangle *area); bool buttonPressImpl (core::EventButton *event); @@ -808,15 +816,13 @@ public: void borderChanged (int y, core::Widget *vloat); inline void oofSizeChanged (bool extremesChanged) { - DBG_OBJ_MSGF ("resize", 0, "<b>oofSizeChanged</b> (%s)", - extremesChanged ? "true" : "false"); + DBG_OBJ_ENTER ("resize", 0, "oofSizeChanged", "%s", + extremesChanged ? "true" : "false"); DBG_OBJ_MSG_START (); queueResize (-1, extremesChanged); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } - inline int getAvailWidth () { return availWidth; } - inline int getAvailAscent () { return availAscent; } - inline int getAvailDescent () { return availDescent; } + inline int getLineBreakWidth () { return lineBreakWidth; } }; #define DBG_SET_WORD_PENALTY(n, i, is) \ diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc index c26b7f6e..56ad6e15 100644 --- a/dw/textblock_iterator.cc +++ b/dw/textblock_iterator.cc @@ -289,7 +289,7 @@ void Textblock::TextblockIterator::getAllocation (int start, int end, Word *word = textblock->words->getRef (index); allocation->x = - textblock->allocation.x + line->offsetCompleteWidget; + textblock->allocation.x + line->textOffset; for (int i = line->firstWord; i < index; i++) { Word *w = textblock->words->getRef(i); diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc index a773f24c..0e492df9 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -92,7 +92,7 @@ void Textblock::BadnessAndPenalty::calcBadness (int totalWidth, int idealWidth, badness = ratio * ratio * ratio; } } - } else { // if (totalWidth > availWidth) + } else { // if (totalWidth > idealWidth) if (totalShrinkability == 0) badnessState = TOO_TIGHT; else { @@ -290,8 +290,7 @@ void Textblock::printWord (Word *word) */ void Textblock::justifyLine (Line *line, int diff) { - DBG_OBJ_MSGF ("construct.line", 0, "<b>justifyLine</b> (..., %d)", diff); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.line", 0, "justifyLine", "..., %d", diff); // To avoid rounding errors, the calculation is based on accumulated // values. See doc/rounding-errors.doc. @@ -342,16 +341,16 @@ void Textblock::justifyLine (Line *line, int diff) } } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } Textblock::Line *Textblock::addLine (int firstWord, int lastWord, int newLastOofPos, bool temporary) { - DBG_OBJ_MSGF ("construct.line", 0, "<b>addLine</b> (%d, %d) => %d", - firstWord, lastWord, lines->size ()); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.line", 0, "addLine", "%d, %d", + firstWord, lastWord); + DBG_OBJ_MSGF ("construct.line", 0, "=> %d", lines->size ()); int lineWidth; if (lastWord >= firstWord) { @@ -378,10 +377,6 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, lineWidth); } - int maxOfMinWidth, sumOfMaxWidth; - accumulateWordExtremes (firstWord, lastWord, &maxOfMinWidth, - &sumOfMaxWidth); - lines->increase (); DBG_OBJ_SET_NUM ("lines.size", lines->size ()); @@ -403,10 +398,17 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, line->boxDescent = line->contentDescent = 0; line->marginDescent = 0; line->breakSpace = 0; - line->leftOffset = 0; line->finished = false; + + bool regardBorder = mustBorderBeRegarded (line); + line->leftOffset = misc::max (regardBorder ? newLineLeftBorder : 0, + boxOffsetX () + leftInnerPadding + + (lineIndex == 0 ? line1OffsetEff : 0)); + line->rightOffset = misc::max (regardBorder ? newLineRightBorder : 0, + boxRestWidth ()); alignLine (lineIndex); + calcTextOffset (lineIndex, lineBreakWidth); for (int i = line->firstWord; i < line->lastWord; i++) { Word *word = words->getRef (i); @@ -438,14 +440,6 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, // the height should be positive. line->boxAscent = misc::max (line->boxAscent, 1); - // Calculate offsetCompleteWidget, which includes also floats. - int leftBorder = mustBorderBeRegarded (line) ? newLineLeftBorder : 0; - line->offsetCompleteWidget = - misc::max (leftBorder, - getStyle()->boxOffsetX() + innerPadding - + (lineIndex == 0 ? line1OffsetEff : 0)) - + line->leftOffset; - DBG_OBJ_MSGF ("construct.line", 1, "top = %d\n", line->top); DBG_OBJ_MSGF ("construct.line", 1, "boxAscent = %d\n", line->boxAscent); DBG_OBJ_MSGF ("construct.line", 1, "boxDescent = %d\n", line->boxDescent); @@ -455,8 +449,7 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, line->contentDescent); DBG_OBJ_MSGF ("construct.line", 1, "maxLineWidth = %d (lineWidth = %d)\n", line->maxLineWidth, lineWidth); - DBG_OBJ_MSGF ("construct.line", 1, "offsetCompleteWidget = %d\n", - line->offsetCompleteWidget); + DBG_OBJ_MSGF ("construct.line", 1, "textOffset = %d\n", line->textOffset); mustQueueResize = true; @@ -467,7 +460,7 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, //words->getRef(line->lastWord)->badnessAndPenalty.print (); //printf ("\n"); - int xWidget = line->offsetCompleteWidget; + int xWidget = line->textOffset; for (int i = firstWord; i <= lastWord; i++) { Word *word = words->getRef (i); if (word->wordImgRenderer) @@ -485,52 +478,13 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, initNewLine (); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return line; } -void Textblock::accumulateWordExtremes (int firstWord, int lastWord, - int *maxOfMinWidth, int *sumOfMaxWidth) -{ - int parMin = 0; - *maxOfMinWidth = *sumOfMaxWidth = 0; - - for (int i = firstWord; i <= lastWord; i++) { - Word *word = words->getRef (i); - bool atLastWord = i == lastWord; - - core::Extremes extremes; - getWordExtremes (word, &extremes); - - // Minimum: between two *possible* breaks (or at the end). - // TODO This is redundant to getExtremesImpl(). - // TODO: Again, index 1 is used for lineCanBeBroken(). See getExtremes(). - if (word->badnessAndPenalty.lineCanBeBroken (1) || atLastWord) { - parMin += extremes.minWidth + word->hyphenWidth; - *maxOfMinWidth = misc::max (*maxOfMinWidth, parMin); - parMin = 0; - } else - // Shrinkability could be considered, but really does not play a - // role. - parMin += extremes.minWidth + word->origSpace; - - //printf ("[%p] after word: ", this); - //printWord (word); - //printf ("\n"); - - //printf ("[%p] (%d / %d) => parMin = %d, maxOfMinWidth = %d\n", - // this, extremes.minWidth, extremes.maxWidth, parMin, - // *maxOfMinWidth); - - *sumOfMaxWidth += (extremes.maxWidth + word->origSpace); - // Notice that the last space is added. See also: Line::parMax. - } -} - void Textblock::processWord (int wordIndex) { - DBG_OBJ_MSGF ("construct.all", 0, "<b>processWord</b> (%d)", wordIndex); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.all", 0, "processWord", "%d", wordIndex); DBG_MSG_WORD ("construct.all", 1, "<i>processed word:</i>", wordIndex, ""); int diffWords = wordWrap (wordIndex, false); @@ -576,7 +530,7 @@ void Textblock::processWord (int wordIndex) DBG_OBJ_MSG_END (); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /* @@ -589,10 +543,8 @@ void Textblock::processWord (int wordIndex) */ int Textblock::wordWrap (int wordIndex, bool wrapAll) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>wordWrap</b> (%d, %s)", + DBG_OBJ_ENTER ("construct.word", 0, "wordWrap", "%d, %s", wordIndex, wrapAll ? "true" : "false"); - DBG_OBJ_MSG_START (); - DBG_MSG_WORD ("construct.word", 1, "<i>wrapped word:</i> ", wordIndex, ""); if (!wrapAll) @@ -612,16 +564,15 @@ int Textblock::wordWrap (int wordIndex, bool wrapAll) n = wrapWordInFlow (wordIndex, wrapAll); DBG_OBJ_MSGF ("construct.word", 1, "=> %d", n); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return n; } int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>wrapWordInFlow</b> (%d, %s)", + DBG_OBJ_ENTER ("construct.word", 0, "wrapWordInFlow", "%d, %s", wordIndex, wrapAll ? "true" : "false"); - DBG_OBJ_MSG_START (); Word *word = words->getRef (wordIndex); int diffWords = 0; @@ -637,7 +588,7 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) // be left empty. // (In other cases, lines are never left empty, even if this means - // that the contents is wider than the available witdh. Leaving + // that the contents is wider than the line break width. Leaving // lines empty does not make sense without floats, since there will // be no possibility with more space anymore.) @@ -846,7 +797,7 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) } } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return diffWords; } @@ -863,15 +814,13 @@ void Textblock::balanceBreakPosAndHeight (int wordIndex, int firstIndex, bool regardBorder, int *height, int *breakPos) { - DBG_OBJ_MSGF ("construct.word", 0, - "<b>balanceBreakPosAndHeight</b> (%d, %d. %d, %s, %d, %s, " - "..., %s, ..., %d, %s, %d, ...)", - wordIndex, firstIndex, *searchUntil, - tempNewLine ? "true" : "false", penaltyIndex, - borderIsCalculated ? "true" : "false", - wrapAll ? "true" : "false", *lastFloatPos, - regardBorder ? "true" : "false", *height); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "balanceBreakPosAndHeight", + "%d, %d. %d, %s, %d, %s, ..., %s, ..., %d, %s, %d, ...", + wordIndex, firstIndex, *searchUntil, + tempNewLine ? "true" : "false", penaltyIndex, + borderIsCalculated ? "true" : "false", + wrapAll ? "true" : "false", *lastFloatPos, + regardBorder ? "true" : "false", *height); // The height of this part of the line (until the new break // position) may change with the break position, but the break @@ -947,7 +896,7 @@ void Textblock::balanceBreakPosAndHeight (int wordIndex, int firstIndex, runNo++; } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } // *wordIndexEnd must be initialized (initially to wordIndex) @@ -957,13 +906,12 @@ int Textblock::searchBreakPos (int wordIndex, int firstIndex, int *searchUntil, int *diffWords, int *wordIndexEnd, int *addIndex1) { - DBG_OBJ_MSGF ("construct.word", 0, - "<b>searchBreakPos</b> (%d, %d. %d, %s, %d, %s, %s, ...)", - wordIndex, firstIndex, *searchUntil, - tempNewLine ? "true" : "false", penaltyIndex, - thereWillBeMoreSpace ? "true" : "false", - wrapAll ? "true" : "false"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "searchBreakPos", + "%d, %d. %d, %s, %d, %s, %s, ...", + wordIndex, firstIndex, *searchUntil, + tempNewLine ? "true" : "false", penaltyIndex, + thereWillBeMoreSpace ? "true" : "false", + wrapAll ? "true" : "false"); DBG_MSG_WORD ("construct.word", 0, "<i>first word:</i> ", firstIndex, ""); int result; @@ -1053,7 +1001,7 @@ int Textblock::searchBreakPos (int wordIndex, int firstIndex, int *searchUntil, } while(!lineAdded); DBG_OBJ_MSGF ("construct.word", 1, "=> %d", result); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return result; } @@ -1061,12 +1009,10 @@ int Textblock::searchBreakPos (int wordIndex, int firstIndex, int *searchUntil, int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex, bool thereWillBeMoreSpace, bool correctAtEnd) { - DBG_OBJ_MSGF ("construct.word", 0, - "<b>searchMinBap</b> (%d, %d, %d, %s, %s)", - firstWord, lastWord, penaltyIndex, - thereWillBeMoreSpace ? "true" : "false", - correctAtEnd ? "true" : "false"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word", 0, "searchMinBap", "%d, %d, %d, %s, %s", + firstWord, lastWord, penaltyIndex, + thereWillBeMoreSpace ? "true" : "false", + correctAtEnd ? "true" : "false"); int pos = -1; @@ -1123,7 +1069,7 @@ int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex, } } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return pos; } @@ -1197,9 +1143,8 @@ void Textblock::handleWordExtremes (int wordIndex) { // TODO Overall, clarify penalty index. - DBG_OBJ_MSGF ("construct.paragraph", 0, - "<b>handleWordExtremes</b> (%d)", wordIndex); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.paragraph", 0, "handleWordExtremes", "%d", + wordIndex); initLine1Offset (wordIndex); @@ -1209,12 +1154,15 @@ void Textblock::handleWordExtremes (int wordIndex) core::Extremes wordExtremes; getWordExtremes (word, &wordExtremes); - DBG_OBJ_MSGF ("construct.paragraph", 1, "extremes: %d / %d", - wordExtremes.minWidth, wordExtremes.maxWidth); + DBG_OBJ_MSGF ("construct.paragraph", 1, "extremes: %d (%d) / %d (%d", + wordExtremes.minWidth, wordExtremes.minWidthIntrinsic, + wordExtremes.maxWidth, wordExtremes.maxWidthIntrinsic); if (wordIndex == 0) { wordExtremes.minWidth += line1OffsetEff; + wordExtremes.minWidthIntrinsic += line1OffsetEff; wordExtremes.maxWidth += line1OffsetEff; + wordExtremes.maxWidthIntrinsic += line1OffsetEff; } if (paragraphs->size() == 0 || @@ -1227,13 +1175,17 @@ void Textblock::handleWordExtremes (int wordIndex) Paragraph *par = paragraphs->getLastRef(); par->firstWord = par->lastWord = wordIndex; - par->parMin = par->parMax = 0; + par->parMin = par->parMinIntrinsic = par->parMax = par->parMaxIntrinsic + = 0; if (prevPar) { par->maxParMin = prevPar->maxParMin; + par->maxParMinIntrinsic = prevPar->maxParMinIntrinsic; par->maxParMax = prevPar->maxParMax; + par->maxParMaxIntrinsic = prevPar->maxParMaxIntrinsic; } else - par->maxParMin = par->maxParMax = 0; + par->maxParMin = par->maxParMinIntrinsic = par->maxParMax = + par->maxParMaxIntrinsic = 0; DBG_OBJ_MSGF ("construct.paragraph", 1, "new par: %d", paragraphs->size() - 1); @@ -1261,25 +1213,44 @@ void Textblock::handleWordExtremes (int wordIndex) lastPar->firstWord, lastPar->lastWord, corrDiffMin, corrDiffMax); + DBG_OBJ_MSGF ("construct.paragraph", 1, + "before: parMin = %d (%d) (max = %d (%d)), " + "parMax = %d (%d) (max = %d (%d))", + lastPar->parMin, lastPar->parMinIntrinsic, + lastPar->maxParMin, lastPar->maxParMinIntrinsic, + lastPar->parMax, lastPar->parMaxIntrinsic, + lastPar->maxParMax, lastPar->maxParMaxIntrinsic); + // Minimum: between two *possible* breaks. // Shrinkability could be considered, but really does not play a role. lastPar->parMin += wordExtremes.minWidth + word->hyphenWidth + corrDiffMin; + lastPar->parMinIntrinsic += + wordExtremes.minWidthIntrinsic + word->hyphenWidth + corrDiffMin; lastPar->maxParMin = misc::max (lastPar->maxParMin, lastPar->parMin); + lastPar->maxParMinIntrinsic = + misc::max (lastPar->maxParMinIntrinsic, lastPar->parMinIntrinsic); if (word->badnessAndPenalty.lineCanBeBroken (1) && (word->flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) == 0) - lastPar->parMin = 0; + lastPar->parMin = lastPar->parMinIntrinsic = 0; // Maximum: between two *necessary* breaks. lastPar->parMax += wordExtremes.maxWidth + word->hyphenWidth + corrDiffMax; + lastPar->parMaxIntrinsic += + wordExtremes.maxWidthIntrinsic + word->hyphenWidth + corrDiffMax; lastPar->maxParMax = misc::max (lastPar->maxParMax, lastPar->parMax); + lastPar->maxParMaxIntrinsic = + misc::max (lastPar->maxParMaxIntrinsic, lastPar->parMaxIntrinsic); DBG_OBJ_MSGF ("construct.paragraph", 1, - "=> parMin = %d (max = %d), parMax = %d (max = %d)", - lastPar->parMin, lastPar->maxParMin, lastPar->parMax, - lastPar->maxParMax); + "after: parMin = %d (%d) (max = %d (%d)), " + "parMax = %d (%d) (max = %d (%d))", + lastPar->parMin, lastPar->parMinIntrinsic, + lastPar->maxParMin, lastPar->maxParMinIntrinsic, + lastPar->parMax, lastPar->parMaxIntrinsic, + lastPar->maxParMax, lastPar->maxParMaxIntrinsic); lastPar->lastWord = wordIndex; - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -1408,9 +1379,8 @@ int Textblock::hyphenateWord (int wordIndex, int *addIndex1) void Textblock::moveWordIndices (int wordIndex, int num, int *addIndex1) { - DBG_OBJ_MSGF ("construct.word", 0, "<b>moveWordIndices</b> (%d, %d)", + DBG_OBJ_ENTER ("construct.word", 0, "moveWordIndices", "%d, %d", wordIndex, num); - DBG_OBJ_MSG_START (); if (containingBlock->outOfFlowMgr) containingBlock->outOfFlowMgr->moveExternalIndices (this, wordIndex, num); @@ -1437,7 +1407,7 @@ void Textblock::moveWordIndices (int wordIndex, int num, int *addIndex1) if (addIndex1 && *addIndex1 >= wordIndex) *addIndex1 += num; - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) @@ -1474,7 +1444,7 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) word->content.widget->getStyle()->margin.bottom); if (lines->size () == 1 && - word->content.widget->blockLevel () && + word->content.widget->isBlockLevel () && getStyle ()->borderWidth.top == 0 && getStyle ()->padding.top == 0) { // collapse top margins of parent element and its first child @@ -1505,9 +1475,8 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) void Textblock::accumulateWordData (int wordIndex) { - DBG_OBJ_MSGF ("construct.word.accum", 1, "<b>accumulateWordData</b> (%d)", + DBG_OBJ_ENTER ("construct.word.accum", 1, "accumulateWordData", "%d", wordIndex); - DBG_OBJ_MSG_START (); DBG_MSG_WORD ("construct.word.accum", 1, "<i>word:</i> ", wordIndex, ""); // Typically, the word in question is in the last line; in any case @@ -1526,12 +1495,13 @@ void Textblock::accumulateWordData (int wordIndex) Word *word = words->getRef (wordIndex); DBG_OBJ_MSGF ("construct.word.accum", 2, "lineIndex = %d", lineIndex); - int availWidth = calcAvailWidth (lineIndex); + int lineBreakWidth = calcLineBreakWidth (lineIndex); DBG_OBJ_MSGF ("construct.word.accum", 2, - "(%s existing line %d starts with word %d; availWidth = %d)", + "(%s existing line %d starts with word %d; " + "lineBreakWidth = %d)", lineIndex < lines->size () ? "already" : "not yet", - lineIndex, firstWordOfLine, availWidth); + lineIndex, firstWordOfLine, lineBreakWidth); if (wordIndex == firstWordOfLine) { // first word of the (not neccessarily yet existing) line @@ -1582,7 +1552,7 @@ void Textblock::accumulateWordData (int wordIndex) "totalShrinkability = %d + ... = %d", word->totalSpaceShrinkability, totalShrinkability); - word->badnessAndPenalty.calcBadness (word->totalWidth, availWidth, + word->badnessAndPenalty.calcBadness (word->totalWidth, lineBreakWidth, totalStretchability, totalShrinkability); @@ -1592,25 +1562,23 @@ void Textblock::accumulateWordData (int wordIndex) DBG_OBJ_MSGF ("construct.word.accum", 1, "b+p: %s", sb.getChars ()); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } -int Textblock::calcAvailWidth (int lineIndex) +int Textblock::calcLineBreakWidth (int lineIndex) { - DBG_OBJ_MSGF ("construct.word.width", 1, - "<b>calcAvailWidth</b> (%d <i>of %d</i>)", - lineIndex, lines->size()); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word.width", 1, "calcLineBreakWidth", + "%d <i>of %d</i>", lineIndex, lines->size()); - int availWidth = this->availWidth - innerPadding; + int lineBreakWidth = this->lineBreakWidth - leftInnerPadding; if (limitTextWidth && layout->getUsesViewport () && // margin/border/padding will be subtracted later, via OOFM. - availWidth - getStyle()->boxDiffWidth() + lineBreakWidth - getStyle()->boxDiffWidth() > layout->getWidthViewport () - 10) - availWidth = layout->getWidthViewport () - 10; + lineBreakWidth = layout->getWidthViewport () - 10; if (lineIndex == 0) - availWidth -= line1OffsetEff; + lineBreakWidth -= line1OffsetEff; int leftBorder, rightBorder; if (mustBorderBeRegarded (lineIndex)) { @@ -1622,14 +1590,14 @@ int Textblock::calcAvailWidth (int lineIndex) leftBorder = misc::max (leftBorder, getStyle()->boxOffsetX()); rightBorder = misc::max (rightBorder, getStyle()->boxRestWidth()); - availWidth -= (leftBorder + rightBorder); + lineBreakWidth -= (leftBorder + rightBorder); DBG_OBJ_MSGF ("construct.word.width", 2, "=> %d - %d - (%d + %d) = %d\n", - this->availWidth, innerPadding, leftBorder, rightBorder, - availWidth); + this->lineBreakWidth, leftInnerPadding, leftBorder, + rightBorder, lineBreakWidth); - DBG_OBJ_MSG_END (); - return availWidth; + DBG_OBJ_LEAVE (); + return lineBreakWidth; } void Textblock::initLine1Offset (int wordIndex) @@ -1639,18 +1607,18 @@ void Textblock::initLine1Offset (int wordIndex) /* Test whether line1Offset can be used. */ if (wordIndex == 0) { if (ignoreLine1OffsetSometimes && - line1Offset + word->size.width > availWidth) { + line1Offset + word->size.width > lineBreakWidth) { line1OffsetEff = 0; } else { int indent = 0; if (word->content.type == core::Content::WIDGET_IN_FLOW && - word->content.widget->blockLevel() == true) { + word->content.widget->isBlockLevel()) { /* don't use text-indent when nesting blocks */ } else { if (core::style::isPerLength(getStyle()->textIndent)) { indent = core::style::multiplyWithPerLengthRounded - (this->availWidth, getStyle()->textIndent); + (lineBreakWidth, getStyle()->textIndent); } else { indent = core::style::absLengthVal (getStyle()->textIndent); } @@ -1667,14 +1635,15 @@ void Textblock::initLine1Offset (int wordIndex) */ void Textblock::alignLine (int lineIndex) { - DBG_OBJ_MSGF ("construct.line", 0, "<b>alignLine</b> (%d)", lineIndex); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.line", 0, "alignLine", "%d", lineIndex); Line *line = lines->getRef (lineIndex); - int availWidth = calcAvailWidth (lineIndex); + if (line->firstWord <= line->lastWord) { Word *firstWord = words->getRef (line->firstWord); Word *lastWord = words->getRef (line->lastWord); + int lineBreakWidth = + this->lineBreakWidth - (line->leftOffset + line->rightOffset); for (int i = line->firstWord; i < line->lastWord; i++) words->getRef(i)->origSpace = words->getRef(i)->effSpace; @@ -1684,18 +1653,18 @@ void Textblock::alignLine (int lineIndex) case core::style::TEXT_ALIGN_LEFT: DBG_OBJ_MSG ("construct.line", 1, "first word has 'text-align: left'"); - line->leftOffset = 0; + line->alignment = Line::LEFT; break; case core::style::TEXT_ALIGN_STRING: /* handled elsewhere (in the * future)? */ DBG_OBJ_MSG ("construct.line", 1, "first word has 'text-align: string'"); - line->leftOffset = 0; + line->alignment = Line::LEFT; break; case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */ - line->leftOffset = 0; DBG_OBJ_MSG ("construct.line", 1, "first word has 'text-align: justify'"); + line->alignment = Line::LEFT; // Do not justify the last line of a paragraph (which ends on a // BREAK or with the last word of the page). if(!(lastWord->content.type == core::Content::BREAK || @@ -1704,33 +1673,62 @@ void Textblock::alignLine (int lineIndex) // when the line would be shrunken otherwise. (This solution is // far from perfect, but a better solution would make changes in // the line breaking algorithm necessary.) - availWidth < lastWord->totalWidth) - justifyLine (line, availWidth - lastWord->totalWidth); + lineBreakWidth < lastWord->totalWidth) + justifyLine (line, lineBreakWidth - lastWord->totalWidth); break; case core::style::TEXT_ALIGN_RIGHT: DBG_OBJ_MSG ("construct.line", 1, "first word has 'text-align: right'"); - line->leftOffset = availWidth - lastWord->totalWidth; + line->alignment = Line::RIGHT; break; case core::style::TEXT_ALIGN_CENTER: DBG_OBJ_MSG ("construct.line", 1, "first word has 'text-align: center'"); - line->leftOffset = (availWidth - lastWord->totalWidth) / 2; + line->alignment = Line::CENTER; break; default: - /* compiler happiness */ - line->leftOffset = 0; + // compiler happiness + line->alignment = Line::LEFT; } - /* For large lines (images etc), which do not fit into the viewport: */ - if (line->leftOffset < 0) - line->leftOffset = 0; - } + } else + // empty line (only line break); + line->alignment = Line::LEFT; } else // empty line - line->leftOffset = 0; + line->alignment = Line::LEFT; - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); +} + +void Textblock::calcTextOffset (int lineIndex, int totalWidth) +{ + Line *line = lines->getRef (lineIndex); + int lineWidth = line->firstWord <= line->lastWord ? + words->getRef(line->lastWord)->totalWidth : 0; + + switch (line->alignment) { + case Line::LEFT: + line->textOffset = line->leftOffset; + break; + + case Line::RIGHT: + line->textOffset = totalWidth - line->rightOffset - lineWidth; + break; + + case Line::CENTER: + line->textOffset = + (line->leftOffset + totalWidth - line->rightOffset - lineWidth) / 2; + break; + + default: + misc::assertNotReached (); + break; + } + + // For large lines (images etc), which do not fit into the viewport: + if (line->textOffset < line->leftOffset) + line->textOffset = line->leftOffset; } /** @@ -1741,8 +1739,7 @@ void Textblock::alignLine (int lineIndex) */ void Textblock::rewrap () { - DBG_OBJ_MSG ("construct.line", 0, "<b>rewrap</b> ()"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("construct.line", 0, "rewrap"); if (wrapRefLines == -1) DBG_OBJ_MSG ("construct.line", 0, "does not have to be rewrapped"); @@ -1790,7 +1787,7 @@ void Textblock::rewrap () DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -1798,8 +1795,7 @@ void Textblock::rewrap () */ void Textblock::fillParagraphs () { - DBG_OBJ_MSG ("resize", 0, "<b>fillParagraphs</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "fillParagraphs"); DBG_OBJ_MSGF ("resize", 1, "wrapRefParagraphs = %d", wrapRefParagraphs); @@ -1850,13 +1846,12 @@ void Textblock::fillParagraphs () DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Textblock::initNewLine () { - DBG_OBJ_MSG ("construct.line", 0, "<b>initNewLine</b> ()"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("construct.line", 0, "initNewLine"); // At the very beginning, in Textblock::Textblock, where this // method is called, containingBlock is not yet defined. @@ -1878,14 +1873,13 @@ void Textblock::initNewLine () DBG_OBJ_SET_NUM ("newLineAscent", newLineAscent); DBG_OBJ_SET_NUM ("newLineDescent", newLineDescent); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Textblock::calcBorders (int lastOofRef, int height) { - DBG_OBJ_MSGF ("construct.line", 0, "<b>calcBorders</b> (%d, %d)", + DBG_OBJ_ENTER ("construct.line", 0, "calcBorders", "%d, %d", lastOofRef, height); - DBG_OBJ_MSG_START (); if (containingBlock && containingBlock->outOfFlowMgr) { // Consider the example: @@ -1953,13 +1947,12 @@ void Textblock::calcBorders (int lastOofRef, int height) DBG_OBJ_MSG ("construct.line", 0, "<i>no CB of OOFM</i>"); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Textblock::showMissingLines () { - DBG_OBJ_MSG ("construct.line", 0, "<b>showMissingLines</b> ()"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("construct.line", 0, "showMissingLines"); // "Temporary word": when the last word is an OOF reference, it is // not processed, and not part of any line. For this reason, we @@ -2010,7 +2003,7 @@ void Textblock::showMissingLines () addLine (firstWordNotInLine, words->size () - 1, -1, true); */ - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } @@ -2045,9 +2038,8 @@ int Textblock::getLineShrinkability(int lastWordIndex) int Textblock::getLineStretchability(int lastWordIndex) { - DBG_OBJ_MSGF ("construct.word.accum", 0, - "<b>getLineStretchability</b> (%d)", lastWordIndex); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("construct.word.accum", 0, "getLineStretchability", "%d", + lastWordIndex); DBG_MSG_WORD ("construct.word.accum", 1, "<i>last word:</i> ", lastWordIndex, ""); @@ -2066,7 +2058,7 @@ int Textblock::getLineStretchability(int lastWordIndex) lastWord->maxDescent, str); } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); return str; // Alternative: return 0; diff --git a/dw/types.hh b/dw/types.hh index e910d296..36d6caa1 100644 --- a/dw/types.hh +++ b/dw/types.hh @@ -180,6 +180,8 @@ struct Extremes { int minWidth; int maxWidth; + int minWidthIntrinsic; + int maxWidthIntrinsic; }; struct Content @@ -56,6 +56,7 @@ void Embed::sizeRequestImpl (Requisition *requisition) void Embed::getExtremesImpl (Extremes *extremes) { resource->getExtremes (extremes); + correctExtremes (extremes); } void Embed::sizeAllocateImpl (Allocation *allocation) @@ -63,6 +64,35 @@ void Embed::sizeAllocateImpl (Allocation *allocation) resource->sizeAllocate (allocation); } +int Embed::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + return resource->getAvailWidthOfChild (child, forceValue); +} + +int Embed::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + return resource->getAvailHeightOfChild (child, forceValue); +} + +void Embed::correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)) +{ + resource->correctRequisitionOfChild (child, requisition, splitHeightFun); +} + +void Embed::correctExtremesOfChild (Widget *child, Extremes *extremes) +{ + resource->correctExtremesOfChild (child, extremes); +} + +void Embed::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + resource->containerSizeChangedForChildren (); + DBG_OBJ_LEAVE (); +} + void Embed::enterNotifyImpl (core::EventCrossing *event) { resource->emitEnter(); @@ -88,36 +118,6 @@ bool Embed::buttonPressImpl (core::EventButton *event) return handled; } -void Embed::setWidth (int width) -{ - DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width); - DBG_OBJ_MSG_START (); - - resource->setWidth (width); - - DBG_OBJ_MSG_END (); -} - -void Embed::setAscent (int ascent) -{ - DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent); - DBG_OBJ_MSG_START (); - - resource->setAscent (ascent); - - DBG_OBJ_MSG_END (); -} - -void Embed::setDescent (int descent) -{ - DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent); - DBG_OBJ_MSG_START (); - - resource->setDescent (descent); - - DBG_OBJ_MSG_END (); -} - void Embed::setDisplayed (bool displayed) { resource->setDisplayed (displayed); @@ -206,8 +206,7 @@ void Resource::setEmbed (Embed *embed) void Resource::getExtremes (Extremes *extremes) { - DBG_OBJ_MSG ("resize", 0, "<b>getExtremes</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "getExtremes"); /* Simply return the requisition width */ Requisition requisition; @@ -216,23 +215,45 @@ void Resource::getExtremes (Extremes *extremes) DBG_OBJ_MSGF ("resize", 1, "result: %d / %d", extremes->minWidth, extremes->maxWidth); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void Resource::sizeAllocate (Allocation *allocation) { } -void Resource::setWidth (int width) +int Resource::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + // Only used when the resource contains other dillo widgets. + misc::assertNotReached (); + return 0; +} + +int Resource::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + // Only used when the resource contains other dillo widgets. + misc::assertNotReached (); + return 0; +} + +void Resource::correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) { + // Only used when the resource contains other dillo widgets. + misc::assertNotReached (); } -void Resource::setAscent (int ascent) +void Resource::correctExtremesOfChild (Widget *child, Extremes *extremes) { + // Only used when the resource contains other dillo widgets. + misc::assertNotReached (); } -void Resource::setDescent (int descent) +void Resource::containerSizeChangedForChildren () { + // No children by default. } void Resource::setDisplayed (bool displayed) @@ -290,14 +311,10 @@ Iterator *LabelButtonResource::iterator (Content::Type mask, bool atEnd) void ComplexButtonResource::LayoutReceiver::resizeQueued (bool extremesChanged) { - DBG_OBJ_MSGF_O ("resize", 0, resource, - "LayoutReceiver::<b>resizeQueued</b> (%s)", + DBG_OBJ_ENTER ("resize", 0, "LayoutReceiver/resizeQueued", "%s", extremesChanged ? "true" : "false"); - DBG_OBJ_MSG_START_O (resource); - resource->queueResize (extremesChanged); - - DBG_OBJ_MSG_END_O (resource); + DBG_OBJ_LEAVE (); } ComplexButtonResource::ComplexButtonResource () @@ -310,21 +327,24 @@ ComplexButtonResource::ComplexButtonResource () void ComplexButtonResource::init (Widget *widget) { - this->childWidget = widget; + childWidget = widget; layout = new Layout (createPlatform ()); setLayout (layout); DBG_OBJ_ASSOC_CHILD (layout); layout->setWidget (widget); layout->connect (&layoutReceiver); + + if (getEmbed ()) + childWidget->setQuasiParent (getEmbed ()); } void ComplexButtonResource::setEmbed (Embed *embed) { ButtonResource::setEmbed (embed); - if (childWidget->usesHints ()) - embed->setUsesHints (); + if (childWidget) + childWidget->setQuasiParent (getEmbed ()); } ComplexButtonResource::~ComplexButtonResource () @@ -335,8 +355,7 @@ ComplexButtonResource::~ComplexButtonResource () void ComplexButtonResource::sizeRequest (Requisition *requisition) { - DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); Requisition widgetRequisition; childWidget->sizeRequest (&widgetRequisition); @@ -346,13 +365,12 @@ void ComplexButtonResource::sizeRequest (Requisition *requisition) DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void ComplexButtonResource::getExtremes (Extremes *extremes) { - DBG_OBJ_MSG ("resize", 0, "<b>getExtremes</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "getExtremes"); Extremes widgetExtremes; childWidget->getExtremes (&widgetExtremes); @@ -361,41 +379,82 @@ void ComplexButtonResource::getExtremes (Extremes *extremes) DBG_OBJ_MSGF ("resize", 1, "result: %d / %d", extremes->minWidth, extremes->maxWidth); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } void ComplexButtonResource::sizeAllocate (Allocation *allocation) { } -void ComplexButtonResource::setWidth (int width) +int ComplexButtonResource::getAvailWidthOfChild (Widget *child, bool forceValue) { - DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width); - DBG_OBJ_MSG_START (); - - childWidget->setWidth (width - 2 * reliefXThickness ()); + int embedWidth = getEmbed()->getAvailWidth (forceValue); + if (embedWidth == -1) + return -1; + else + return misc::max (embedWidth - 2 * reliefXThickness (), 0); +} - DBG_OBJ_MSG_END (); +int ComplexButtonResource::getAvailHeightOfChild (Widget *child, + bool forceValue) +{ + int embedHeight = getEmbed()->getAvailHeight (forceValue); + if (embedHeight == -1) + return -1; + else + return misc::max (embedHeight - 2 * reliefYThickness (), 0); } -void ComplexButtonResource::setAscent (int ascent) +void ComplexButtonResource::correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) + (int, int*, int*)) { - DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent); - DBG_OBJ_MSG_START (); + // Similar to Widget::correctRequisitionOfChild, but for percentage + // the relief has to be considered. + + if (style::isPerLength (child->getStyle()->width)) { + int availWidth = getEmbed()->getAvailHeight (false); + if (availWidth != -1) { + int baseWidth = misc::max (availWidth + - getEmbed()->boxDiffWidth () + - 2 * reliefXThickness (), + 0); + requisition->width = + child->applyPerWidth (baseWidth, child->getStyle()->width); + } + } else + getEmbed()->correctReqWidthOfChildNoRec (child, requisition); - childWidget->setAscent (ascent - reliefYThickness ()); + // TODO Percentage heights are ignored again. + getEmbed()->correctReqHeightOfChildNoRec (child, requisition, + splitHeightFun); - DBG_OBJ_MSG_END (); } -void ComplexButtonResource::setDescent (int descent) +void ComplexButtonResource::correctExtremesOfChild (Widget *child, + Extremes *extremes) { - DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent); - DBG_OBJ_MSG_START (); - - childWidget->setDescent (descent - reliefYThickness ()); + // Similar to Widget::correctExtremesOfChild, but for percentage + // the relief has to be considered. + + if (style::isPerLength (child->getStyle()->width)) { + int availWidth = getEmbed()->getAvailHeight (false); + if (availWidth != -1) { + int baseWidth = misc::max (availWidth + - getEmbed()->boxDiffWidth () + - 2 * reliefXThickness (), + 0); + extremes->minWidth = extremes->maxWidth = + child->applyPerWidth (baseWidth, child->getStyle()->width); + } + } else + getEmbed()->correctExtremesOfChildNoRec (child, extremes); +} - DBG_OBJ_MSG_END (); +void ComplexButtonResource::containerSizeChangedForChildren () +{ + layout->containerSizeChanged (); } Iterator *ComplexButtonResource::iterator (Content::Type mask, bool atEnd) @@ -231,6 +231,16 @@ protected: void sizeRequestImpl (Requisition *requisition); void getExtremesImpl (Extremes *extremes); void sizeAllocateImpl (Allocation *allocation); + + int getAvailWidthOfChild (Widget *child, bool forceValue); + int getAvailHeightOfChild (Widget *child, bool forceValue); + void correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + void correctExtremesOfChild (Widget *child, Extremes *extremes); + + void containerSizeChangedForChildren (); + void enterNotifyImpl (core::EventCrossing *event); void leaveNotifyImpl (core::EventCrossing *event); bool buttonPressImpl (core::EventButton *event); @@ -241,18 +251,26 @@ public: Embed(Resource *resource); ~Embed(); - void setWidth (int width); - void setAscent (int ascent); - void setDescent (int descent); void setDisplayed (bool displayed); void setEnabled (bool enabled); void draw (View *view, Rectangle *area); Iterator *iterator (Content::Type mask, bool atEnd); void setStyle (style::Style *style); - inline void setUsesHints () { setFlags (USES_HINTS); } - inline Resource *getResource () { return resource; } + + inline void correctReqWidthOfChildNoRec (Widget *child, + Requisition *requisition) + { Widget::correctReqWidthOfChild (child, requisition); } + + inline void correctReqHeightOfChildNoRec (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) + { Widget::correctReqHeightOfChild (child, requisition, splitHeightFun); } + + virtual void correctExtremesOfChildNoRec (Widget *child, Extremes *extremes) + { Widget::correctExtremesOfChild (child, extremes); } }; /** @@ -337,9 +355,16 @@ public: virtual void sizeRequest (Requisition *requisition) = 0; virtual void getExtremes (Extremes *extremes); virtual void sizeAllocate (Allocation *allocation); - virtual void setWidth (int width); - virtual void setAscent (int ascent); - virtual void setDescent (int descent); + + virtual int getAvailWidthOfChild (Widget *child, bool forceValue); + virtual int getAvailHeightOfChild (Widget *child, bool forceValue); + virtual void correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)); + virtual void correctExtremesOfChild (Widget *child, Extremes *extremes); + virtual void containerSizeChangedForChildren (); + virtual void setDisplayed (bool displayed); virtual void draw (View *view, Rectangle *area); virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; @@ -407,9 +432,15 @@ public: void sizeRequest (Requisition *requisition); void getExtremes (Extremes *extremes); void sizeAllocate (Allocation *allocation); - void setWidth (int width); - void setAscent (int ascent); - void setDescent (int descent); + + int getAvailWidthOfChild (Widget *child, bool forceValue); + int getAvailHeightOfChild (Widget *child, bool forceValue); + void correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + void correctExtremesOfChild (Widget *child, Extremes *extremes); + void containerSizeChangedForChildren (); + Iterator *iterator (Content::Type mask, bool atEnd); int getClickX () {return click_x;}; int getClickY () {return click_y;}; diff --git a/dw/widget.cc b/dw/widget.cc index a3d85d0a..44c02436 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -69,8 +69,10 @@ Widget::Widget () DBG_OBJ_CREATE ("dw::core::Widget"); registerName ("dw::core::Widget", &CLASS_ID); - flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS); - parent = generator = NULL; + flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED); + parent = quasiParent = generator = container = NULL; + DBG_OBJ_SET_PTR ("container", container); + layout = NULL; allocation.x = -1; @@ -79,6 +81,8 @@ Widget::Widget () allocation.ascent = 1; allocation.descent = 0; + extraSpace.top = extraSpace.right = extraSpace.bottom = extraSpace.left = 0; + style = NULL; bgColor = NULL; buttonSensitive = true; @@ -153,16 +157,38 @@ void Widget::setParent (Widget *parent) //printf ("The %s %p becomes a child of the %s %p\n", // getClassName(), this, parent->getClassName(), parent); + // Determine the container. Currently rather simple; will become + // more complicated when absolute and fixed positions are + // supported. + container = NULL; + for (Widget *widget = getParent (); widget != NULL && container == NULL; + widget = widget->getParent()) + if (widget->isPossibleContainer ()) + container = widget; + // If there is no possible container widget, there is + // (surprisingly!) also no container (i. e. the viewport is + // used). Does not occur in dillo, where the toplevel widget is a + // Textblock. + DBG_OBJ_SET_PTR ("container", container); + notifySetParent(); } +void Widget::setQuasiParent (Widget *quasiParent) +{ + this->quasiParent = quasiParent; + + // More to do? Compare with setParent(). + + DBG_OBJ_SET_PTR ("quasiParent", quasiParent); +} + void Widget::queueDrawArea (int x, int y, int width, int height) { /** \todo Maybe only the intersection? */ - DBG_OBJ_MSGF ("draw", 0, "<b>queueDrawArea</b> (%d, %d, %d, %d)", - x, y, width, height); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("draw", 0, "queueDrawArea", "%d, %d, %d, %d", + x, y, width, height); _MSG("Widget::queueDrawArea alloc(%d %d %d %d) wid(%d %d %d %d)\n", allocation.x, allocation.y, @@ -171,17 +197,21 @@ void Widget::queueDrawArea (int x, int y, int width, int height) if (layout) layout->queueDraw (x + allocation.x, y + allocation.y, width, height); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** * \brief This method should be called, when a widget changes its size. + * + * A "fast" queueResize will ignore the anchestors, and furthermore + * not trigger the idle function. Used only within + * viewportSizeChanged, and not available outside Layout and Widget. */ -void Widget::queueResize (int ref, bool extremesChanged) +void Widget::queueResize (int ref, bool extremesChanged, bool fast) { - DBG_OBJ_MSGF ("resize", 0, "<b>queueResize</b> (%d, %s)", - ref, extremesChanged ? "true" : "false"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 0, "queueResize", "%d, %s, %s", + ref, extremesChanged ? "true" : "false", + fast ? "true" : "false"); // queueResize() can be called recursively; calls are queued, so // that actualQueueResize() is clean. @@ -189,38 +219,60 @@ void Widget::queueResize (int ref, bool extremesChanged) if (queueResizeEntered ()) { DBG_OBJ_MSG ("resize", 1, "put into queue"); layout->queueQueueResizeList->put (new Layout::QueueResizeItem - (this, ref, extremesChanged)); + (this, ref, extremesChanged, fast)); } else { - actualQueueResize (ref, extremesChanged); - + actualQueueResize (ref, extremesChanged, fast); + + DBG_IF_RTFL { + if (layout == NULL) + DBG_OBJ_MSG ("resize", 1, "layout is not set"); + else if (layout->queueQueueResizeList->size () == 0) + DBG_OBJ_MSG ("resize", 1, "queue item list is empty"); + } + while (layout != NULL && layout->queueQueueResizeList->size () > 0) { + DBG_IF_RTFL { + DBG_OBJ_MSGF ("resize", 1, "queue item list has %d elements:", + layout->queueQueueResizeList->size ()); + DBG_OBJ_MSG_START (); + for (int i = 0; i < layout->queueQueueResizeList->size (); i++) { + DBG_OBJ_MSGF + ("resize", 1, + "#%d: widget = %p, ref = %d, extremesChanged = %s, " + "fast = %s", + i, layout->queueQueueResizeList->get(i)->widget, + layout->queueQueueResizeList->get(i)->ref, + layout->queueQueueResizeList->get(i)->extremesChanged ? + "true" : "false", + layout->queueQueueResizeList->get(i)->fast ? + "true" : "false"); + } + DBG_OBJ_MSG_END (); + DBG_OBJ_MSG ("resize", 1, "taking #0 out of list"); + } + Layout::QueueResizeItem *item = layout->queueQueueResizeList->get (0); - DBG_OBJ_MSGF ("resize", 1, "taken out of queue queue (size = %d)", - layout->queueQueueResizeList->size ()); - item->widget->actualQueueResize (item->ref, item->extremesChanged); + item->widget->actualQueueResize (item->ref, item->extremesChanged, + item->fast); layout->queueQueueResizeList->remove (0); // hopefully not too large } } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } -void Widget::actualQueueResize (int ref, bool extremesChanged) +void Widget::actualQueueResize (int ref, bool extremesChanged, bool fast) { assert (!queueResizeEntered ()); - DBG_OBJ_MSGF ("resize", 0, "<b>actualQueueResize</b> (%d, %s)", - ref, extremesChanged ? "true" : "false"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 0, "actualQueueResize", "%d, %s, %s", + ref, extremesChanged ? "true" : "false", + fast ? "true" : "false"); enterQueueResize (); Widget *widget2, *child; - //printf("The %stop-level %s %p with parentRef = %d has changed its size. " - // "Layout = %p.\n", - // parent ? "non-" : "", getClassName(), this, parentRef, layout); - Flags resizeFlag, extremesFlag; if (layout) { @@ -243,36 +295,173 @@ void Widget::actualQueueResize (int ref, bool extremesChanged) setFlags (extremesFlag); markExtremesChange (ref); } - - for (widget2 = parent, child = this; widget2; - child = widget2, widget2 = widget2->parent) { - //printf (" Setting %s and ALLOCATE_QUEUED for the " - // "%stop-level %s %p with parentRef = %d\n", - // resizeFlag == RESIZE_QUEUED ? "RESIZE_QUEUED" : "NEEDS_RESIZE", - // widget2->parent ? "non-" : "", widget2->getClassName(), widget2, - // widget2->parentRef); - - if (layout && !widget2->resizeQueued ()) - layout->queueResizeList->put (widget2); - - widget2->setFlags (resizeFlag); - widget2->markSizeChange (child->parentRef); - widget2->setFlags (ALLOCATE_QUEUED); - - if (extremesChanged) { - widget2->setFlags (extremesFlag); - widget2->markExtremesChange (child->parentRef); + + if (fast) { + if (parent) { + // In this case, queueResize is called from top (may be a + // random entry point) to bottom, so markSizeChange and + // markExtremesChange have to be called explicitly for the + // parent. The tests (needsResize etc.) are uses to check + // whether queueResize has been called for the parent, or + // whether this widget is the enty point. + if (parent->needsResize () || parent->resizeQueued ()) + parent->markSizeChange (parentRef); + if (parent->extremesChanged () || parent->extremesQueued ()) + parent->markExtremesChange (parentRef); + } + } else { + for (widget2 = parent, child = this; widget2; + child = widget2, widget2 = widget2->parent) { + if (layout && !widget2->resizeQueued ()) + layout->queueResizeList->put (widget2); + + DBG_OBJ_MSGF ("resize", 2, "setting %s and ALLOCATE_QUEUED for %p", + resizeFlag == RESIZE_QUEUED ? + "RESIZE_QUEUED" : "NEEDS_RESIZE", + widget2); + + widget2->setFlags (resizeFlag); + widget2->markSizeChange (child->parentRef); + widget2->setFlags (ALLOCATE_QUEUED); + + if (extremesChanged) { + widget2->setFlags (extremesFlag); + widget2->markExtremesChange (child->parentRef); + } } - } - if (layout) - layout->queueResize (extremesChanged); + if (layout) + layout->queueResize (extremesChanged); + } leaveQueueResize (); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } +void Widget::containerSizeChanged () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChanged"); + + // If there is a container widget (not the viewport), which has not + // changed its size (which can be determined by the respective + // flags: this method is called recursively), this widget will + // neither change its size. Also, the recursive iteration can be + // stopped, since the children of this widget will + if (container == NULL || + container->needsResize () || container->resizeQueued () || + container->extremesChanged () || container->extremesQueued ()) { + // Viewport (container == NULL) or container widget has changed + // its size. + if (affectedByContainerSizeChange ()) + queueResizeFast (0, true); + + // Even if *this* widget is not affected, children may be, so + // iterate over children. + containerSizeChangedForChildren (); + } + + DBG_OBJ_LEAVE (); +} + +bool Widget::affectedByContainerSizeChange () +{ + DBG_OBJ_ENTER0 ("resize", 0, "affectedByContainerSizeChange"); + + bool ret; + + // This standard implementation is suitable for all widgets which + // call correctRequisition() and correctExtremes(), even in the way + // how Textblock and Image do (see comments there). Has to be kept + // in sync. + + if (container == NULL) { + if (style::isAbsLength (getStyle()->width) && + style::isAbsLength (getStyle()->height)) + // Both absolute, i. e. fixed: no dependency. + ret = false; + else if (style::isPerLength (getStyle()->width) || + style::isPerLength (getStyle()->height)) { + // Any percentage: certainly dependenant. + ret = true; + } else + // One or both is "auto": depends ... + ret = + (getStyle()->width == style::LENGTH_AUTO ? + usesAvailWidth () : false) || + (getStyle()->height == style::LENGTH_AUTO ? + usesAvailHeight () : false); + } else + ret = container->affectsSizeChangeContainerChild (this); + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); + return ret; +} + +bool Widget::affectsSizeChangeContainerChild (Widget *child) +{ + DBG_OBJ_ENTER ("resize", 0, "affectsSizeChangeContainerChild", "%p", child); + + bool ret; + + // From the point of view of the container. This standard + // implementation should be suitable for most (if not all) + // containers. + + if (style::isAbsLength (child->getStyle()->width) && + style::isAbsLength (child->getStyle()->height)) + // Both absolute, i. e. fixed: no dependency. + ret = false; + else if (style::isPerLength (child->getStyle()->width) || + style::isPerLength (child->getStyle()->height)) { + // Any percentage: certainly dependenant. + ret = true; + } else + // One or both is "auto": depends ... + ret = + (child->getStyle()->width == style::LENGTH_AUTO ? + child->usesAvailWidth () : false) || + (child->getStyle()->height == style::LENGTH_AUTO ? + child->usesAvailHeight () : false); + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); + return ret; +} + +void Widget::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + // Working, but inefficient standard implementation. + Iterator *it = iterator ((Content::Type)(Content::WIDGET_IN_FLOW | + Content::WIDGET_OOF_CONT), + false); + while (it->next ()) + it->getContent()->widget->containerSizeChanged (); + it->unref (); + + DBG_OBJ_LEAVE (); +} + +/** + * \brief Must be implemengted by a method returning true, when + * getAvailWidth() is called. + */ +bool Widget::usesAvailWidth () +{ + return false; +} + +/** + * \brief Must be implemengted by a method returning true, when + * getAvailHeight() is called. + */ +bool Widget::usesAvailHeight () +{ + return false; +} /** * \brief This method is a wrapper for Widget::sizeRequestImpl(); it calls @@ -282,8 +471,7 @@ void Widget::sizeRequest (Requisition *requisition) { assert (!queueResizeEntered ()); - DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); enterSizeRequest (); @@ -319,7 +507,208 @@ void Widget::sizeRequest (Requisition *requisition) leaveSizeRequest (); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); +} + +/** + * Return available width including margin/border/padding + * (extraSpace?), not only the content width. + */ +int Widget::getAvailWidth (bool forceValue) +{ + // TODO Correct by extremes? + + DBG_OBJ_ENTER ("resize", 0, "getAvailWidth", "%s", + forceValue ? "true" : "false"); + + int width; + + if (parent == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + // TODO Consider nested layouts (e. g. <button>). + if (style::isAbsLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (getStyle()->width)); + width = style::absLengthVal (getStyle()->width) + boxDiffWidth (); + } else { + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + if (style::isPerLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->width)); + width = applyPerWidth (viewportWidth, getStyle()->width); + } else { + DBG_OBJ_MSG ("resize", 1, "no specification"); + width = viewportWidth; + } + + } + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + width = parent->getAvailWidthOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + width = quasiParent->getAvailWidthOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + + return width; +} + +/** + * Return available height including margin/border/padding + * (extraSpace?), not only the content height. + */ +int Widget::getAvailHeight (bool forceValue) +{ + // TODO Correct by ... not extremes, but ...? (Height extremes?) + + DBG_OBJ_ENTER ("resize", 0, "getAvailHeight", "%s", + forceValue ? "true" : "false"); + + int height; + + if (parent == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + // TODO Consider nested layouts (e. g. <button>). + if (style::isAbsLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (getStyle()->height)); + height = style::absLengthVal (getStyle()->height) + boxDiffHeight (); + } else if (style::isPerLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->height)); + // Notice that here -- unlike getAvailWidth() -- + // layout->hScrollbarThickness is not considered here; + // something like canvasWidthGreater (analogue to + // canvasHeightGreater) would be complicated and lead to + // possibly contradictory self-references. + height = applyPerHeight (layout->viewportHeight, getStyle()->height); + } else { + DBG_OBJ_MSG ("resize", 1, "no specification"); + height = layout->viewportHeight; + } + + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + height = quasiParent->getAvailHeightOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + height = quasiParent->getAvailHeightOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + + return height; +} + +void Widget::correctRequisition (Requisition *requisition, + void (*splitHeightFun) (int, int *, int *)) +{ + // TODO Correct by extremes? + + DBG_OBJ_ENTER ("resize", 0, "correctRequisition", "%d * (%d + %d), ...", + requisition->width, requisition->ascent, + requisition->descent); + + if (container == NULL && quasiParent == NULL) { + if (style::isAbsLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (getStyle()->width)); + requisition->width = + style::absLengthVal (getStyle()->width) + boxDiffWidth (); + } else if (style::isPerLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->width)); + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + requisition->width = applyPerWidth (viewportWidth, getStyle()->width); + } + + // TODO Perhaps split first, then add box ascent and descent. + if (style::isAbsLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (getStyle()->height)); + splitHeightFun (style::absLengthVal (getStyle()->height) + + boxDiffHeight (), + &requisition->ascent, &requisition->descent); + } else if (style::isPerLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->height)); +#if 0 + // TODO Percentage heights are somewhat more complicated. Has + // to be clarified. + + // For layout->viewportHeight, see comment in getAvailHeight(). + splitHeightFun (applyPerHeight (layout->viewportHeight, + getStyle()->height), + &requisition->ascent, &requisition->descent); +#endif + } + } else if (container) + container->correctRequisitionOfChild (this, requisition, splitHeightFun); + else // if (quasiParent) + // Here, quasiParent plays the same role as the container. + quasiParent->correctRequisitionOfChild (this, requisition, + splitHeightFun); + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctExtremes (Extremes *extremes) +{ + // TODO Extremes only corrected? + + DBG_OBJ_ENTER ("resize", 0, "correctExtremes", "%d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + if (container == NULL && quasiParent == NULL) { + if (style::isAbsLength (getStyle()->width)) + extremes->minWidth = extremes->maxWidth = + style::absLengthVal (getStyle()->width) + boxDiffWidth (); + else if (style::isPerLength (getStyle()->width)) { + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + extremes->minWidth = extremes->maxWidth = + applyPerWidth (viewportWidth, getStyle()->width); + } + } else if (container) + container->correctExtremesOfChild (this, extremes); + else // if (quasiParent) + // Here, quasiParent plays the same role as the container. + quasiParent->correctExtremesOfChild (this, extremes); + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_LEAVE (); } /** @@ -329,7 +718,7 @@ void Widget::getExtremes (Extremes *extremes) { assert (!queueResizeEntered ()); - DBG_OBJ_MSG ("resize", 0, "<b>getExtremes</b>"); + DBG_OBJ_ENTER0 ("resize", 0, "getExtremes"); DBG_OBJ_MSG_START (); enterGetExtremes (); @@ -344,18 +733,32 @@ void Widget::getExtremes (Extremes *extremes) } if (extremesChanged ()) { + // For backward compatibility (part 1/2): + extremes->minWidthIntrinsic = extremes->maxWidthIntrinsic = -1; + getExtremesImpl (extremes); + + // For backward compatibility (part 2/2): + if (extremes->minWidthIntrinsic == -1) + extremes->minWidthIntrinsic = extremes->minWidth; + if (extremes->maxWidthIntrinsic == -1) + extremes->maxWidthIntrinsic = extremes->maxWidth; + this->extremes = *extremes; unsetFlags (EXTREMES_CHANGED); DBG_OBJ_SET_NUM ("extremes.minWidth", extremes->minWidth); + DBG_OBJ_SET_NUM ("extremes.minWidthIntrinsic", + extremes->minWidthIntrinsic); DBG_OBJ_SET_NUM ("extremes.maxWidth", extremes->maxWidth); + DBG_OBJ_SET_NUM ("extremes.maxWidthIntrinsic", + extremes->maxWidthIntrinsic); } else *extremes = this->extremes; leaveGetExtremes (); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -369,10 +772,9 @@ void Widget::sizeAllocate (Allocation *allocation) assert (!getExtremesEntered ()); assert (resizeIdleEntered ()); - DBG_OBJ_MSGF ("resize", 0, "<b>sizeAllocate</b> (%d, %d; %d * (%d + %d))", - allocation->x, allocation->y, allocation->width, - allocation->ascent, allocation->descent); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 0, "sizeAllocate", "%d, %d; %d * (%d + %d)", + allocation->x, allocation->y, allocation->width, + allocation->ascent, allocation->descent); DBG_OBJ_MSGF ("resize", 1, "old allocation (%d, %d; %d * (%d + %d)); needsAllocate: %s", @@ -435,7 +837,7 @@ void Widget::sizeAllocate (Allocation *allocation) leaveSizeAllocate (); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } bool Widget::buttonPress (EventButton *event) @@ -507,6 +909,28 @@ void Widget::setStyle (style::Style *style) queueResize (0, true); else queueDraw (); + + // These should better be attributed to the style itself, and a + // script processing RTFL messages could transfer it to something + // equivalent: + + DBG_OBJ_SET_NUM ("style.margin.top", style->margin.top); + DBG_OBJ_SET_NUM ("style.margin.bottom", style->margin.bottom); + DBG_OBJ_SET_NUM ("style.margin.left", style->margin.left); + DBG_OBJ_SET_NUM ("style.margin.right", style->margin.right); + + DBG_OBJ_SET_NUM ("style.border-width.top", style->borderWidth.top); + DBG_OBJ_SET_NUM ("style.border-width.bottom", style->borderWidth.bottom); + DBG_OBJ_SET_NUM ("style.border-width.left", style->borderWidth.left); + DBG_OBJ_SET_NUM ("style.border-width.right", style->borderWidth.right); + + DBG_OBJ_SET_NUM ("style.padding.top", style->padding.top); + DBG_OBJ_SET_NUM ("style.padding.bottom", style->padding.bottom); + DBG_OBJ_SET_NUM ("style.padding.left", style->padding.left); + DBG_OBJ_SET_NUM ("style.padding.right", style->padding.right); + + DBG_OBJ_SET_NUM ("style.border-spacing (h)", style->hBorderSpacing); + DBG_OBJ_SET_NUM ("style.border-spacing (v)", style->vBorderSpacing); } /** @@ -781,14 +1205,6 @@ void Widget::getPaddingArea (int *xPad, int *yPad, int *widthPad, - style->margin.bottom - style->borderWidth.bottom; } -void Widget::getExtremesImpl (Extremes *extremes) -{ - /* Simply return the requisition width */ - Requisition requisition; - sizeRequest (&requisition); - extremes->minWidth = extremes->maxWidth = requisition.width; -} - void Widget::sizeAllocateImpl (Allocation *allocation) { } @@ -801,6 +1217,254 @@ void Widget::markExtremesChange (int ref) { } +int Widget::applyPerWidth (int containerWidth, style::Length perWidth) +{ + return style::multiplyWithPerLength (containerWidth, perWidth) + + boxDiffWidth (); +} + +int Widget::applyPerHeight (int containerHeight, style::Length perHeight) +{ + return style::multiplyWithPerLength (containerHeight, perHeight) + + boxDiffHeight (); +} + +int Widget::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + // This is a halfway suitable implementation for all + // containers. For simplification, this will be used during the + // development; then, a differentiation could be possible. + + // TODO Correct by extremes? + + DBG_OBJ_ENTER ("resize", 0, "getAvailWidthOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int width; + + if (child->getStyle()->width == style::LENGTH_AUTO) { + DBG_OBJ_MSG ("resize", 1, "no specification"); + int availWidth = getAvailWidth (forceValue); + if (availWidth == -1) + width = -1; + else + width = misc::max (availWidth - boxDiffWidth (), 0); + } else { + // In most cases, the toplevel widget should be a container, so + // the container is non-NULL when the parent is non-NULL. Just + // in case ...: + Widget *effContainer = + child->container ? child->container : child->parent; + + if (effContainer == this) { + if (style::isAbsLength (child->getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (child->getStyle()->width)); + width = misc::max (style::absLengthVal (child->getStyle()->width) + + child->boxDiffWidth (), 0); + } else { + assert (style::isPerLength (child->getStyle()->width)); + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->width)); + + int availWidth = getAvailWidth (forceValue); + if (availWidth == -1) + width = -1; + else + width = + misc::max (child->applyPerWidth (availWidth - boxDiffWidth (), + child->getStyle()->width), + 0); + } + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + width = effContainer->getAvailWidthOfChild (child, forceValue); + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + + return width; +} + +int Widget::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + // Again, a suitable implementation for all widgets (perhaps). + + // TODO Correct by extremes? (Height extemes?) + + DBG_OBJ_ENTER ("resize", 0, "getAvailHeightOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int height; + + if (child->getStyle()->height == style::LENGTH_AUTO) { + DBG_OBJ_MSG ("resize", 1, "no specification"); + int availHeight = getAvailHeight (forceValue); + if (availHeight == -1) + height = -1; + else + height = misc::max (availHeight - boxDiffHeight (), 0); + } else { + // In most cases, the toplevel widget should be a container, so + // the container is non-NULL when the parent is non-NULL. Just + // in case ...: + Widget *effContainer = + child->container ? child->container : child->parent; + + if (effContainer == this) { + if (style::isAbsLength (child->getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (child->getStyle()->height)); + height = misc::max (style::absLengthVal (child->getStyle()->height) + + child->boxDiffHeight (), 0); + } else { + assert (style::isPerLength (child->getStyle()->height)); + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->height)); + + int availHeight = getAvailHeight (forceValue); + if (availHeight == -1) + height = -1; + else + height = + misc::max (child->applyPerHeight (availHeight - + boxDiffHeight (), + child->getStyle()->height), + 0); + } + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + height = effContainer->getAvailHeightOfChild (child, forceValue); + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + + return height; +} + +void Widget::correctRequisitionOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) +{ + // Again, a suitable implementation for all widgets (perhaps). + + // TODO Correct by extremes? + + DBG_OBJ_ENTER ("resize", 0, "correctRequisitionOfChild", + "%p, %d * (%d + %d), ...", child, requisition->width, + requisition->ascent, requisition->descent); + + correctReqWidthOfChild (child, requisition); + correctReqHeightOfChild (child, requisition, splitHeightFun); + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctReqWidthOfChild (Widget *child, Requisition *requisition) +{ + DBG_OBJ_ENTER ("resize", 0, "correctReqWidthOfChild", "%p, %d * (%d + %d)", + child, requisition->width, requisition->ascent, + requisition->descent); + + if (style::isAbsLength (child->getStyle()->width)) + requisition->width = style::absLengthVal (child->getStyle()->width) + + child->boxDiffWidth (); + else if (style::isPerLength (child->getStyle()->width)) { + int availWidth = getAvailWidth (false); + if (availWidth != -1) { + int containerWidth = availWidth - boxDiffWidth (); + requisition->width = child->applyPerWidth (containerWidth, + child->getStyle()->width); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctReqHeightOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)) +{ + DBG_OBJ_ENTER ("resize", 0, "correctReqHeightOfChild", + "%p, %d * (%d + %d), ...", child, requisition->width, + requisition->ascent, requisition->descent); + + // TODO Perhaps split first, then add box ascent and descent. + if (style::isAbsLength (child->getStyle()->height)) + splitHeightFun (style::absLengthVal (child->getStyle()->height) + + child->boxDiffHeight (), + &requisition->ascent, &requisition->descent); + else if (style::isPerLength (child->getStyle()->height)) { +#if 0 + // TODO Percentage heights are somewhat more complicated. Has to + // be clarified. See also Widget::correctRequisition. + + int availHeight = getAvailHeight (false); + if (availHeight != -1) { + int containerHeight = availHeight - boxDiffHeight (); + splitHeightFun (child->applyPerHeight (containerHeight, + child->getStyle()->height), + &requisition->ascent, &requisition->descent); + } +#endif + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctExtremesOfChild (Widget *child, Extremes *extremes) +{ + // See comment in correctRequisitionOfChild. + + // TODO Extremes only corrected? + + DBG_OBJ_ENTER ("resize", 0, "correctExtremesOfChild", "%p, %d / %d", + child, extremes->minWidth, extremes->maxWidth); + + if (style::isAbsLength (child->getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (child->getStyle()->width)); + extremes->minWidth = extremes->maxWidth = + style::absLengthVal (child->getStyle()->width) + + child->boxDiffWidth (); + } else if (style::isPerLength (child->getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->width)); + int availWidth = getAvailWidth (false); + if (availWidth != -1) { + int containerWidth = availWidth - boxDiffWidth (); + DBG_OBJ_MSGF ("resize", 1, "containerWidth = %d - %d = %d", + availWidth, boxDiffWidth (), containerWidth); + extremes->minWidth = extremes->maxWidth = + child->applyPerWidth (containerWidth, child->getStyle()->width); + } + } else + DBG_OBJ_MSG ("resize", 1, "no specification"); + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_LEAVE (); +} + /** * \brief This method is called after a widget has been set as the top of a * widget tree. @@ -820,16 +1484,16 @@ void Widget::notifySetParent() { } -void Widget::setWidth (int width) -{ -} - -void Widget::setAscent (int ascent) +bool Widget::isBlockLevel () { + // Most widgets are not block-level. + return false; } -void Widget::setDescent (int descent) +bool Widget::isPossibleContainer () { + // In most (all?) cases identical to: + return isBlockLevel (); } bool Widget::buttonPressImpl (EventButton *event) @@ -849,7 +1513,7 @@ bool Widget::motionNotifyImpl (EventMotion *event) void Widget::enterNotifyImpl (EventCrossing *) { - core::style::Tooltip *tooltip = getStyle()->x_tooltip; + style::Tooltip *tooltip = getStyle()->x_tooltip; if (tooltip) tooltip->onEnter(); @@ -857,7 +1521,7 @@ void Widget::enterNotifyImpl (EventCrossing *) void Widget::leaveNotifyImpl (EventCrossing *) { - core::style::Tooltip *tooltip = getStyle()->x_tooltip; + style::Tooltip *tooltip = getStyle()->x_tooltip; if (tooltip) tooltip->onLeave(); @@ -869,5 +1533,25 @@ void Widget::removeChild (Widget *child) misc::assertNotReached (); } +// ---------------------------------------------------------------------- + +void splitHeightPreserveAscent (int height, int *ascent, int *descent) +{ + *descent = height - *ascent; + if (*descent < 0) { + *descent = 0; + *ascent = height; + } +} + +void splitHeightPreserveDescent (int height, int *ascent, int *descent) +{ + *ascent = height - *descent; + if (*ascent < 0) { + *ascent = 0; + *descent = height; + } +} + } // namespace core } // namespace dw diff --git a/dw/widget.hh b/dw/widget.hh index e3d9cbe1..bfbf1e91 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -69,32 +69,11 @@ protected: EXTREMES_CHANGED = 1 << 5, /** - * \brief Set by the widget itself (in the constructor), when set... - * methods are implemented. - * - * Will hopefully be removed, after redesigning the size model. - */ - USES_HINTS = 1 << 6, - - /** - * \brief Set by the widget itself (in the constructor), when it contains - * some contents, e.g. an image, as opposed to a horizontal ruler. - * - * Will hopefully be removed, after redesigning the size model. - */ - HAS_CONTENTS = 1 << 7, - - /** * \brief Set, when a widget was already once allocated, * * The dw::Image widget uses this flag, see dw::Image::setBuffer. */ - WAS_ALLOCATED = 1 << 8, - - /** - * \brief Set for block-level widgets (as opposed to inline widgets) - */ - BLOCK_LEVEL = 1 << 9, + WAS_ALLOCATED = 1 << 6, }; /** @@ -126,12 +105,24 @@ private: Widget *parent; /** + * \brief ... + */ + Widget *quasiParent; + + /** * \brief The generating widget, NULL for top-level widgets, or if - * not set; in the latter case, the effective generator (see - * getGenerator) is the parent. + * not set; in the latter case, the effective generator (see + * getGenerator) is the parent. */ Widget *generator; + /** + * \brief The containing widget, equivalent to the "containing + * block" defined by CSS. May be NULL, in this case the viewport + * is used. + */ + Widget *container; + style::Style *style; Flags flags; @@ -164,7 +155,10 @@ private: */ bool buttonSensitiveSet; - void actualQueueResize (int ref, bool extremesChanged); + void queueResize (int ref, bool extremesChanged, bool fast); + inline void queueResizeFast (int ref, bool extremesChanged) + { queueResize (ref, extremesChanged, true); } + void actualQueueResize (int ref, bool extremesChanged, bool fast); public: /** @@ -191,20 +185,23 @@ protected: Layout *layout; + /** + * \brief Space around the margin box. Allocation is extraSpace + + * margin + border + padding + contents; + */ + style::Box extraSpace; + /*inline void printFlags () { DBG_IF_RTFL { char buf[10 * 3 - 1 + 1]; - snprintf (buf, sizeof (buf), "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s", + snprintf (buf, sizeof (buf), "%s:%s:%s:%s:%s:%s:%s", (flags & RESIZE_QUEUED) ? "Rq" : "--", (flags & EXTREMES_QUEUED) ? "Eq" : "--", (flags & NEEDS_RESIZE) ? "nR" : "--", (flags & NEEDS_ALLOCATE) ? "nA" : "--", (flags & ALLOCATE_QUEUED) ? "Aq" : "--", (flags & EXTREMES_CHANGED) ? "Ec" : "--", - (flags & USES_HINTS) ? "uh" : "--", - (flags & HAS_CONTENTS) ? "hc" : "--", - (flags & WAS_ALLOCATED) ? "wA" : "--", - (flags & BLOCK_LEVEL) ? "bl" : "--"); + (flags & WAS_ALLOCATED) ? "wA" : "--"); DBG_OBJ_SET_SYM ("flags", buf); } }*/ @@ -241,26 +238,11 @@ protected: DBG_OBJ_SET_SYM ("flags.EXTREMES_CHANGED", (flags & EXTREMES_CHANGED) ? "true" : "false"); break; - - case USES_HINTS: - DBG_OBJ_SET_SYM ("flags.USES_HINTS", - (flags & USES_HINTS) ? "true" : "false"); - break; - - case HAS_CONTENTS: - DBG_OBJ_SET_SYM ("flags.HAS_CONTENTS", - (flags & HAS_CONTENTS) ? "true" : "false"); - break; - + case WAS_ALLOCATED: DBG_OBJ_SET_SYM ("flags.WAS_ALLOCATED", (flags & WAS_ALLOCATED) ? "true" : "false"); - break; - - case BLOCK_LEVEL: - DBG_OBJ_SET_SYM ("flags.BLOCK_LEVEL", - (flags & BLOCK_LEVEL) ? "true" : "false"); - break; + break; } } } @@ -272,11 +254,10 @@ protected: inline void queueDraw () - { - queueDrawArea (0, 0, allocation.width, getHeight()); - } + { queueDrawArea (0, 0, allocation.width, getHeight()); } void queueDrawArea (int x, int y, int width, int height); - void queueResize (int ref, bool extremesChanged); + inline void queueResize (int ref, bool extremesChanged) + { queueResize (ref, extremesChanged, false); } /** * \brief See \ref dw-widget-sizes. @@ -286,7 +267,7 @@ protected: /** * \brief See \ref dw-widget-sizes. */ - virtual void getExtremesImpl (Extremes *extremes); + virtual void getExtremesImpl (Extremes *extremes) = 0; /** * \brief See \ref dw-widget-sizes. @@ -309,6 +290,24 @@ protected: */ virtual void markExtremesChange (int ref); + virtual int getAvailWidthOfChild (Widget *child, bool forceValue); + virtual int getAvailHeightOfChild (Widget *child, bool forceValue); + virtual void correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)); + void correctReqWidthOfChild (Widget *child, Requisition *requisition); + void correctReqHeightOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + virtual void correctExtremesOfChild (Widget *child, Extremes *extremes); + + virtual void containerSizeChangedForChildren (); + + virtual bool affectedByContainerSizeChange (); + virtual bool affectsSizeChangeContainerChild (Widget *child); + virtual bool usesAvailWidth (); + virtual bool usesAvailHeight (); + virtual void notifySetAsTopLevel(); virtual void notifySetParent(); @@ -394,11 +393,9 @@ public: inline bool allocateQueued () { return flags & ALLOCATE_QUEUED; } inline bool extremesChanged () { return flags & EXTREMES_CHANGED; } inline bool wasAllocated () { return flags & WAS_ALLOCATED; } - inline bool usesHints () { return flags & USES_HINTS; } - inline bool hasContents () { return flags & HAS_CONTENTS; } - inline bool blockLevel () { return flags & BLOCK_LEVEL; } void setParent (Widget *parent); + void setQuasiParent (Widget *quasiParent); void setGenerator (Widget *generator) { this->generator = generator; } @@ -406,12 +403,34 @@ public: /** \todo I do not like this. */ inline Allocation *getAllocation () { return &allocation; } + inline int boxOffsetX () + { return extraSpace.left + getStyle()->boxOffsetX (); } + inline int boxRestWidth () + { return extraSpace.right + getStyle()->boxRestWidth (); } + inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); } + inline int boxOffsetY () + { return extraSpace.top + getStyle()->boxOffsetY (); } + inline int boxRestHeight () + { return extraSpace.bottom + getStyle()->boxRestHeight (); } + inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } + void sizeRequest (Requisition *requisition); void getExtremes (Extremes *extremes); void sizeAllocate (Allocation *allocation); - virtual void setWidth (int width); - virtual void setAscent (int ascent); - virtual void setDescent (int descent); + + int getAvailWidth (bool forceValue); + int getAvailHeight (bool forceValue); + void correctRequisition (Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + void correctExtremes (Extremes *extremes); + + virtual int applyPerWidth (int containerWidth, style::Length perWidth); + virtual int applyPerHeight (int containerHeight, style::Length perHeight); + + virtual bool isBlockLevel (); + virtual bool isPossibleContainer (); + + void containerSizeChanged (); bool intersects (Rectangle *area, Rectangle *intersection); @@ -470,6 +489,9 @@ public: virtual void removeChild (Widget *child); }; +void splitHeightPreserveAscent (int height, int *ascent, int *descent); +void splitHeightPreserveDescent (int height, int *ascent, int *descent); + } // namespace core } // namespace dw diff --git a/lout/debug.hh b/lout/debug.hh index b823f171..d4e545a2 100644 --- a/lout/debug.hh +++ b/lout/debug.hh @@ -108,6 +108,48 @@ fflush (stdout); \ } D_STMT_END +#define DBG_OBJ_ENTER0(aspect, prio, funname) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:\n", \ + RTFL_PREFIX_ARGS, this, aspect, prio, funname); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:\n", \ + RTFL_PREFIX_ARGS, obj, aspect, prio, funname); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) \ + D_STMT_START { \ + fflush (stdout); \ + printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:" fmt "\n", \ + RTFL_PREFIX_ARGS, this, aspect, prio, funname, __VA_ARGS__); \ + } D_STMT_END + +#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) \ + D_STMT_START { \ + fflush (stdout); \ + printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:" fmt "\n", \ + RTFL_PREFIX_ARGS, obj, aspect, prio, funname, __VA_ARGS__); \ + } D_STMT_END + +#define DBG_OBJ_LEAVE() \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-leave:%p\n", \ + RTFL_PREFIX_ARGS, this); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_LEAVE_O(obj) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-leave:%p\n", \ + RTFL_PREFIX_ARGS, obj); \ + fflush (stdout); \ + } D_STMT_END + #define DBG_OBJ_CREATE(klass) \ D_STMT_START { \ printf (RTFL_PREFIX_FMT "obj-create:%p:%s\n", \ @@ -196,6 +238,20 @@ fflush (stdout); \ } D_STMT_END +#define DBG_OBJ_SET_BOOL(var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \ + RTFL_PREFIX_ARGS, this, var, val ? "true" : "false"); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_BOOL_O(obj, var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \ + RTFL_PREFIX_ARGS, obj, var, val ? "true" : "false"); \ + fflush (stdout); \ + } D_STMT_END + #define DBG_OBJ_ARRSET_NUM(var, ind, val) \ D_STMT_START { \ printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%d\n", \ @@ -263,35 +319,43 @@ #define DBG_IF_RTFL if(0) -#define DBG_OBJ_MSG(aspect, prio, msg) D_STMT_NOP -#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) D_STMT_NOP -#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) D_STMT_NOP -#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) D_STMT_NOP -#define DBG_OBJ_MSG_START() D_STMT_NOP -#define DBG_OBJ_MSG_START_O(obj) D_STMT_NOP -#define DBG_OBJ_MSG_END() D_STMT_NOP -#define DBG_OBJ_MSG_END_O(obj) D_STMT_NOP -#define DBG_OBJ_CREATE(klass) D_STMT_NOP -#define DBG_OBJ_DELETE() D_STMT_NOP -#define DBG_OBJ_BASECLASS(klass) D_STMT_NOP -#define DBG_OBJ_ASSOC_PARENT(parent) D_STMT_NOP -#define DBG_OBJ_ASSOC_CHILD(child) D_STMT_NOP -#define DBG_OBJ_ASSOC(parent, child) D_STMT_NOP -#define DBG_OBJ_SET_NUM(var, val) D_STMT_NOP -#define DBG_OBJ_SET_NUM_O(obj, var, val) D_STMT_NOP -#define DBG_OBJ_SET_SYM(var, val) D_STMT_NOP -#define DBG_OBJ_SET_STR(var, val) D_STMT_NOP -#define DBG_OBJ_SET_PTR(var, val) D_STMT_NOP -#define DBG_OBJ_SET_PTR_O(obj, var, val) D_STMT_NOP -#define DBG_OBJ_ARRSET_NUM(var, ind, val) D_STMT_NOP -#define DBG_OBJ_ARRSET_SYM(var, ind, val) D_STMT_NOP -#define DBG_OBJ_ARRSET_STR(var, ind, val) D_STMT_NOP -#define DBG_OBJ_ARRSET_PTR(var, ind, val) D_STMT_NOP -#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) D_STMT_NOP -#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) D_STMT_NOP -#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) D_STMT_NOP -#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) D_STMT_NOP -#define DBG_OBJ_COLOR(klass, color) D_STMT_NOP +#define DBG_OBJ_MSG(aspect, prio, msg) D_STMT_NOP +#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) D_STMT_NOP +#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) D_STMT_NOP +#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) D_STMT_NOP +#define DBG_OBJ_MSG_START() D_STMT_NOP +#define DBG_OBJ_MSG_START_O(obj) D_STMT_NOP +#define DBG_OBJ_MSG_END() D_STMT_NOP +#define DBG_OBJ_MSG_END_O(obj) D_STMT_NOP +#define DBG_OBJ_ENTER0(aspect, prio, funname) D_STMT_NOP +#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) D_STMT_NOP +#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) D_STMT_NOP +#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) D_STMT_NOP +#define DBG_OBJ_LEAVE() D_STMT_NOP +#define DBG_OBJ_LEAVE_O(obj) D_STMT_NOP +#define DBG_OBJ_CREATE(klass) D_STMT_NOP +#define DBG_OBJ_DELETE() D_STMT_NOP +#define DBG_OBJ_BASECLASS(klass) D_STMT_NOP +#define DBG_OBJ_ASSOC_PARENT(parent) D_STMT_NOP +#define DBG_OBJ_ASSOC_CHILD(child) D_STMT_NOP +#define DBG_OBJ_ASSOC(parent, child) D_STMT_NOP +#define DBG_OBJ_SET_NUM(var, val) D_STMT_NOP +#define DBG_OBJ_SET_NUM_O(obj, var, val) D_STMT_NOP +#define DBG_OBJ_SET_SYM(var, val) D_STMT_NOP +#define DBG_OBJ_SET_STR(var, val) D_STMT_NOP +#define DBG_OBJ_SET_PTR(var, val) D_STMT_NOP +#define DBG_OBJ_SET_PTR_O(obj, var, val) D_STMT_NOP +#define DBG_OBJ_SET_BOOL(var, val) D_STMT_NOP +#define DBG_OBJ_SET_BOOL_O(obj, var, val) D_STMT_NOP +#define DBG_OBJ_ARRSET_NUM(var, ind, val) D_STMT_NOP +#define DBG_OBJ_ARRSET_SYM(var, ind, val) D_STMT_NOP +#define DBG_OBJ_ARRSET_STR(var, ind, val) D_STMT_NOP +#define DBG_OBJ_ARRSET_PTR(var, ind, val) D_STMT_NOP +#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) D_STMT_NOP +#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) D_STMT_NOP +#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) D_STMT_NOP +#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) D_STMT_NOP +#define DBG_OBJ_COLOR(klass, color) D_STMT_NOP #endif /* DBG_RTFL */ diff --git a/lout/misc.hh b/lout/misc.hh index 3082f33c..4c20208a 100644 --- a/lout/misc.hh +++ b/lout/misc.hh @@ -223,6 +223,14 @@ public: assert (i >= 0 && this->num - i > 0); this->array[i] = t; } + + /** + * \brief Store an object at the end of the vector. + */ + inline void setLast (T t) { + assert (this->num > 0); + this->array[this->num - 1] = t; + } }; /** @@ -488,6 +496,13 @@ public: inline void set (int i, T t) { *(this->getRef(i)) = t; } + + /** + * \brief Store an object at the end of the vector. + */ + inline void setLast (T t) { + *(this->getLastRef()) = t; + } }; /** diff --git a/src/table.cc b/src/table.cc index a3002ebf..29b5fbc9 100644 --- a/src/table.cc +++ b/src/table.cc @@ -15,6 +15,7 @@ #include "dw/style.hh" #include "dw/textblock.hh" #include "dw/table.hh" +#include "dw/simpletablecell.hh" #include "prefs.h" #include "msg.h" @@ -445,11 +446,11 @@ static void Html_tag_content_table_cell(DilloHtml *html, rowspan = MAX(1, strtol (attrbuf, NULL, 10)); if (html->style ()->textAlign == TEXT_ALIGN_STRING) - col_tb = new dw::TableCell ( + col_tb = new AlignedTableCell ( ((dw::Table*)S_TOP(html)->table)->getCellRef (), prefs.limit_text_width); else - col_tb = new Textblock (prefs.limit_text_width); + col_tb = new SimpleTableCell (prefs.limit_text_width); if (html->style()->borderCollapse == BORDER_MODEL_COLLAPSE){ Html_set_collapsing_border_model(html, col_tb); diff --git a/test/Makefile.am b/test/Makefile.am index f0ac2b0a..3b474466 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -17,6 +17,7 @@ noinst_PROGRAMS = \ dw-images-scaled \ dw-images-scaled2 \ dw-lists \ + dw-simple-container-test \ dw-table-aligned \ dw-table \ dw-border-test \ @@ -119,6 +120,17 @@ dw_lists_LDADD = \ $(top_builddir)/lout/liblout.a \ @LIBFLTK_LIBS@ @LIBX11_LIBS@ +dw_simple_container_test_SOURCES = \ + dw_simple_container.hh \ + dw_simple_container.cc \ + dw_simple_container_test.cc +dw_simple_container_test_LDADD = \ + $(top_builddir)/dw/libDw-widgets.a \ + $(top_builddir)/dw/libDw-fltk.a \ + $(top_builddir)/dw/libDw-core.a \ + $(top_builddir)/lout/liblout.a \ + @LIBFLTK_LIBS@ + dw_table_aligned_SOURCES = dw_table_aligned.cc dw_table_aligned_LDADD = \ $(top_builddir)/dw/libDw-widgets.a \ diff --git a/test/dw_simple_container.cc b/test/dw_simple_container.cc new file mode 100644 index 00000000..c4a1aa2a --- /dev/null +++ b/test/dw_simple_container.cc @@ -0,0 +1,241 @@ +/* + * Dillo Widget + * + * Copyright 2014 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, see <http://www.gnu.org/licenses/>. + */ + +#include <math.h> + +#include "dw_simple_container.hh" + +using namespace dw::core; +using namespace dw::core::style; +using namespace lout::misc; + +namespace dw { + +int SimpleContainer::CLASS_ID = -1; + +// ---------------------------------------------------------------------- + +SimpleContainer::SimpleContainerIterator::SimpleContainerIterator + (SimpleContainer *simpleContainer, Content::Type mask, bool atEnd) : + Iterator (simpleContainer, mask, atEnd) +{ + content.type = atEnd ? Content::END : Content::START; +} + +lout::object::Object *SimpleContainer::SimpleContainerIterator::clone () +{ + SimpleContainerIterator *sci = + new SimpleContainerIterator ((SimpleContainer*)getWidget(), + getMask(), false); + sci->content = content; + return sci; +} + +int SimpleContainer::SimpleContainerIterator::index () +{ + switch (content.type) { + case Content::START: + return 0; + case Content::WIDGET_IN_FLOW: + return 1; + case Content::END: + return 2; + default: + assertNotReached (); + return 0; + } +} + +int SimpleContainer::SimpleContainerIterator::compareTo + (lout::object::Comparable *other) +{ + return index () - ((SimpleContainerIterator*)other)->index (); +} + +bool SimpleContainer::SimpleContainerIterator::next () +{ + SimpleContainer *simpleContainer = (SimpleContainer*)getWidget(); + + if (content.type == Content::END) + return false; + + // simple containers only contain widgets: + if ((getMask() & Content::WIDGET_IN_FLOW) == 0) { + content.type = Content::END; + return false; + } + + if (content.type == Content::START) { + if (simpleContainer->child != NULL) { + content.type = Content::WIDGET_IN_FLOW; + content.widget = simpleContainer->child; + return true; + } else { + content.type = Content::END; + return false; + } + } else /* if (content.type == Content::WIDGET) */ { + content.type = Content::END; + return false; + } +} + +bool SimpleContainer::SimpleContainerIterator::prev () +{ + SimpleContainer *simpleContainer = (SimpleContainer*)getWidget(); + + if (content.type == Content::START) + return false; + + // simple containers only contain widgets: + if ((getMask() & Content::WIDGET_IN_FLOW) == 0) { + content.type = Content::START; + return false; + } + + if (content.type == Content::END) { + if (simpleContainer->child != NULL) { + content.type = Content::WIDGET_IN_FLOW; + content.widget = simpleContainer->child; + return true; + } else { + content.type = Content::START; + return false; + } + } else /* if (content.type == Content::WIDGET) */ { + content.type = Content::START; + return false; + } +} + +void SimpleContainer::SimpleContainerIterator::highlight (int start, + int end, + HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void SimpleContainer::SimpleContainerIterator::unhighlight (int direction, + HighlightLayer + layer) +{ + /** todo Needs this an implementation? */ +} + +void SimpleContainer::SimpleContainerIterator::getAllocation (int start, + int end, + Allocation + *allocation) +{ + /** \bug Not implemented. */ +} + +// ---------------------------------------------------------------------- + +SimpleContainer::SimpleContainer () +{ + registerName ("dw::SimpleContainer", &CLASS_ID); + child = NULL; +} + +SimpleContainer::~SimpleContainer () +{ + if (child) + delete child; +} + +void SimpleContainer::sizeRequestImpl (Requisition *requisition) +{ + Requisition childReq; + if (child) + child->sizeRequest (&childReq); + else + childReq.width = childReq.ascent = childReq.descent = 0; + + requisition->width = childReq.width + boxDiffWidth (); + requisition->ascent = childReq.ascent + boxOffsetY (); + requisition->descent = childReq.descent + boxRestHeight (); + + correctRequisition (requisition, splitHeightPreserveAscent); +} + + +void SimpleContainer::getExtremesImpl (Extremes *extremes) +{ + Extremes childExtr; + if (child) + child->getExtremes (&childExtr); + else + childExtr.minWidth = childExtr.maxWidth = 0; + + extremes->minWidth = childExtr.minWidth + boxDiffWidth (); + extremes->maxWidth = childExtr.maxWidth + boxDiffWidth (); + + correctExtremes (extremes); +} + + +void SimpleContainer::sizeAllocateImpl (Allocation *allocation) +{ + Allocation childAlloc; + + if (child) { + childAlloc.x = allocation->x + boxOffsetX (); + childAlloc.y = allocation->y + boxOffsetY (); + childAlloc.width = allocation->width - boxDiffWidth (); + childAlloc.ascent = allocation->ascent - boxOffsetY (); + childAlloc.descent = allocation->descent - boxRestHeight (); + child->sizeAllocate (&childAlloc); + } +} + +void SimpleContainer::draw (View *view, Rectangle *area) +{ + drawWidgetBox (view, area, false); + Rectangle childArea; + if (child && child->intersects (area, &childArea)) + child->draw (view, &childArea); +} + +Iterator *SimpleContainer::iterator (Content::Type mask, bool atEnd) +{ + return new SimpleContainerIterator (this, mask, atEnd); +} + +void SimpleContainer::removeChild (Widget *child) +{ + assert (child == this->child); + this->child = NULL; + + queueResize (0, true); +} + +void SimpleContainer::setChild (Widget *child) +{ + if (this->child) + delete this->child; + + this->child = child; + if (this->child) + this->child->setParent (this); + + queueResize (0, true); +} + +} // namespace dw diff --git a/test/dw_simple_container.hh b/test/dw_simple_container.hh new file mode 100644 index 00000000..c8a629db --- /dev/null +++ b/test/dw_simple_container.hh @@ -0,0 +1,56 @@ +#ifndef __DW_SIMPLE_CONTAINER_HH__ +#define __DWR_SIMPLE_CONTAINER_HH__ + +#include "dw/core.hh" + +namespace dw { + +/** + * Simple widget used for testing concepts. + */ +class SimpleContainer: public core::Widget +{ +private: + class SimpleContainerIterator: public core::Iterator + { + private: + int index (); + + public: + SimpleContainerIterator (SimpleContainer *simpleContainer, + core::Content::Type mask, + bool atEnd); + + lout::object::Object *clone (); + int compareTo (lout::object::Comparable *other); + + bool next (); + bool prev (); + void highlight (int start, int end, core::HighlightLayer layer); + void unhighlight (int direction, core::HighlightLayer layer); + void getAllocation (int start, int end, core::Allocation *allocation); + }; + + Widget *child; + +protected: + void sizeRequestImpl (core::Requisition *requisition); + void getExtremesImpl (core::Extremes *extremes); + void sizeAllocateImpl (core::Allocation *allocation); + +public: + static int CLASS_ID; + + SimpleContainer (); + ~SimpleContainer (); + + void draw (core::View *view, core::Rectangle *area); + core::Iterator *iterator (core::Content::Type mask, bool atEnd); + void removeChild (Widget *child); + + void setChild (core::Widget *child); +}; + +} // namespace dw + +#endif // __DW_SIMPLE_CONTAINER_HH__ diff --git a/test/dw_simple_container_test.cc b/test/dw_simple_container_test.cc new file mode 100644 index 00000000..20697dd6 --- /dev/null +++ b/test/dw_simple_container_test.cc @@ -0,0 +1,114 @@ +/* + * Dillo Widget + * + * Copyright 2014 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, see <http://www.gnu.org/licenses/>. + */ + + + +#include <FL/Fl_Window.H> +#include <FL/Fl.H> + +#include "../dw/core.hh" +#include "../dw/fltkcore.hh" +#include "../dw/fltkviewport.hh" +#include "dw_simple_container.hh" +#include "../dw/textblock.hh" + +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace dw::fltk; + +int main(int argc, char **argv) +{ + FltkPlatform *platform = new FltkPlatform (); + Layout *layout = new Layout (platform); + + Fl_Window *window = new Fl_Window(200, 300, "Dw Example"); + window->box(FL_NO_BOX); + window->begin(); + + FltkViewport *viewport = new FltkViewport (0, 0, 200, 300); + layout->attachView (viewport); + + StyleAttrs styleAttrs; + styleAttrs.initValues (); + styleAttrs.margin.setVal (5); + + FontAttrs fontAttrs; + fontAttrs.name = "Bitstream Charter"; + fontAttrs.size = 14; + fontAttrs.weight = 400; + fontAttrs.style = FONT_STYLE_NORMAL; + fontAttrs.letterSpacing = 0; + fontAttrs.fontVariant = FONT_VARIANT_NORMAL; + styleAttrs.font = style::Font::create (layout, &fontAttrs); + + styleAttrs.color = Color::create (layout, 0x000000); + styleAttrs.backgroundColor = Color::create (layout, 0xffffff); + + Style *textblockStyle1 = Style::create (&styleAttrs); + + styleAttrs.backgroundColor = NULL; + styleAttrs.margin.setVal (0); + + Style *textblockStyle2 = Style::create (&styleAttrs); + Style *wordStyle = Style::create (&styleAttrs); + + styleAttrs.borderWidth.setVal (5); + styleAttrs.setBorderColor (Color::create (layout, 0x800080)); + styleAttrs.setBorderStyle (BORDER_DASHED); + styleAttrs.padding.setVal (5); + + Style *containerStyle = Style::create (&styleAttrs); + + Textblock *textblock1 = new Textblock (false); + textblock1->setStyle (textblockStyle1); + layout->setWidget (textblock1); + + SimpleContainer *simpleContainer = new SimpleContainer (); + simpleContainer->setStyle (containerStyle); + textblock1->addWidget (simpleContainer, containerStyle); + + Textblock *textblock2 = new Textblock (false); + textblock2->setStyle (textblockStyle2); + simpleContainer->setChild (textblock2); + + const char *words[] = { "This", "is", "only", "a", "short", "paragraph.", + NULL }; + + for(int j = 0; words[j]; j++) { + textblock2->addText(words[j], wordStyle); + textblock2->addSpace(wordStyle); + } + + textblockStyle1->unref(); + textblockStyle2->unref(); + containerStyle->unref(); + wordStyle->unref(); + + textblock1->flush (); + textblock2->flush (); + + window->resizable(viewport); + window->show(); + int errorCode = Fl::run(); + + delete layout; + + return errorCode; +} diff --git a/test/dw_table.cc b/test/dw_table.cc index 5416d05b..9bec1a09 100644 --- a/test/dw_table.cc +++ b/test/dw_table.cc @@ -26,7 +26,6 @@ #include "../dw/fltkcore.hh" #include "../dw/fltkviewport.hh" #include "../dw/table.hh" -#include "../dw/tablecell.hh" using namespace dw; using namespace dw::core; diff --git a/test/dw_table_aligned.cc b/test/dw_table_aligned.cc index 96cb0602..bef3d521 100644 --- a/test/dw_table_aligned.cc +++ b/test/dw_table_aligned.cc @@ -26,7 +26,7 @@ #include "../dw/fltkcore.hh" #include "../dw/fltkviewport.hh" #include "../dw/table.hh" -#include "../dw/tablecell.hh" +#include "../dw/alignedtablecell.hh" using namespace dw; using namespace dw::core; @@ -87,10 +87,10 @@ int main(int argc, char **argv) Style *wordStyle = Style::create (&styleAttrs); - TableCell *ref = NULL; + AlignedTableCell *ref = NULL; for(int i = 0; i < 10; i++) { //for(int i = 0; i < 1; i++) { - TableCell *cell = new TableCell (ref, false); + AlignedTableCell *cell = new AlignedTableCell (ref, false); cell->setStyle (cellStyle); ref = cell; table->addRow (wordStyle); |