diff options
58 files changed, 6034 insertions, 662 deletions
@@ -27,6 +27,7 @@ ^test/dw-border-test$ ^test/dw-example$ ^test/dw-find-test$ +^test/dw-float-test$ ^test/dw-image-background$ ^test/dw-images-scaled$ ^test/dw-images-scaled2$ @@ -1,4 +1,4 @@ -============================================================================= +b============================================================================= Dillo project ============================================================================= diff --git a/configure.ac b/configure.ac index 0c4dd953..5d0f919e 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,14 @@ AC_TYPE_UINT16_T AC_TYPE_INT32_T AC_TYPE_UINT32_T +dnl ----------------------------------------------------------------- +dnl Check for absolute path of working directory. +dnl This is needed for RTFL, to get full the full paths of the source +dnl file names +dnl ----------------------------------------------------------------- +dnl +BASE_CUR_WORKING_DIR=`pwd` + dnl -------------------------------------- dnl Check whether to add /usr/local or not dnl (this is somewhat a religious problem) @@ -482,6 +490,7 @@ if eval "test x$GCC = xyes"; then CXXFLAGS="$CXXFLAGS -Wall -W -Wno-unused-parameter -fno-rtti -fno-exceptions" fi +AC_SUBST(BASE_CUR_WORKING_DIR) AC_SUBST(LIBJPEG_LIBS) AC_SUBST(LIBJPEG_LDFLAGS) AC_SUBST(LIBJPEG_CPPFLAGS) diff --git a/doc/Makefile.am b/doc/Makefile.am index d48e3e73..8ade3d15 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -11,6 +11,8 @@ EXTRA_DIST = \ dw-widget-sizes.doc \ dw-changes.doc \ dw-images-and-backgrounds.doc \ + dw-dw-out-of-flow.doc \ + dw-dw-out-of-flow-2.doc \ fltk-problems.doc \ rounding-errors.doc \ uml-legend.doc \ @@ -27,6 +29,7 @@ EXTRA_DIST = \ dw-textblock-collapsing-spaces-1-2.png \ dw-textblock-collapsing-spaces-2-1.png \ dw-textblock-collapsing-spaces-2-2.png \ + dw-floats-01.png \ not-so-simple-container.png \ Cache.txt \ Cookies.txt \ diff --git a/doc/dw-floats-01.png b/doc/dw-floats-01.png Binary files differnew file mode 100644 index 00000000..16eb63a5 --- /dev/null +++ b/doc/dw-floats-01.png diff --git a/doc/dw-out-of-flow-2.doc b/doc/dw-out-of-flow-2.doc new file mode 100644 index 00000000..de15c672 --- /dev/null +++ b/doc/dw-out-of-flow-2.doc @@ -0,0 +1,70 @@ +/** \page dw-out-of-flow-2 Handling Elements Out Of Flow (cont.) + +(Some notes, to be integrated into \ref dw-out-of-flow; focus mainly +on performance aspects.) + +Constructing a page with floats +------------------------------- +When a page is constructed (dw::Textblock::addWord), the *generating* +block tells the positions of floats (or, generally, widgets out of +flow) via dw::OutOfFlowMgr::tellPosition. This method considers +already collisions with other floats (only previous floats; floats +following this float are not considered); after the call, +dw::OutOfFlowMgr::getBorder will return correct values. + +dw::OutOfFlowMgr::tellPosition also checks for overlaps of this float +with other textblocks, except this textblock (the *generator*, which +is just constructed, so nothing has to be done). The fact that the +position of the float is the top, and so the float has only an +allocation below this position, leads to the effect that only the +textblocks following the generator are affected. (**Check:** Can the +search be limited here?) When a page is constructed, no textblocks +should be following the generating block, so no textblocks are +affected. + +**Todo:** Clarify details of line breaking (\ref dw-line-breaking). + +Float changes its size +---------------------- +The float itself will call queueResize, which will result in a call of +markSizeChange for the *containing* block, which will then call +dw::OutOfFlowMgr::markSizeChange. Here, the vloat is only *marked* as +dirty; the size will be calculated later (in +dw::OutOfFlowMgr::ensureFloatSize). + +This will trigger the resize idle function, so sizeRequest and +sizeAllocate for all floats and textblocks. In this run, +dw::OutOfFlowMgr::hasRelationChanged will return *true*, and so result +in a call of dw::Textblock::borderChanged, and trigger a second run of +the resize idle function, dealing correctly with the new size. + +(This case is handles in a not perfectly optimal way, since two runs +of the resize idle function are neccessary; but size changes of floats +is not a very common case. + +When a page is constructed (see above), a changing size of a float +currently constructed typically only affects the most bottom +textblock; the other textblocks are not covered by this float.) + +**Error:** In this case, new collisions are not yet considered. + + +Changing the width of the page +------------------------------ + +When the page width is changed, this will result in a reconstruction +of the page; see *Constructing a page with floats*. Anyway, checking +for overlaps will play a more important role. This is handled in an +optimal way by dw::OutOfFlowMgr::hasRelationChanged. + +**Check:** Are "cascades" avoided, like this: + +1. All textblocks are constructed. A float in textblock 1 overlaps + with textblock 2, so dw::Textblock::borderChanged is called for + textblock 2. +2. In another resize idle run, textblock 2 is constructed again. A + float in textblock 2 overlaps with textblock 3, so that + dw::Textblock::borderChanged is called for textblock 3. +3. Etc. + +*/
\ No newline at end of file diff --git a/doc/dw-out-of-flow.doc b/doc/dw-out-of-flow.doc new file mode 100644 index 00000000..16523952 --- /dev/null +++ b/doc/dw-out-of-flow.doc @@ -0,0 +1,185 @@ +/** \page dw-out-of-flow Handling Elements Out Of Flow + +<div style="border: 2px solid #ff4040; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b> +Incomplete; some parts have been removed, beause they are not up to +date.</div> + +<div style="border: 2px solid #ffff00; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #ffffe0"><b>Info:</b> +Should be incorporated into dw::Textblock.</div> + +This texts deals with both floats and absolute positions, which have +in common that there is a distinction between generating block and +containing block (we are here using the same notation as in the +CSS 2 specification). Consider this snippet (regarding floats): + + + <ul> + <li>Some text.</li> + <li> + <div style="float:right; width=50%">Some longer text, so + that the effect described in this passage can be + demonstrated. + </div> + Some more and longer text.</li> + <li>Final text. Plus some more to demonstrate how text flows + around the float on the right side.</li> + </ul> + +which may be rendered like this + +\image html dw-floats-01.png + +The float (the DIV section, yellow in the image) is defined +("generated") within the list item (blue), so, in CSS 2 terms, the +list item is the generating block of the float. However, as the image +shows, the float is not contained by the list item, but another block, +several levels above (not shown here). In terms of ::dw, this means +that the dw::Textblock representing the float cannot be a child of the +dw::Textblock representing the generating block, the list item, since +the allocation of a child widget must be within the allocation of the +parent widget. Instead, to each dw::Textblock, another dw::Textblock +is assigned as the containing box. + +(Notice also that other text blocks must regard floats to calculate +their borders, and so their size. In this example, the following list +item (green) must consider the position of the float. This is +discussed in detail in the next section.) + + +Implication for size calculations and allocation +================================================ + +*See also:* \ref dw-widget-sizes, especially the section *Rules for +Methods Related to Resizing*. + +The size/position model of ::dw consists mainly of the following two +steps: + +1. First, the size of the toplevel widget is calculated. Size + calculation typically depends on the sizes of the widgets, which + are calculated recursively, but not more. +2. After this, the toplevel widget is allocated at position (0, 0), + with the previosly calculated size. Each widget must allocate its + children; here, the condition for the toplevel widget (allocated + size equals requested size) is not necessary; instead, each widget + may be allocated at every size. + +Especially for floats, this model becomes a bit difficult, for reasons +described below. For the solutions, much is centralized at the level +of the containing block, which delegates most to an instance of +dw::OutOfFlowMgr (details below). + +**The size of a widget depends on the size not only of the children.** +In the example above, the last list item (green, following the +generating list item) must know the size of the the float (which is +not a child or, generally, descendant) to determine the borders, which +is done in dw::Textblock::sizeRequestImpl. + +For this, the size model has been extended (see \ref dw-widget-sizes, +section *Rules for Methods Related to Resizing*): *sizeRequest* can be +called within *sizeRequestImpl* for other widgets that children (with +some caution). Namely, dw::Textblock::sizeRequestImpl calls +dw::core::Widget::sizeRequest for the float, via +dw::OutOfFlowMgr::getBorder and dw::OutOfFlowMgr::ensureFloatSize. + +**The size of a widget depends on the allocation of another widget.** +In the example above, both list items (blue and green) must know the +position of the float widget, within dw::Textblock::sizeRequestImpl, +to calculate the borders. The position, however, is stored in the +allocation, which is typically calculated later. + +Here, two cases must be distinguished. The position of a float is +always relative to its generating block, so for calculating the +borders for the generating block, the allocation needs not to be +know. For other textblocks, it needs to be known, so the calculation +of the borders will ignore floats generated by other textblocks, until +all widgets are allocated. The latter will call (when neccessary) +dw::core::Widget::queueResize, so that all border calculations are +repeated. + +For details see: + +- dw::OutOfFlowMgr::getLeftBorder, dw::OutOfFlowMgr::getRightBorder, + dw::OutOfFlowMgr::getBorder (called by the first two), and + especially, dw::OutOfFlowMgr::getFloatsListForTextblock (called by + the latter), where these three cases are distinguished; +- dw::OutOfFlowMgr::sizeAllocateStart, + dw::OutOfFlowMgr::sizeAllocateEnd which are called by the containing + block. + +Implementation overview +======================= + +<div style="border: 2px solid #ff4040; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #fff0f0"><b>Warning:</b> +This complete section must be reviewed.</div> + +Widget level +------------ +The terms _generating block_ and _containing block_ have been raised +to a higher level, the one of dw::core::Widget, and are here called +_generating widget_ and _containing widget_. To represent the +distinction, the type of dw::core::Content has been split into three +parts: + +- If a widget is out of flow, the generating widget keeps a reference + with the type dw::core::Content::WIDGET_OOF_REF, while the + containing block refers to it as dw::core::Content::WIDGET_OOF_CONT. + +- For widgets within flow, dw::core::Content::WIDGET_IN_FLOW is used. + +Notice that in the first case, there are two pieces of content +referring to the same widget. + +An application of this distinction is iterators. TODO: more. And still +missing: DeepIterator may need the generating parent widget in some +cases. + + +Textblock level +--------------- +Both dw::Textblock::notifySetAsTopLevel and +dw::Textblock::notifySetParent set the member +dw::Textblock::containingBlock appropriately, (according to rules +which should be defined in this document). + +Handling widgets out of flow is partly the task of the new class +dw::OutOfFlowMgr, which is stored by dw::Textblock::outOfFlowMgr, but +only for containing blocks. Generating blocks should refer to +_containingBlock->outOfFlowMgr_. (Perhaps dw::OutOfFlowMgr may become +independent of dw::Textblock.) + +dw::Textblock::addWidget is extended, so that floats and absolutely +positioned elements can be added. Notice that not this widget, but the +containing block becomes the parent of the newly added child, if it is +out of flow. dw::Textblock::addWidget decides this by calling +dw::OutOfFlowMgr::isOutOfFlow. (See new content types above.) + +dw::core::Widget::parentRef has become a new representation. Before, +it represented the line numer. Now (least signifant bit left): + + +---+---+- - - - - - - - - - -+---+ + | line number | 0 | + +---+---+- - - - - - - - - - -+---+ + + +---+---+- - - - - - - - -+---+---+ + | number of left float | 0 | 1 | + +---+---+- - - - - - - - -+---+---+ + + +---+---+- - - - - - - - -+---+---+ + | number of right float | 1 | 1 | + +---+---+- - - - - - - - -+---+---+ + +The latter two must be changed, as soom as absolute positions are +introduced. Details are hidden by static inline methods of +dw::OutOfFlowMgr. + + +See also +======== + +\ref dw-out-of-flow-2 + +*/
\ No newline at end of file diff --git a/doc/dw-widget-sizes.doc b/doc/dw-widget-sizes.doc index 419a4a73..dcc74bfc 100644 --- a/doc/dw-widget-sizes.doc +++ b/doc/dw-widget-sizes.doc @@ -1,22 +1,20 @@ /** \page dw-widget-sizes Sizes of Dillo Widgets -<h2>Allocation</h2> +Allocation +========== Each widget has an \em allocation at a given time, this includes -<ul> -<li> the position (\em x, \em y) relative to the upper left corner of the - canvas, and -<li> the size (\em width, \em ascent, \em descent). -</ul> +- the position (\em x, \em y) relative to the upper left corner of the + canvas, and +- the size (\em width, \em ascent, \em descent). The \em canvas is the whole area available for the widgets, in most -cases, only a part is seen in a viewport. The allocation of the toplevel widget is exactly the allocation of the canvas, i.e. +cases, only a part is seen in a viewport. The allocation of the +toplevel widget is exactly the allocation of the canvas, i.e. -<ul> -<li> the position of the toplevel widget is always (0, 0), and -<li> the canvas size is defined by the size of the toplevel widget. -</ul> +- the position of the toplevel widget is always (0, 0), and +- the canvas size is defined by the size of the toplevel widget. The size of a widget is not simply defined by the width and the height, instead, widgets may have a base line, and so are vertically @@ -31,13 +29,11 @@ defined by the limits of the C++ type \em int. In the example in the image, the widget has the following allocation: -<ul> -<li>\em x = 50 -<li>\em y = 50 -<li>\em width = 150 -<li>\em ascent = 150 -<li>\em descent = 100 -</ul> +- \em x = 50 +- \em y = 50 +- \em width = 150 +- \em ascent = 150 +- \em descent = 100 The current allocation of a widget is hold in dw::core::Widget::allocation. It can be set from outside by @@ -54,7 +50,9 @@ appropriate child allocations. dw::core::Widget::allocation should not be changed here, this is already done in dw::core::Widget::sizeAllocate. -<h2>Requisitions</h2> + +Requisitions +============ A widget may prefer a given size for the allocation. This size, the \em requisition, should be returned by the method @@ -62,14 +60,12 @@ dw::core::Widget::sizeRequestImpl. In the simplest case, this is independent of the context, e.g. for an image. dw::Image::sizeRequestImpl returns the following size: -<ul> -<li> If no buffer has yet been assigned (see dw::Image for more details), - the size necessary for the alternative text is returned. If no - alternative text has been set, zero is returned. +- If no buffer has yet been assigned (see dw::Image for more details), + the size necessary for the alternative text is returned. If no + alternative text has been set, zero is returned. -<li> If a buffer has been assigned (by dw::Image::setBuffer), the root - size is returned (i.e. the original size of the image to display). -</ul> +- If a buffer has been assigned (by dw::Image::setBuffer), the root + size is returned (i.e. the original size of the image to display). This is a bit simplified, dw::Image::sizeRequestImpl should also deal with margins, borders and paddings, see dw::core::style. @@ -87,7 +83,9 @@ widget), may, but also may not consider the requisition. Instead, a widget must deal with any allocation. (For example, dw::Image scales the image buffer when allocated at another size.) -<h2>Size Hints</h2> + +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>, @@ -95,11 +93,9 @@ 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 -<ul> -<li> the viewport size (ascent is the heigt here, while descent is 0) for - the toplevel widget, and -<li> determined by the parent for its child widgets. -</ul> +- 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. @@ -109,42 +105,41 @@ dw::core::Widget::queueResize, when apropriate. \todo There should be a definition of "available space". -<h2>Width Extremes</h2> + +Width Extremes +============== dw::Table uses width extremes for fast calculation of column widths. The structure dw::core::Extremes represents the minimal and maximal width of a widget, as defined by: -<ul> -<li> the minimal width is the smallest width, at which a widget can still - display contents, and -<li> the maximal width is the largest width, above which increasing the width - does not make any sense. -</ul> +- the minimal width is the smallest width, at which a widget can still + display contents, and +- the maximal width is the largest width, above which increasing the + width- does not make any sense. Especially the latter is vaguely defined, here are some examples: -<ul> -<li> For those widgets, which do not depend on size hints, the minimal and - the maximal width is the inherent width (the one returned by - dw::core::Widget::sizeRequest). +- For those widgets, which do not depend on size hints, the minimal + and the maximal width is the inherent width (the one returned by + dw::core::Widget::sizeRequest). -<li> For a textblock, the minimal width is the width of the widest - (unbreakable) word, the maximal width is the width of the total - paragraph (stretching a paragraph further would only waste space). - Actually, the implementation of dw::Textblock::getExtremesImpl is - a bit more complex. +- For a textblock, the minimal width is the width of the widest + (unbreakable) word, the maximal width is the width of the total + paragraph (stretching a paragraph further would only waste space). + Actually, the implementation of dw::Textblock::getExtremesImpl is a + bit more complex. -<li> dw::Table is an example, where the width extremes are calculated - from the width extremes of the children. -</ul> +- dw::Table is an example, where the width extremes are calculated + from the width extremes of the children. Handling width extremes is similar to handling requisitions, a widget must implement dw::core::Widget::getExtremesImpl, but a caller will use dw::core::Widget::getExtremes. -<h2>Resizing</h2> +Resizing +======== When the widget changes its size (requisition), it should call dw::core::Widget::queueResize. The next call of @@ -160,27 +155,127 @@ done before. In this case, a widget must exactly know the reasons, why a call of dw::core::Widget::sizeRequestImpl is necessary. To make use of this, a widget must implement the following: -<ol> -<li> There is a member dw::core::Widget::parentRef, which is - totally under control of the parent widget (and so sometimes not - used at all). It is necessary to define how parentRef is used - by a specific parent widget, and it has to be set to the correct - value whenever necessary. - -<li> The widget must implement dw::core::Widget::markSizeChange and +1. There is a member dw::core::Widget::parentRef, which is totally + under control of the parent widget (and so sometimes not used at + all). It is necessary to define how parentRef is used by a specific + parent widget, and it has to be set to the correct value whenever + necessary. +2. The widget must implement dw::core::Widget::markSizeChange and dw::core::Widget::markExtremesChange, these methods are called in two cases: - - <ol> - <li> directly after dw::core::Widget::queueResize, with the argument - ref was passed to dw::core::Widget::queueResize, and - <li> if a child widget has called dw::core::Widget::queueResize, - with the value of the parent_ref member of this child. - </ol> -</ol> + 1. directly after dw::core::Widget::queueResize, with the + argument ref was passed to dw::core::Widget::queueResize, + and + 2. if a child widget has called dw::core::Widget::queueResize, + with the value of the parent_ref member of this child. This way, a widget can exactly keep track on size changes, and so implement resizing in a faster way. A good example on how to use this is dw::Textblock. + +Rules for Methods Related to Resizing +===================================== + +Which method can be called, when the call of another method is not +finished? These rules are important in two circumstances: + +1. To know which method can be called, and, especially, which methods + *must not* be called, within the implementation of + *sizeRequestImpl* (called by *sizeRequest*), *markSizeChange*, and + *markExtremesChange* (the latter two are called by *queueResize*). +2. On the other hand, to make sure that the calls, which are allowed, + are handled correctly, especially in implementations of + *sizeRequestImpl*, *markSizeChange*, *markExtremesChange* + +Generally, the rules defined below are, in case of doubt, rather +strict; when changing the rules, loosening is simpler than to tighten +them, since this will make it neccessary to review old code for calls +previously allowed but now forbidden. + +Short recap: + +- *QueueResize* directly calls *markSizeChange* and + *markExtremesChanges*, and queues an idle function for the actual + resizing (dw::core::Layout::resizeIdle). (The idle function is + called some time after *queueResize* is finished.) +- The resize idle function first calls *sizeRequest*, then + *sizeAllocate*, for the toplevel widget. + +In the following table, the rules are defined in detail. "Within call +of ..." includes all methods called from the original method: the +first row (*queueResize*) defines also the rules for +*markExtremesChanges* and *markExtremesChanges*, and in the second row +(*sizeAllocate*), even *sizeRequest* has to be considered. + +<div style="border: 2px solid #ff4040; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b> +Not up to date: *queueResize* can now be called recursively (so to +speak). See code there.</div> + +<table> + <tr> + <th>Within call of ... ↓ + <th>... is call allowed of ... ? → + <th>queueResize + <th>sizeAllocate + <th>sizeRequest + <th>getExtremes + <tr> + <th colspan=2>queueResize + <td>No + <td>No<sup>1</sup> + <td>No<sup>1</sup> + <td>No<sup>1</sup> + <tr> + <th colspan=2>sizeAllocate + <td>Yes + <td>Only for children<sup>2</sup> + <td>Yes(?) + <td>Yes(?) + <tr> + <th colspan=2>sizeRequest + <td>Yes<sup>3</sup> + <td>No + <td>Limited<sup>4</sup> + <td>Limited<sup>4</sup> + <tr> + <th colspan=2>getExtremes + <td>Yes<sup>3</sup> + <td>No + <td>Limited<sup>4</sup> + <td>Limited<sup>4</sup> + <tr> + <td colspan=6><sup>1</sup>) Otherwise, since these other methods +may be call *queueResize*, the limitation that *queueResize* must not +call *queueResize* can be violated. + +<sup>2</sup>) Could perhaps be loosened as for *sizeRequest* and +*getExtremes*, but there is probably no need. + +<sup>3</sup>) Therefore the distinction between *RESIZE_QUEUED* and +*NEEDS_RESIZE*, and *EXTREMES_QUEUED* and *EXTREMES_CHANGED*, +respectively. + +<sup>4</sup>) Calls only for children are safe. In other cases, you +take a large responsibility to prevent endless recursions by +(typically indirectly) calling *sizeRequest* / *getExtremes* for +direct ancestors. +</table> + +Furthermore, *sizeAllocate* can only be called within a call of +dw::core::Layout::resizeIdleId, so (if you do not touch dw::core) do +not call it outside of *sizeAllocateImpl*. The other methods can be +called outsize; e. g. *sizeRequest* is called in +dw::Textblock::addWidget. + +To avoid painful debugging, there are some tests for the cases that +one method call is strictly forbidden while another method is called. + +This could be done furthermore: + +- The tests could be refined. +- Is it possible to define exacter rules, along with a proof that no + problems (like endless recursion) can occur? + */ diff --git a/dw/Makefile.am b/dw/Makefile.am index d0d56d2a..5306c5a5 100644 --- a/dw/Makefile.am +++ b/dw/Makefile.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir) \ - -DDILLO_LIBDIR='"$(pkglibdir)/"' - + -DDILLO_LIBDIR='"$(pkglibdir)/"' \ + -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/dw"' noinst_LIBRARIES = \ libDw-core.a \ @@ -67,6 +67,8 @@ libDw_widgets_a_SOURCES = \ image.hh \ listitem.cc \ listitem.hh \ + outofflowmgr.cc \ + outofflowmgr.hh \ ruler.cc \ ruler.hh \ table.cc \ diff --git a/dw/findtext.cc b/dw/findtext.cc index cc57e991..e9384917 100644 --- a/dw/findtext.cc +++ b/dw/findtext.cc @@ -96,7 +96,7 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens, if (iterator) delete iterator; - iterator = new CharIterator (widget); + iterator = new CharIterator (widget, true); if (backwards) { /* Go to end */ @@ -128,7 +128,7 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens, } else { // Nothing found anymore, reset the state for the next trial. delete iterator; - iterator = new CharIterator (widget); + iterator = new CharIterator (widget, true); if (backwards) { /* Go to end */ while (iterator->next ()) ; diff --git a/dw/iterator.cc b/dw/iterator.cc index 18d7cd5a..18d62a49 100644 --- a/dw/iterator.cc +++ b/dw/iterator.cc @@ -55,6 +55,24 @@ bool Iterator::equals (Object *other) (getWidget() == otherIt->getWidget() && compareTo(otherIt) == 0); } +void Iterator::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append ("{ widget = "); + //widget->intoStringBuffer (sb); + sb->appendPointer (widget); + sb->append (" ("); + sb->append (widget->getClassName()); + sb->append (")>"); + + sb->append (", mask = "); + Content::maskIntoStringBuffer (mask, sb); + + sb->append (", content = "); + Content::intoStringBuffer (&content, sb); + + sb->append (" }"); +} + /** * \brief Delete the iterator. * @@ -186,6 +204,14 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end, } } + +void Iterator::print () +{ + misc::StringBuffer sb; + intoStringBuffer (&sb); + printf ("%s", sb.getChars ()); +} + // ------------------- // EmptyIterator // ------------------- @@ -343,7 +369,7 @@ Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask, //DEBUG_MSG (1, "%*smoving down (%swards) from %s\n", // indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it)); - assert (it->getContent()->type == Content::WIDGET); + assert (it->getContent()->type & Content::ANY_WIDGET); it2 = it->getContent()->widget->iterator (mask, fromEnd); if (it2 == NULL) { @@ -356,7 +382,7 @@ Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask, //DEBUG_MSG (1, "%*sexamining %s\n", // indent, "", a_Dw_iterator_text (it2)); - if (it2->getContent()->type == Content::WIDGET) { + if (it2->getContent()->type & Content::ANY_WIDGET) { // Another widget. Search in it downwards. it3 = searchDownward (it2, mask, fromEnd); if (it3 != NULL) { @@ -390,11 +416,11 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask, //DEBUG_MSG (1, "%*smoving %swards from %s\n", // indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it)); - assert (it->getContent()->type == Content::WIDGET); + assert (it->getContent()->type & Content::ANY_WIDGET); it2 = it->cloneIterator (); while (fromEnd ? it2->prev () : it2->next ()) { - if (it2->getContent()->type == Content::WIDGET) { + if (it2->getContent()->type & Content::ANY_WIDGET) { // Search downwards in this widget. it3 = searchDownward (it2, mask, fromEnd); if (it3 != NULL) { @@ -416,13 +442,14 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask, /* Nothing found, go upwards in the tree (if possible). */ it2->unref (); - if (it->getWidget()->getParent ()) { - it2 = it->getWidget()->getParent()->iterator (mask, false); + Widget *respParent = getRespectiveParent (it->getWidget(), it->getMask()); + if (respParent) { + it2 = respParent->iterator (mask, false); while (true) { if (!it2->next ()) misc::assertNotReached (); - if (it2->getContent()->type == Content::WIDGET && + if (it2->getContent()->type & Content::ANY_WIDGET && it2->getContent()->widget == it->getWidget ()) { it3 = searchSideward (it2, mask, fromEnd); it2->unref (); @@ -440,6 +467,27 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask, return NULL; } +Widget *DeepIterator::getRespectiveParent (Widget *widget, Content::Type mask) +{ + // Return, depending on which is requested indirectly (follow + // references or containments) the parent (container) or the + // generator. At this point, the type of the parent/generator is + // not known (since the parent/generator is not known), so we have + // to examine the mask. This is the reason why only one of + // WIDGET_OOF_CONT and WIDGET_OOF_REF is allowed. + + return (mask & Content::WIDGET_OOF_REF) ? + widget->getGenerator() : widget->getParent(); +} + +int DeepIterator::getRespectiveLevel (Widget *widget, Content::Type mask) +{ + // Similar to getRespectiveParent. + + return (mask & Content::WIDGET_OOF_REF) ? + widget->getGeneratorLevel() : widget->getLevel(); +} + /** * \brief Create a new deep iterator from an existing dw::core::Iterator. * @@ -456,6 +504,19 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask, */ DeepIterator::DeepIterator (Iterator *it) { + //printf ("Starting creating DeepIterator %p ...\n", this); + //printf ("Initial iterator: "); + //it->print (); + //printf ("\n"); + + // Widgets out of flow are either followed widtin containers, or + // generators. Both (and also nothing at all) is not allowed. See + // also comment in getRespectiveParent. + int oofMask = + it->getMask() & (Content::WIDGET_OOF_CONT | Content::WIDGET_OOF_REF); + assert (oofMask == Content::WIDGET_OOF_CONT || + oofMask == Content::WIDGET_OOF_REF); + //DEBUG_MSG (1, "a_Dw_ext_iterator_new: %s\n", a_Dw_iterator_text (it)); // Clone input iterator, so the iterator passed as parameter @@ -467,7 +528,7 @@ DeepIterator::DeepIterator (Iterator *it) // If it points to a widget, find a near non-widget content, // since an DeepIterator should never return widgets. - if (it->getContent()->type == Content::WIDGET) { + if (it->getContent()->type & Content::ANY_WIDGET) { Iterator *it2; // The second argument of searchDownward is actually a matter of @@ -494,31 +555,50 @@ DeepIterator::DeepIterator (Iterator *it) // \todo There may be a faster way instead of iterating through the // parent widgets. + //printf ("Starting with: "); + //it->print (); + //printf ("\n"); + // Construct the iterators. - int thisLevel = it->getWidget()->getLevel (), level; + int thisLevel = getRespectiveLevel (it->getWidget()), level; Widget *w; - for (w = it->getWidget (), level = thisLevel; w->getParent() != NULL; - w = w->getParent (), level--) { - Iterator *it = w->getParent()->iterator (mask, false); + for (w = it->getWidget (), level = thisLevel; + getRespectiveParent (w) != NULL; + w = getRespectiveParent (w), level--) { + Iterator *it = getRespectiveParent(w)->iterator (mask, false); + + //printf (" parent: %s %p\n", w->getClassName (), w); + stack.put (it, level - 1); while (true) { + //printf (" "); + //it->print (); + //printf ("\n"); + bool hasNext = it->next(); assert (hasNext); - if (it->getContent()->type == Content::WIDGET && + if (it->getContent()->type & Content::ANY_WIDGET && it->getContent()->widget == w) break; } + + //printf (" %d: ", level - 1); + //it->print (); + //printf ("\n"); } stack.put (it, thisLevel); content = *(it->getContent()); } + + //printf ("... done creating DeepIterator %p.\n", this); } DeepIterator::~DeepIterator () { + //printf ("Deleting DeepIterator %p ...\n", this); } object::Object *DeepIterator::clone () @@ -539,9 +619,29 @@ int DeepIterator::compareTo (object::Comparable *other) { DeepIterator *otherDeepIterator = (DeepIterator*)other; + //printf ("Compare: %s\n", stack.toString ()); + //printf (" to: %s\n", otherDeepIterator->stack.toString ()); + // Search the highest level, where the widgets are the same. int level = 0; + // The Comparable interface does not define "uncomparable". Deep + // iterators are only comparable if they belong to the same widget + // tree, so have the same widget at the bottom at the + // stack. If this is not the case, we abort. + + assert (stack.size() > 0); + assert (otherDeepIterator->stack.size() > 0); + + //printf ("Equal? The %s %p (of %p) and the %s %p (of %p)?\n", + // stack.get(0)->getWidget()->getClassName(), + // stack.get(0)->getWidget(), this, + // otherDeepIterator->stack.get(0)->getWidget()->getClassName(), + // otherDeepIterator->stack.get(0)->getWidget(), otherDeepIterator); + + assert (stack.get(0)->getWidget() + == otherDeepIterator->stack.get(level)->getWidget()); + while (stack.get(level)->getWidget () == otherDeepIterator->stack.get(level)->getWidget ()) { if (level == stack.size() - 1 || @@ -550,10 +650,14 @@ int DeepIterator::compareTo (object::Comparable *other) level++; } + //printf (" => level = %d (temorally)\n", level); + while (stack.get(level)->getWidget () != otherDeepIterator->stack.get(level)->getWidget ()) level--; + //printf (" => level = %d (finally)\n", level); + return stack.get(level)->compareTo (otherDeepIterator->stack.get(level)); } @@ -577,7 +681,7 @@ bool DeepIterator::next () Iterator *it = stack.getTop (); if (it->next ()) { - if (it->getContent()->type == Content::WIDGET) { + if (it->getContent()->type & Content::ANY_WIDGET) { // Widget: new iterator on stack, to search in this widget. stack.push (it->getContent()->widget->iterator (mask, false)); return next (); @@ -610,7 +714,7 @@ bool DeepIterator::prev () Iterator *it = stack.getTop (); if (it->prev ()) { - if (it->getContent()->type == Content::WIDGET) { + if (it->getContent()->type & Content::ANY_WIDGET) { // Widget: new iterator on stack, to search in this widget. stack.push (it->getContent()->widget->iterator (mask, true)); return prev (); @@ -642,9 +746,17 @@ CharIterator::CharIterator () it = NULL; } -CharIterator::CharIterator (Widget *widget) +/** + * \brief ... + * + * If followReferences is true, only the reference are followed, when + * the container and generator for a widget is different. If false, + * only the container is followed. + */ +CharIterator::CharIterator (Widget *widget, bool followReferences) { - Iterator *i = widget->iterator (Content::SELECTION_CONTENT, false); + Iterator *i = + widget->iterator (Content::maskForSelection (followReferences), false); it = new DeepIterator (i); i->unref (); ch = START; diff --git a/dw/iterator.hh b/dw/iterator.hh index d086721c..abf31d0b 100644 --- a/dw/iterator.hh +++ b/dw/iterator.hh @@ -31,6 +31,7 @@ private: public: bool equals (Object *other); + void intoStringBuffer(lout::misc::StringBuffer *sb); inline Widget *getWidget () { return widget; } inline Content *getContent () { return &content; } @@ -85,6 +86,8 @@ public: static void scrollTo (Iterator *it1, Iterator *it2, int start, int end, HPosition hpos, VPosition vpos); + + virtual void print (); }; @@ -168,6 +171,16 @@ private: inline DeepIterator () { } + static Widget *getRespectiveParent (Widget *widget, Content::Type mask); + inline Widget *getRespectiveParent (Widget *widget) { + return getRespectiveParent (widget, mask); + } + + static int getRespectiveLevel (Widget *widget, Content::Type mask); + inline int getRespectiveLevel (Widget *widget) { + return getRespectiveLevel (widget, mask); + } + public: DeepIterator(Iterator *it); ~DeepIterator(); @@ -230,7 +243,7 @@ private: CharIterator (); public: - CharIterator (Widget *widget); + CharIterator (Widget *widget, bool followReferences); ~CharIterator (); lout::object::Object *clone(); diff --git a/dw/layout.cc b/dw/layout.cc index 6f2e8d8b..58b30fb3 100644 --- a/dw/layout.cc +++ b/dw/layout.cc @@ -245,6 +245,9 @@ Layout::Layout (Platform *platform) topLevel = NULL; widgetAtPoint = NULL; + queueQueueResizeList = new typed::Vector<QueueResizeItem> (4, true); + queueResizeList = new typed::Vector<Widget> (4, false); + DBG_OBJ_CREATE ("dw::core::Layout"); bgColor = NULL; @@ -277,7 +280,13 @@ Layout::Layout (Platform *platform) selectionState.setLayout(this); + queueResizeCounter = sizeAllocateCounter = sizeRequestCounter = + getExtremesCounter = 0; + layoutImgRenderer = NULL; + + resizeIdleCounter = queueResizeCounter = sizeAllocateCounter + = sizeRequestCounter = getExtremesCounter = 0; } Layout::~Layout () @@ -303,6 +312,9 @@ Layout::~Layout () topLevel = NULL; delete w; } + + delete queueQueueResizeList; + delete queueResizeList; delete platform; delete view; delete anchorsTable; @@ -320,12 +332,18 @@ void Layout::addWidget (Widget *widget) topLevel = widget; widget->layout = this; + queueResizeList->clear (); + widget->notifySetAsTopLevel(); findtextState.setWidget (widget); canvasHeightGreater = false; setSizeHints (); - queueResize (); + + // Do not directly call Layout::queueResize(), but + // Widget::queueResize(), so that all flags are set properly, + // queueResizeList is filled, etc. + topLevel->queueResize (-1, false); } void Layout::removeWidget () @@ -334,6 +352,7 @@ void Layout::removeWidget () * \bug Some more attributes must be reset here. */ topLevel = NULL; + queueResizeList->clear (); widgetAtPoint = NULL; canvasWidth = canvasAscent = canvasDescent = 0; scrollX = scrollY = 0; @@ -776,11 +795,41 @@ void Layout::setBgImage (style::StyleImage *bgImage, void Layout::resizeIdle () { + DBG_OBJ_MSG ("resize", 0, "<b>resizeIdle</b>"); + DBG_OBJ_MSG_START (); + + enterResizeIdle (); + //static int calls = 0; - //MSG(" Layout::resizeIdle calls = %d\n", ++calls); + //printf ("Layout::resizeIdle calls = %d\n", ++calls); assert (resizeIdleId != -1); + for (typed::Iterator <Widget> it = queueResizeList->iterator(); + it.hasNext (); ) { + Widget *widget = it.getNext (); + + //printf (" the %stop-level %s %p was queued (extremes changed: %s)\n", + // widget->parent ? "non-" : "", widget->getClassName(), widget, + // widget->extremesQueued () ? "yes" : "no"); + + if (widget->resizeQueued ()) { + widget->setFlags (Widget::NEEDS_RESIZE); + widget->unsetFlags (Widget::RESIZE_QUEUED); + } + + if (widget->allocateQueued ()) { + widget->setFlags (Widget::NEEDS_ALLOCATE); + widget->unsetFlags (Widget::ALLOCATE_QUEUED); + } + + if (widget->extremesQueued ()) { + widget->setFlags (Widget::EXTREMES_CHANGED); + widget->unsetFlags (Widget::EXTREMES_QUEUED); + } + } + queueResizeList->clear (); + // Reset already here, since in this function, queueResize() may be // called again. resizeIdleId = -1; @@ -790,7 +839,15 @@ void Layout::resizeIdle () Allocation allocation; topLevel->sizeRequest (&requisition); - + DBG_OBJ_MSGF ("resize", 1, "toplevel size: %d * (%d + %d)", + 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. + assert (topLevel->needsAllocate ()); + allocation.x = allocation.y = 0; allocation.width = requisition.width; allocation.ascent = requisition.ascent; @@ -825,10 +882,14 @@ void Layout::resizeIdle () } // views are redrawn via Widget::resizeDrawImpl () - } updateAnchor (); + + DBG_OBJ_MSGF ("resize", 1, "resizeIdleId = %d", resizeIdleId); + DBG_OBJ_MSG_END (); + + leaveResizeIdle (); } void Layout::setSizeHints () @@ -1164,8 +1225,13 @@ void Layout::viewportSizeChanged (View *view, int width, int height) /* if size changes, redraw this view. * TODO: this is a resize call (redraw/resize code needs a review). */ - if (viewportWidth != width || viewportHeight != height) - queueResize(); + if (viewportWidth != width || viewportHeight != height) { + if (topLevel) + // similar to addWidget() + topLevel->queueResize (-1, false); + else + queueResize (); + } viewportWidth = width; viewportHeight = height; diff --git a/dw/layout.hh b/dw/layout.hh index 47554b42..f76d755a 100644 --- a/dw/layout.hh +++ b/dw/layout.hh @@ -150,9 +150,26 @@ private: ~Anchor (); }; + class QueueResizeItem: public lout::object::Object + { + public: + Widget *widget; + int ref; + bool extremesChanged; + + inline QueueResizeItem (Widget *widget, int ref, bool extremesChanged) + { + this->widget = widget; + this->ref = ref; + this->extremesChanged = extremesChanged; + } + }; + Platform *platform; View *view; Widget *topLevel, *widgetAtPoint; + lout::container::typed::Vector<QueueResizeItem> *queueQueueResizeList; + lout::container::typed::Vector<Widget> *queueResizeList; /* The state, which must be projected into the view. */ style::Color *bgColor; @@ -236,6 +253,16 @@ private: void queueResize (); void removeWidget (); + /* For tests regarding the respective Layout and (mostly) Widget + methods. Accessed by respective methods (enter..., leave..., + ...Entered) defined here and in Widget. */ + + int resizeIdleCounter, queueResizeCounter, sizeAllocateCounter, + sizeRequestCounter, getExtremesCounter; + + void enterResizeIdle () { resizeIdleCounter++; } + void leaveResizeIdle () { resizeIdleCounter--; } + public: Layout (Platform *platform); ~Layout (); diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc new file mode 100644 index 00000000..ad266759 --- /dev/null +++ b/dw/outofflowmgr.cc @@ -0,0 +1,2017 @@ +/* + * Dillo Widget + * + * Copyright 2013 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 "outofflowmgr.hh" +#include "textblock.hh" +#include "../lout/debug.hh" + +using namespace lout::object; +using namespace lout::container::typed; +using namespace lout::misc; +using namespace dw::core; +using namespace dw::core::style; + +namespace dw { + +OutOfFlowMgr::WidgetInfo::WidgetInfo (OutOfFlowMgr *oofm, Widget *widget) +{ + this->oofm = oofm; + this->widget = widget; + wasAllocated = false; + xCB = yCB = width = height = -1; +} + +void OutOfFlowMgr::WidgetInfo::update (bool wasAllocated, int xCB, int yCB, + int width, int height) +{ + this->wasAllocated = wasAllocated; + this->xCB = xCB; + this->yCB = yCB; + this->width = width; + this->height = height; + + DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.xCB", xCB); + 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); +} + +void OutOfFlowMgr::WidgetInfo::updateAllocation () +{ + update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), + getNewHeight ()); +} + +// ---------------------------------------------------------------------- + +OutOfFlowMgr::Float::Float (OutOfFlowMgr *oofm, Widget *widget, + Textblock *generatingBlock, int externalIndex) : + WidgetInfo (oofm, widget) +{ + this->generatingBlock = generatingBlock; + this->externalIndex = externalIndex; + + yReq = yReal = size.width = size.ascent = size.descent = 0; + dirty = sizeChangedSinceLastAllocation = true; + inCBList = false; +} + +void OutOfFlowMgr::Float::intoStringBuffer(StringBuffer *sb) +{ + sb->append ("{ widget = "); + sb->appendPointer (getWidget ()); + + if (getWidget ()) { + sb->append (" ("); + sb->append (getWidget()->getClassName ()); + sb->append (")"); + } + + sb->append (", index = "); + sb->appendInt (index); + sb->append (", sideSpanningIndex = "); + sb->appendInt (sideSpanningIndex); + sb->append (", generatingBlock = "); + sb->appendPointer (generatingBlock); + sb->append (", yReq = "); + sb->appendInt (yReq); + sb->append (", yReal = "); + sb->appendInt (yReal); + sb->append (", size = { "); + sb->appendInt (size.width); + sb->append (" * "); + sb->appendInt (size.ascent); + sb->append (" + "); + sb->appendInt (size.descent); + sb->append (" }, dirty = "); + sb->appendBool (dirty); + sb->append (", sizeChangedSinceLastAllocation = "); + sb->appendBool (sizeChangedSinceLastAllocation); + sb->append (", inCBList = "); + sb->appendBool (inCBList); + sb->append (" }"); +} + +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 ()); + + bool b; + + if (textblock == generatingBlock) { + int reqyGB = y; + int flyGB = yReal; + getOutOfFlowMgr()->ensureFloatSize (this); + int flh = size.ascent + size.descent; + b = flyGB + flh > reqyGB && flyGB < reqyGB + h; + + DBG_OBJ_MSGF_O ("border", 1, getOutOfFlowMgr (), + "for generator: reqyGB = %d, flyGB = %d, " + "flh = %d + %d = %d => %s", + reqyGB, flyGB, size.ascent, size.descent, flh, + b ? "true" : "false"); + } else { + assert (getOutOfFlowMgr()->wasAllocated (generatingBlock)); + assert (getOutOfFlowMgr()->wasAllocated (textblock)); + + if (!getWidget()->wasAllocated ()) { + DBG_OBJ_MSG_O ("border", 1, getOutOfFlowMgr (), + "not generator (not allocated) => false"); + b = false; + } else { + Allocation *tba = getOutOfFlowMgr()->getAllocation(textblock), + //*gba = getOutOfFlowMgr()->getAllocation(generatingBlock), + *fla = getWidget()->getAllocation (); + int reqyCanv = tba->y + y; + int flyCanv = fla->y; + int flh = fla->ascent + fla->descent; + b = flyCanv + flh > reqyCanv && flyCanv < reqyCanv + h; + + DBG_OBJ_MSGF_O ("border", 1, getOutOfFlowMgr (), + "not generator (allocated): reqyCanv = %d + %d = %d, " + "flyCanv = %d, flh = %d + %d = %d => %s", + tba->y, y, reqyCanv, flyCanv, + fla->ascent, fla->descent, flh, b ? "true" : "false"); + } + } + + DBG_OBJ_MSG_END_O (getOutOfFlowMgr ()); + + return b; +} + +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->index, fl2->index, refTB); + DBG_OBJ_MSG_START_O (oofm); + + if (refTB == fl1->generatingBlock && refTB == fl2->generatingBlock) { + DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is generating both floats"); + r = fl1->yReal - fl2->yReal; + } else { + DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is not generating both floats"); + DBG_OBJ_MSG_START_O (oofm); + + assert (oofm->wasAllocated (fl1->generatingBlock)); + assert (oofm->wasAllocated (fl2->generatingBlock)); + + DBG_OBJ_MSGF_O ("border", 2, oofm, "generators are %p and %p", + fl1->generatingBlock, fl2->generatingBlock); + + // (i) Floats may not yet been allocated (although the + // generators are). Non-allocated floats do not have an effect + // yet, they are considered "at the end" of the list. + + // (ii) Float::widget for the key used for binary search. In + // this case, Float::yReal is used instead (which is set in + // SortedFloatsVector::find). + + bool a1 = fl1->getWidget () ? fl1->getWidget()->wasAllocated () : true; + bool a2 = fl2->getWidget () ? fl2->getWidget()->wasAllocated () : true; + + DBG_OBJ_MSGF_O ("border", 2, oofm, + "float 1 allocated: %s; float 2 allocated: %s", + a1 ? "yes" : "no", a2 ? "yes" : "no"); + + if (a1 && a2) { + int fly1 = fl1->getWidget() ? fl1->getWidget()->getAllocation()->y : + oofm->getAllocation(fl1->generatingBlock)->y + fl1->yReal; + int fly2 = fl2->getWidget() ? fl2->getWidget()->getAllocation()->y : + oofm->getAllocation(fl2->generatingBlock)->y + fl2->yReal; + DBG_OBJ_MSGF_O ("border", 2, oofm, "y diff = %d - %d", fly1, fly2); + r = fly1 - fly2; + } else if (a1 && !a2) + r = -1; + else if (!a1 && a2) + r = +1; + else // if (!a1 && !a2) + return 0; + + DBG_OBJ_MSG_END_O (oofm); + } + + DBG_OBJ_MSGF_O ("border", 1, oofm, "result: %d", r); + DBG_OBJ_MSG_END_O (oofm); + return r; +} + +int OutOfFlowMgr::Float::CompareSideSpanningIndex::compare (Object *o1, + Object *o2) +{ + return ((Float*)o1)->sideSpanningIndex - ((Float*)o2)->sideSpanningIndex; +} + +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->index, f1->generatingBlock, f1->externalIndex, + f2->index, f2->generatingBlock, f2->externalIndex); + DBG_OBJ_MSG_START_O (oofm); + + if (f1->generatingBlock == f2->generatingBlock) { + r = f1->externalIndex - f2->externalIndex; + DBG_OBJ_MSGF_O ("border", 2, oofm, + "(a) generating blocks equal => %d - %d = %d", + f1->externalIndex, f2->externalIndex, r); + } else { + TBInfo *t1 = oofm->getTextblock (f1->generatingBlock), + *t2 = oofm->getTextblock (f2->generatingBlock); + bool rdef = false; + + for (TBInfo *t = t1; t != NULL; t = t->parent) + if (t->parent == t2) { + rdef = true; + r = t->parentExtIndex - f2->externalIndex; + DBG_OBJ_MSGF_O ("border", 2, oofm, + "(b) %p is an achestor of %p; direct child is " + "%p (%d) => %d - %d = %d\n", + t2->getTextblock (), t1->getTextblock (), + t->getTextblock (), t->parentExtIndex, + t->parentExtIndex, f2->externalIndex, r); + } + + for (TBInfo *t = t2; !rdef && t != NULL; t = t->parent) + if (t->parent == t1) { + r = f1->externalIndex - t->parentExtIndex; + rdef = true; + DBG_OBJ_MSGF_O ("border", 2, oofm, + "(c) %p is an achestor of %p; direct child is %p " + "(%d) => %d - %d = %d\n", + t1->getTextblock (), t2->getTextblock (), + t->getTextblock (), t->parentExtIndex, + f1->externalIndex, t->parentExtIndex, r); + } + + if (!rdef) { + r = t1->index - t2->index; + DBG_OBJ_MSGF_O ("border", 2, oofm, "(d) other => %d - %d = %d", + t1->index, t2->index, r); + } + } + + DBG_OBJ_MSGF_O ("border", 2, oofm, "result: %d", r); + DBG_OBJ_MSG_END_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); + + Float key (oofm, NULL, lastGB, lastExtIndex); + key.index = -1; // for debugging + Float::CompareGBAndExtIndex comparator (oofm); + int i = bsearch (&key, false, &comparator); + + // At position i is the next larger element, so element i should + // not included, but i - 1 returned; except if the exact element is + // found: then include it and so return i. + int r; + if (i == size()) + r = i - 1; + else { + Float *f = get (i); + if (comparator.compare (f, &key) == 0) + r = i; + else + r = i - 1; + } + + //printf ("[%p] findFloatIndex (%p, %d) => i = %d, r = %d (size = %d); " + // "in %s list %p on the %s side\n", + // oofm->containingBlock, lastGB, lastExtIndex, i, r, size (), + // type == GB ? "GB" : "CB", this, side == LEFT ? "left" : "right"); + + //for (int i = 0; i < size (); i++) { + // Float *f = get(i); + // TBInfo *t = oofm->getTextblock(f->generatingBlock); + // printf (" %d: (%p [%d, %p], %d)\n", i, f->generatingBlock, + // t->index, t->parent ? t->parent->textblock : NULL, + // get(i)->externalIndex); + //} + + DBG_OBJ_MSGF_O ("border", 1, oofm, "=> r = %d", r); + DBG_OBJ_MSG_END_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); + + Float key (oofm, NULL, NULL, 0); + key.generatingBlock = textblock; + key.yReal = y; + key.index = -1; // for debugging + Float::ComparePosition comparator (oofm, textblock); + int result = bsearch (&key, false, start, end, &comparator); + + DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); + DBG_OBJ_MSG_END_O (oofm); + return result; +} + +int OutOfFlowMgr::SortedFloatsVector::findFirst (Textblock *textblock, + int y, int h, + 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_IF_RTFL { + DBG_OBJ_MSG_O ("border", 2, oofm, "searching in list:"); + DBG_OBJ_MSG_START_O (oofm); + + for (int i = 0; i < size(); i++) { + Float *f = get(i); + DBG_OBJ_MSGF_O ("border", 2, oofm, + "%d: (%p, i = %d/%d, y = %d/%d, s = (%d * (%d + %d)), " + "%s, %s, ext = %d, GB = %p); widget at (%d, %d)", + i, f->getWidget (), f->index, f->sideSpanningIndex, + f->yReq, f->yReal, f->size.width, f->size.ascent, + f->size.descent, f->dirty ? "dirty" : "clean", + f->sizeChangedSinceLastAllocation ? "scsla" : "sNcsla", + f->externalIndex, f->generatingBlock, + f->getWidget()->getAllocation()->x, + f->getWidget()->getAllocation()->y); + } + + DBG_OBJ_MSG_END_O (oofm); + } + + int last = findFloatIndex (lastGB, lastExtIndex); + DBG_OBJ_MSGF_O ("border", 1, oofm, "last = %d", last); + assert (last < size()); + int i = find (textblock, y, 0, last), result; + DBG_OBJ_MSGF_O ("border", 1, oofm, "i = %d", i); + + // Note: The smallest value of "i" is 0, which means that "y" is before or + // equal to the first float. The largest value is "last + 1", which means + // that "y" is after the last float. In both cases, the first or last, + // respectively, float is a candidate. Generally, both floats, before and + // at the search position, are candidates. + + if (i > 0 && get(i - 1)->covers (textblock, y, h)) + result = i - 1; + else if (i <= last && get(i)->covers (textblock, y, h)) + result = i; + else + result = -1; + + DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); + DBG_OBJ_MSG_END_O (oofm); + return result; +} + +int OutOfFlowMgr::SortedFloatsVector::findLastBeforeSideSpanningIndex + (int sideSpanningIndex) +{ + OutOfFlowMgr::Float::CompareSideSpanningIndex comparator; + Float key (NULL, NULL, NULL, 0); + key.sideSpanningIndex = sideSpanningIndex; + return bsearch (&key, false, &comparator) - 1; +} + +void OutOfFlowMgr::SortedFloatsVector::put (Float *vloat) +{ + lout::container::typed::Vector<Float>::put (vloat); + vloat->index = size() - 1; + vloat->inCBList = type == CB; +} + +OutOfFlowMgr::TBInfo::TBInfo (OutOfFlowMgr *oofm, Textblock *textblock, + TBInfo *parent, int parentExtIndex) : + WidgetInfo (oofm, textblock) +{ + this->parent = parent; + this->parentExtIndex = parentExtIndex; + + leftFloatsGB = new SortedFloatsVector (oofm, LEFT, SortedFloatsVector::GB); + rightFloatsGB = new SortedFloatsVector (oofm, RIGHT, SortedFloatsVector::GB); +} + +OutOfFlowMgr::TBInfo::~TBInfo () +{ + delete leftFloatsGB; + delete rightFloatsGB; +} + +OutOfFlowMgr::AbsolutelyPositioned::AbsolutelyPositioned (OutOfFlowMgr *oofm, + core::Widget *widget, + Textblock + *generatingBlock, + int externalIndex) +{ + this->widget = widget; + dirty = true; +} + +OutOfFlowMgr::OutOfFlowMgr (Textblock *containingBlock) +{ + DBG_OBJ_CREATE ("dw::OutOfFlowMgr"); + + this->containingBlock = containingBlock; + + leftFloatsCB = new SortedFloatsVector (this, LEFT, SortedFloatsVector::CB); + rightFloatsCB = new SortedFloatsVector (this, RIGHT, SortedFloatsVector::CB); + + leftFloatsAll = new Vector<Float> (1, true); + rightFloatsAll = new Vector<Float> (1, true); + + floatsByWidget = new HashTable <TypedPointer <Widget>, Float> (true, false); + + tbInfos = new Vector<TBInfo> (1, false); + tbInfosByTextblock = + new HashTable <TypedPointer <Textblock>, TBInfo> (true, true); + + leftFloatsMark = rightFloatsMark = 0; + lastLeftTBIndex = lastRightTBIndex = 0; + + absolutelyPositioned = new Vector<AbsolutelyPositioned> (1, true); + + containingBlockWasAllocated = containingBlock->wasAllocated (); + if (containingBlockWasAllocated) + containingBlockAllocation = *(containingBlock->getAllocation()); + + addWidgetInFlow (containingBlock, NULL, 0); +} + +OutOfFlowMgr::~OutOfFlowMgr () +{ + //printf ("OutOfFlowMgr::~OutOfFlowMgr\n"); + + delete leftFloatsCB; + delete rightFloatsCB; + + // Order is important: tbInfosByTextblock is owner of the instances + // of TBInfo.tbInfosByTextblock + delete tbInfos; + delete tbInfosByTextblock; + + delete floatsByWidget; + + // Order is important, since the instances of Float are owned by + // leftFloatsAll and rightFloatsAll, so these should be deleted + // last. + delete leftFloatsAll; + delete rightFloatsAll; + + delete absolutelyPositioned; +} + +void OutOfFlowMgr::sizeAllocateStart (Allocation *containingBlockAllocation) +{ + DBG_OBJ_MSG ("resize.oofm", 0, "<b>sizeAllocateStart</b>"); + this->containingBlockAllocation = *containingBlockAllocation; + containingBlockWasAllocated = true; +} + +void OutOfFlowMgr::sizeAllocateEnd () +{ + DBG_OBJ_MSG ("resize.oofm", 0, "<b>sizeAllocateEnd</b>"); + DBG_OBJ_MSG_START (); + + // Move floats from GB lists to the one CB list. + moveFromGBToCB (LEFT); + moveFromGBToCB (RIGHT); + + // Floats and absolutely positioned blocks have to be allocated + sizeAllocateFloats (LEFT); + sizeAllocateFloats (RIGHT); + sizeAllocateAbsolutelyPositioned (); + + // Textblocks have already been allocated here. + + // Check changes of both textblocks and floats allocation. (All is checked + // by hasRelationChanged (...).) + for (lout::container::typed::Iterator<TypedPointer <Textblock> > it = + tbInfosByTextblock->iterator (); + it.hasNext (); ) { + TypedPointer <Textblock> *key = it.getNext (); + TBInfo *tbInfo = tbInfosByTextblock->get (key); + Textblock *tb = key->getTypedValue(); + + int minFloatPos; + Widget *minFloat; + if (hasRelationChanged (tbInfo, &minFloatPos, &minFloat)) + tb->borderChanged (minFloatPos, minFloat); + } + + // Store some information for later use. + for (lout::container::typed::Iterator<TypedPointer <Textblock> > it = + tbInfosByTextblock->iterator (); + it.hasNext (); ) { + TypedPointer <Textblock> *key = it.getNext (); + TBInfo *tbInfo = tbInfosByTextblock->get (key); + Textblock *tb = key->getTypedValue(); + + tbInfo->updateAllocation (); + tbInfo->availWidth = tb->getAvailWidth (); + } + + for (int i = 0; i < leftFloatsCB->size(); i++) + leftFloatsCB->get(i)->updateAllocation (); + + for (int i = 0; i < rightFloatsCB->size(); i++) + rightFloatsCB->get(i)->updateAllocation (); + + DBG_OBJ_MSG_END (); +} + +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 (); + + int leftMinPos, rightMinPos; + Widget *leftMinFloat, *rightMinFloat; + bool c1 = + hasRelationChanged (tbInfo, LEFT, &leftMinPos, &leftMinFloat); + bool c2 = + hasRelationChanged (tbInfo, RIGHT, &rightMinPos, &rightMinFloat); + if (c1 || c2) { + if (!c1) { + *minFloatPos = rightMinPos; + *minFloat = rightMinFloat; + } else if (!c2) { + *minFloatPos = leftMinPos; + *minFloat = leftMinFloat; + } else { + if (leftMinPos < rightMinPos) { + *minFloatPos = leftMinPos; + *minFloat = leftMinFloat; + } else{ + *minFloatPos = rightMinPos; + *minFloat = rightMinFloat; + } + } + } + + if (c1 || c2) + DBG_OBJ_MSGF ("resize.oofm", 1, + "has changed: minFloatPos = %d, minFloat = %p", + *minFloatPos, *minFloat); + else + DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); + + DBG_OBJ_MSG_END (); + 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 (); + + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + bool changed = false; + + for (int i = 0; i < list->size(); i++) { + // TODO binary search? + Float *vloat = list->get(i); + int floatPos; + + if (tbInfo->getTextblock () == vloat->generatingBlock) + DBG_OBJ_MSGF ("resize.oofm", 1, + "not checking (generating!) textblock %p against float " + "%p", tbInfo->getWidget (), vloat->getWidget ()); + else { + Allocation *gba = getAllocation (vloat->generatingBlock); + + int newFlx = + calcFloatX (vloat, side, + vloat->generatingBlock->getAllocation()->x + - containingBlockAllocation.x, + gba->width, vloat->generatingBlock->getAvailWidth ()); + int newFly = vloat->generatingBlock->getAllocation()->y + - containingBlockAllocation.y + vloat->yReal; + + DBG_OBJ_MSGF ("resize.oofm", 1, + "checking textblock %p against float %p", + tbInfo->getWidget (), vloat->getWidget ()); + DBG_OBJ_MSG_START (); + + if (hasRelationChanged (tbInfo->wasThenAllocated (), + tbInfo->getOldXCB (), tbInfo->getOldYCB (), + tbInfo->getOldWidth (), + tbInfo->getOldHeight (), + tbInfo->getNewXCB (), tbInfo->getNewYCB (), + tbInfo->getNewWidth (), + tbInfo->getNewHeight (), + vloat->wasThenAllocated (), + // When not allocated before, these values + // are undefined, but this does not matter, + // since they are neither used. + vloat->getOldXCB (), vloat->getOldYCB (), + vloat->getOldWidth (), vloat->getOldHeight (), + newFlx, newFly, vloat->size.width, + vloat->size.ascent + vloat->size.descent, + side, &floatPos)) { + if (!changed || floatPos < *minFloatPos) { + *minFloatPos = floatPos; + *minFloat = vloat->getWidget (); + } + changed = true; + } else + DBG_OBJ_MSG ("resize.oofm", 0, "No."); + + DBG_OBJ_MSG_END (); + } + + // All floarts are searched, to find the minimum. TODO: Are + // floats sorted, so this can be shortened? (The first is the + // minimum?) + } + + if (changed) + DBG_OBJ_MSGF ("resize.oofm", 1, + "has changed: minFloatPos = %d, minFloat = %p", + *minFloatPos, *minFloat); + else + DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); + + DBG_OBJ_MSG_END (); + return changed; +} + +/** + * \brief ... + * + * All coordinates are given relative to the CB. *floatPos is relative + * to the TB, and may be negative. + */ +bool OutOfFlowMgr::hasRelationChanged (bool oldTBAlloc, + int oldTBx, int oldTBy, int oldTBw, + int oldTBh, int newTBx, int newTBy, + int newTBw, int newTBh, + bool oldFlAlloc, + int oldFlx, int oldFly, int oldFlw, + int oldFlh, int newFlx, int newFly, + 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 (); + + if (oldTBAlloc) + DBG_OBJ_MSGF ("resize.oofm", 1, "old TB: %d, %d; %d * %d", + oldTBx, oldTBy, oldTBw, oldTBh); + else + DBG_OBJ_MSG ("resize.oofm", 1, "old TB: undefined"); + DBG_OBJ_MSGF ("resize.oofm", 1, "new TB: %d, %d; %d * %d", + newTBx, newTBy, newTBw, newTBh); + + if (oldFlAlloc) + DBG_OBJ_MSGF ("resize.oofm", 1, "old Fl: %d, %d; %d * %d", + oldFlx, oldFly, oldFlw, oldFlh); + else + DBG_OBJ_MSG ("resize.oofm", 1, "old Fl: undefined"); + DBG_OBJ_MSGF ("resize.oofm", 1, "new Fl: %d, %d; %d * %d", + newFlx, newFly, newFlw, newFlh); + + bool result; + if (oldTBAlloc && oldFlAlloc) { + bool oldCov = oldFly + oldFlh > oldTBy && oldFly < oldTBy + oldTBh; + bool newCov = newFly + newFlh > newTBy && newFly < newTBy + newTBh; + + DBG_OBJ_MSGF ("resize.oofm", 1, "covered? then: %s, now: %s.", + oldCov ? "yes" : "no", newCov ? "yes" : "no"); + DBG_OBJ_MSG_START (); + + if (oldCov && newCov) { + int yOld = oldFly - oldTBy, yNew = newFly - newTBy; + if (yOld == yNew) { + DBG_OBJ_MSGF ("resize.oofm", 2, + "old (%d - %d) and new (%d - %d) position equal: %d", + oldFly, oldTBy, newFly, newTBy, yOld); + + // Float position has not changed, but perhaps the amout + // how far the float reaches into the TB. (TODO: + // Generally, not only here, it could be tested whether + // the float reaches into the TB at all.) + int wOld, wNew; + if (side == LEFT) { + wOld = oldFlx + oldFlw - oldTBx; + wNew = newFlx + newFlw - newTBx; + } else { + wOld = oldTBx + oldTBw - oldFlx; + wNew = newTBx + newTBw - newFlx; + } + + DBG_OBJ_MSGF ("resize.oofm", 2, "wOld = %d, wNew = %d\n", + wOld, wNew); + + if (wOld == wNew) { + if (oldFlh == newFlh) + result = false; + else { + // Only heights of floats changed. Relevant only + // from bottoms of float. + *floatPos = min (yOld + oldFlh, yNew + newFlh); + result = true; + } + } else { + *floatPos = yOld; + result = true; + } + } else { + DBG_OBJ_MSGF ("resize.oofm", 2, + "old (%d - %d = %d) and new (%d - %d = %d) position " + "different", + oldFly, oldTBy, yOld, newFly, newTBy, yNew); + *floatPos = min (yOld, yNew); + result = true; + } + } else if (oldCov) { + *floatPos = oldFly - oldTBy; + result = true; + DBG_OBJ_MSGF ("resize.oofm", 2, + "returning old position: %d - %d = %d", oldFly, oldTBy, + *floatPos); + } else if (newCov) { + *floatPos = newFly - newTBy; + result = true; + DBG_OBJ_MSGF ("resize.oofm", 2, + "returning new position: %d - %d = %d", newFly, newTBy, + *floatPos); + } else + result = false; + + DBG_OBJ_MSG_END (); + } else { + // Not allocated before: ignore all old values, only check whether + // TB is covered by Float. + if (newFly + newFlh > newTBy && newFly < newTBy + newTBh) { + *floatPos = newFly - newTBy; + result = true; + } else + result = false; + } + + if (result) + DBG_OBJ_MSGF ("resize.oofm", 1, "has changed: floatPos = %d", + *floatPos); + else + DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); + + DBG_OBJ_MSG_END (); + + return result; +} + +bool OutOfFlowMgr::isTextblockCoveredByFloat (Float *vloat, Textblock *tb, + int tbx, int tby, + int tbWidth, int tbHeight, + int *floatPos) +{ + assert (wasAllocated (vloat->generatingBlock)); + + int flh = vloat->dirty ? 0 : vloat->size.ascent + vloat->size.descent; + int y1 = getAllocation(vloat->generatingBlock)->y + vloat->yReal; + int y2 = y1 + flh; + + // TODO: Also regard horizontal dimension (same for tellFloatPosition)? + if (y2 > tby && y1 < tby + tbHeight) { + *floatPos = y1 - tby; + return true; + } else + return false; +} + +void OutOfFlowMgr::moveFromGBToCB (Side side) +{ + SortedFloatsVector *dest = side == LEFT ? leftFloatsCB : rightFloatsCB; + int *floatsMark = side == LEFT ? &leftFloatsMark : &rightFloatsMark; + + for (int mark = 0; mark <= *floatsMark; mark++) + for (lout::container::typed::Iterator<TBInfo> it = tbInfos->iterator (); + it.hasNext (); ) { + TBInfo *tbInfo = it.getNext (); + SortedFloatsVector *src = + side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; + for (int i = 0; i < src->size (); i++) { + Float *vloat = src->get(i); + if (!vloat->inCBList && 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); + } + } + } + + *floatsMark = 0; + + /* Old code: GB lists do not have to be cleared, but their contents + are still useful after allocation. Soon to be deleted, not only + uncommented. + + for (lout::container::typed::Iterator<TBInfo> it = tbInfos->iterator (); + it.hasNext (); ) { + TBInfo *tbInfo = it.getNext (); + SortedFloatsVector *src = + side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; + src->clear (); + } + */ + + //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()); +} + +void OutOfFlowMgr::sizeAllocateFloats (Side side) +{ + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + ensureFloatSize (vloat); + + Allocation *gbAllocation = getAllocation(vloat->generatingBlock); + Allocation *cbAllocation = getAllocation(containingBlock); + + Allocation childAllocation; + childAllocation.x = cbAllocation->x + + calcFloatX (vloat, side, gbAllocation->x - cbAllocation->x, + gbAllocation->width, + vloat->generatingBlock->getAvailWidth()); + childAllocation.y = gbAllocation->y + vloat->yReal; + childAllocation.width = vloat->size.width; + childAllocation.ascent = vloat->size.ascent; + childAllocation.descent = vloat->size.descent; + + vloat->getWidget()->sizeAllocate (&childAllocation); + } +} + + +/** + * \brief ... + * + * 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 gbActualWidth; + + switch (side) { + case LEFT: + // Left floats are always aligned on the left side of the + // generator (content, not allocation). + return gbX + vloat->generatingBlock->getStyle()->boxOffsetX(); + break; + + case RIGHT: + // In some cases, the actual (allocated) width is too large; we + // use the "available" width here. + gbActualWidth = min (gbWidth, gbAvailWidth); + + // 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). + return max (gbX + gbActualWidth - vloat->size.width + - vloat->generatingBlock->getStyle()->boxRestWidth(), + // Do not exceed CB allocation: + 0); + + default: + assertNotReached (); + return 0; + } +} + + +void OutOfFlowMgr::draw (View *view, Rectangle *area) +{ + drawFloats (leftFloatsCB, view, area); + drawFloats (rightFloatsCB, view, area); + drawAbsolutelyPositioned (view, area); +} + +void OutOfFlowMgr::drawFloats (SortedFloatsVector *list, View *view, + Rectangle *area) +{ + // This could be improved, since the list is sorted: search the + // first float fitting into the area, and iterate until one is + // found below the area. + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + core::Rectangle childArea; + if (vloat->getWidget()->intersects (area, &childArea)) + vloat->getWidget()->draw (view, &childArea); + } +} + +void OutOfFlowMgr::drawAbsolutelyPositioned (View *view, Rectangle *area) +{ + for (int i = 0; i < absolutelyPositioned->size(); i++) { + AbsolutelyPositioned *abspos = absolutelyPositioned->get(i); + core::Rectangle childArea; + if (abspos->widget->intersects (area, &childArea)) + abspos->widget->draw (view, &childArea); + } +} + +/** + * This method consideres also the attributes not yet considered by + * dillo, so that the containing block is determined correctly, which + * leads sometimes to a cleaner rendering. + */ +bool OutOfFlowMgr::isWidgetOutOfFlow (core::Widget *widget) +{ + return + widget->getStyle()->vloat != core::style::FLOAT_NONE || + widget->getStyle()->position == core::style::POSITION_ABSOLUTE || + widget->getStyle()->position == core::style::POSITION_FIXED; +} + +bool OutOfFlowMgr::isWidgetHandledByOOFM (core::Widget *widget) +{ + // May be extended for fixed (and relative?) positions. + return isWidgetFloat (widget); + // TODO temporary disabled: || isWidgetAbsolutelyPositioned (widget); +} + +void OutOfFlowMgr::addWidgetInFlow (Textblock *textblock, + Textblock *parentBlock, int externalIndex) +{ + //printf ("[%p] addWidgetInFlow (%p, %p, %d)\n", + // containingBlock, textblock, parentBlock, externalIndex); + + TBInfo *tbInfo = + new TBInfo (this, textblock, + parentBlock ? getTextblock (parentBlock) : NULL, + externalIndex); + tbInfo->index = tbInfos->size(); + + tbInfos->put (tbInfo); + tbInfosByTextblock->put (new TypedPointer<Textblock> (textblock), tbInfo); +} + +void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, + int externalIndex) +{ + DBG_OBJ_MSGF ("construct.oofm", 0, "<b>addWidgetOOF</b> (%p, %p, %d)", + widget, generatingBlock, externalIndex); + + if (isWidgetFloat (widget)) { + TBInfo *tbInfo = getTextblock (generatingBlock); + + Float *vloat = new Float (this, widget, generatingBlock, externalIndex); + + // Note: Putting the float first in the GB list, and then, + // possibly into the CB list (in that order) will trigger + // setting Float::inCBList to the right value. + + switch (widget->getStyle()->vloat) { + case FLOAT_LEFT: + leftFloatsAll->put (vloat); + 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); + } else { + if (tbInfo->index < lastLeftTBIndex) + leftFloatsMark++; + + vloat->mark = leftFloatsMark; + //printf ("[%p] adding left float %p (%s %p, mark %d) to GB list " + // "(index %d, last = %d)\n", + // containingBlock, vloat, widget->getClassName(), widget, + // vloat->mark, tbInfo->index, lastLeftTBIndex); + + lastLeftTBIndex = tbInfo->index; + } + break; + + case FLOAT_RIGHT: + rightFloatsAll->put (vloat); + 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); + } else { + if (tbInfo->index < lastRightTBIndex) + rightFloatsMark++; + + vloat->mark = rightFloatsMark; + //printf ("[%p] adding right float %p (%s %p, mark %d) to GB list " + // "(index %d, last = %d)\n", + // containingBlock, vloat, widget->getClassName(), widget, + // vloat->mark, tbInfo->index, lastRightTBIndex); + + lastRightTBIndex = tbInfo->index; + } + + break; + + default: + assertNotReached(); + } + + // "sideSpanningIndex" is only compared, so this simple + // assignment is sufficient; differenciation between GB and CB + // lists is not neccessary. TODO: Can this also be applied to + // "index", to simplify the current code? Check: where is + // "index" used. + vloat->sideSpanningIndex = + leftFloatsAll->size() + rightFloatsAll->size() - 1; + + floatsByWidget->put (new TypedPointer<Widget> (widget), vloat); + } else if (isWidgetAbsolutelyPositioned (widget)) { + AbsolutelyPositioned *abspos = + new AbsolutelyPositioned (this, widget, generatingBlock, + externalIndex); + absolutelyPositioned->put (abspos); + widget->parentRef = + createRefAbsolutelyPositioned (absolutelyPositioned->size() - 1); + } else + // May be extended. + assertNotReached(); +} + +void OutOfFlowMgr::moveExternalIndices (Textblock *generatingBlock, + int oldStartIndex, int diff) +{ + TBInfo *tbInfo = getTextblock (generatingBlock); + moveExternalIndices (tbInfo->leftFloatsGB, oldStartIndex, diff); + moveExternalIndices (tbInfo->rightFloatsGB, oldStartIndex, diff); +} + +void OutOfFlowMgr::moveExternalIndices (SortedFloatsVector *list, + int oldStartIndex, int diff) +{ + // Could be faster with binary search, but the GB (not CB!) lists + // should be rather small. + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + if (vloat->externalIndex >= oldStartIndex) + vloat->externalIndex += diff; + } +} + +OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget) +{ + TypedPointer <Widget> key (widget); + Float *vloat = floatsByWidget->get (&key); + assert (vloat != NULL); + return vloat; +} + +void OutOfFlowMgr::markSizeChange (int ref) +{ + DBG_OBJ_MSGF ("resize", 0, "<b>markSizeChange</b> (%d)", ref); + DBG_OBJ_MSG_START (); + + if (isRefFloat (ref)) { + Float *vloat; + + if (isRefLeftFloat (ref)) { + int i = getFloatIndexFromRef (ref); + vloat = leftFloatsAll->get (i); + //printf (" => left float %d\n", i); + } else if (isRefRightFloat (ref)) { + int i = getFloatIndexFromRef (ref); + vloat = rightFloatsAll->get (i); + //printf (" => right float %d\n", i); + } else { + assertNotReached(); + vloat = NULL; // compiler happiness + } + + vloat->dirty = vloat->sizeChangedSinceLastAllocation = true; + + // 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, + vloat->generatingBlock->borderChanged (vloat->yReal, vloat->getWidget ()); + } else if (isRefAbsolutelyPositioned (ref)) { + int i = getAbsolutelyPositionedIndexFromRef (ref); + absolutelyPositioned->get(i)->dirty = true; + } else + assertNotReached(); + + DBG_OBJ_MSG_END (); +} + + +void OutOfFlowMgr::markExtremesChange (int ref) +{ + // Nothing to do here. +} + +Widget *OutOfFlowMgr::getWidgetAtPoint (int x, int y, int level) +{ + Widget *childAtPoint = getFloatWidgetAtPoint (leftFloatsCB, x, y, level); + if (childAtPoint == NULL) + childAtPoint = getFloatWidgetAtPoint (rightFloatsCB, x, y, level); + if (childAtPoint == NULL) + childAtPoint = getAbsolutelyPositionedWidgetAtPoint (x, y, level); + return childAtPoint; +} + +Widget *OutOfFlowMgr::getFloatWidgetAtPoint (SortedFloatsVector *list, + int x, int y, int level) +{ + for (int i = 0; i < list->size(); i++) { + // Could use binary search to be faster. + Float *vloat = list->get(i); + Widget *childAtPoint = + vloat->getWidget()->getWidgetAtPoint (x, y, level + 1); + if (childAtPoint) + return childAtPoint; + } + + return NULL; +} + +Widget *OutOfFlowMgr::getAbsolutelyPositionedWidgetAtPoint (int x, int y, + int level) +{ + for (int i = 0; i < absolutelyPositioned->size(); i++) { + AbsolutelyPositioned *abspos = absolutelyPositioned->get(i); + Widget *childAtPoint = abspos->widget->getWidgetAtPoint (x, y, level + 1); + if (childAtPoint) + return childAtPoint; + } + + return NULL; +} + +void OutOfFlowMgr::tellPosition (Widget *widget, int yReq) +{ + if (isWidgetFloat (widget)) + tellFloatPosition (widget, yReq); + + // Nothing to do for absolutely positioned blocks. +} + + +void OutOfFlowMgr::tellFloatPosition (Widget *widget, int yReq) +{ + DBG_OBJ_MSGF ("resize.oofm", 0, "<b>tellFloatPosition</b> (%p, %d)", + widget, yReq); + DBG_OBJ_MSG_START (); + + assert (yReq >= 0); + + Float *vloat = findFloatByWidget(widget); + + SortedFloatsVector *listSame, *listOpp; + Side side; + getFloatsListsAndSide (vloat, &listSame, &listOpp, &side); + ensureFloatSize (vloat); + + // "yReal" may change due to collisions (see below). + vloat->yReq = vloat->yReal = yReq; + + // Test collisions (on this side). Only previous float is relevant. + int yRealNew; + if (vloat->index >= 1 && + collidesV (vloat, listSame->get (vloat->index - 1), &yRealNew)) { + vloat->yReal = yRealNew; + } + + // Test collisions (on the opposite side). Search the last float on + // the other size before this float; only this is relevant. + int lastOppFloat = + listOpp->findLastBeforeSideSpanningIndex (vloat->sideSpanningIndex); + if (lastOppFloat >= 0) { + Float *last = listOpp->get (lastOppFloat); + if (collidesV (vloat, last, &yRealNew)) { + // Here, test also horizontal values. + bool collidesH; + if (vloat->generatingBlock == last->generatingBlock) + collidesH = vloat->size.width + last->size.width + + vloat->generatingBlock->getStyle()->boxDiffWidth() + > vloat->generatingBlock->getAvailWidth(); + else { + // Here (different generating blocks) it can be assumed + // that the allocations are defined, otherwise, the float + // "last" would not be found in "listOpp". + assert (wasAllocated (vloat->generatingBlock)); + assert (wasAllocated (last->generatingBlock)); + Float *left, *right; + if (widget->getStyle()->vloat == FLOAT_LEFT) { + left = vloat; + right = last; + } else { + left = last; + right = vloat; + } + + // right border of the left float (canvas coordinates) + int rightOfLeft = + left->generatingBlock->getAllocation()->x + + left->generatingBlock->getStyle()->boxOffsetX() + + left->size.width; + // left border of the right float (canvas coordinates) + int leftOfRight = + right->generatingBlock->getAllocation()->x + + min (right->generatingBlock->getAllocation()->width, + right->generatingBlock->getAvailWidth()) + - right->generatingBlock->getStyle()->boxRestWidth() + - right->size.width; + + collidesH = rightOfLeft > leftOfRight; + } + + if (collidesH) + vloat->yReal = yRealNew; + } + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "vloat->yReq = %d, vloat->yReal = %d", + vloat->yReq, vloat->yReal); + + DBG_OBJ_MSG_END (); +} + +bool OutOfFlowMgr::collidesV (Float *vloat, Float *other, int *yReal) +{ + // Only checks vertical (possible) collisions, and only refers to + // vloat->yReal; never to vloat->allocation, even when the GBs are + // different. Used only in tellPosition. + + DBG_OBJ_MSGF ("resize.oofm", 0, + "<b>collides</b> (#%d [%p], #%d [%p], ...)", + vloat->index, vloat->getWidget (), other->index, + other->getWidget ()); + DBG_OBJ_MSG_START (); + + bool result; + + DBG_OBJ_MSGF ("resize.oofm", 1, "initial yReal = %d", vloat->yReal); + + if (vloat->generatingBlock == other->generatingBlock) { + ensureFloatSize (other); + int otherBottomGB = + other->yReal + other->size.ascent + other->size.descent; + + DBG_OBJ_MSGF ("resize.oofm", 1, + "same generators: otherBottomGB = %d + (%d + %d) = %d", + other->yReal, other->size.ascent, other->size.descent, + otherBottomGB); + + if (vloat->yReal < otherBottomGB) { + *yReal = otherBottomGB; + result = true; + } else + result = false; + } else { + assert (wasAllocated (vloat->generatingBlock)); + assert (wasAllocated (other->generatingBlock)); + + // If the other float is not allocated, there is no collision. The + // allocation of this float (vloat) is not used at all. + if (!other->getWidget()->wasAllocated ()) + result = false; + else { + Allocation *gba = getAllocation (vloat->generatingBlock), + *flaOther = other->getWidget()->getAllocation (); + int otherBottomGB = + flaOther->y + flaOther->ascent + flaOther->descent - gba->y; + + DBG_OBJ_MSGF ("resize.oofm", 1, + "different generators: " + "otherBottomGB = %d + (%d + %d) - %d = %d", + flaOther->y, flaOther->ascent, flaOther->descent, gba->y, + otherBottomGB); + + if (vloat->yReal < otherBottomGB) { + *yReal = otherBottomGB; + result = true; + } else + result = false; + } + } + + if (result) + DBG_OBJ_MSGF ("resize.oofm", 1, "collides: new yReal = %d", *yReal); + else + DBG_OBJ_MSG ("resize.oofm", 1, "does not collide"); + + DBG_OBJ_MSG_END (); + return result; +} + +void OutOfFlowMgr::getFloatsListsAndSide (Float *vloat, + SortedFloatsVector **listSame, + SortedFloatsVector **listOpp, + Side *side) +{ + TBInfo *tbInfo = getTextblock (vloat->generatingBlock); + + switch (vloat->getWidget()->getStyle()->vloat) { + case FLOAT_LEFT: + if (wasAllocated (vloat->generatingBlock)) { + if (listSame) *listSame = leftFloatsCB; + if (listOpp) *listOpp = rightFloatsCB; + } else { + if (listSame) *listSame = tbInfo->leftFloatsGB; + if (listOpp) *listOpp = tbInfo->rightFloatsGB; + } + if (side) *side = LEFT; + break; + + case FLOAT_RIGHT: + if (wasAllocated (vloat->generatingBlock)) { + if (listSame) *listSame = rightFloatsCB; + if (listOpp) *listOpp = leftFloatsCB; + } else { + if (listSame) *listSame = tbInfo->rightFloatsGB; + if (listOpp) *listOpp = tbInfo->leftFloatsGB; + } + if (side) *side = RIGHT; + break; + + default: + assertNotReached(); + } +} + +void OutOfFlowMgr::getSize (int *oofWidth, int *oofHeight) +{ + DBG_OBJ_MSG ("resize.oofm", 0, "<b>getSize</b> ()"); + DBG_OBJ_MSG_START (); + + int oofWidthAbsPos, oofHeightAbsPos; + getAbsolutelyPositionedSize (&oofWidthAbsPos, &oofHeightAbsPos); + + int oofWidthtLeft, oofWidthRight, oofHeightLeft, oofHeightRight; + getFloatsSize (LEFT, &oofWidthtLeft, &oofHeightLeft); + getFloatsSize (RIGHT, &oofWidthRight, &oofHeightRight); + + *oofWidth = max (oofWidthtLeft, oofWidthRight, oofWidthAbsPos); + *oofHeight = max (oofHeightLeft, oofHeightRight, oofHeightAbsPos); + + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> (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 (); +} + +void OutOfFlowMgr::getFloatsSize (Side side, int *width, int *height) +{ + DBG_OBJ_MSGF ("resize.oofm", 0, "<b>getFloatsSize</b> (%s, ...)", + side == LEFT ? "LEFT" : "RIGHT"); + DBG_OBJ_MSG_START (); + + SortedFloatsVector *list = getFloatsListForTextblock (containingBlock, side); + + *width = *height = 0; + + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + + if (vloat->generatingBlock == containingBlock) { + ensureFloatSize (vloat); + + *width = max (*width, + vloat->size.width + side == LEFT ? + containingBlock->getStyle()->boxOffsetX () : + containingBlock->getStyle()->boxRestWidth ()); + *height = + max (*height, + vloat->yReal + vloat->size.ascent + vloat->size.descent + + containingBlock->getStyle()->boxRestHeight ()); + + DBG_OBJ_MSGF ("resize.oofm", 1, + "considering float %p generated by CB: (%d + %d) * " + "(%d + (%d + %d) + %d) => %d * %d", + vloat->getWidget (), vloat->size.width, + side == LEFT ? + containingBlock->getStyle()->boxOffsetX () : + containingBlock->getStyle()->boxRestWidth (), + vloat->yReal, vloat->size.ascent, vloat->size.descent, + containingBlock->getStyle()->boxRestHeight (), + *width, *height); + } else { + // The GB must be allocated, but the float may not yet be allocated; + // it is ignored in this case. + assert (wasAllocated (vloat->generatingBlock)); + DBG_OBJ_MSGF ("resize.oofm", 1, "considering float %p generated by %p", + vloat->getWidget (), vloat->generatingBlock); + + if (vloat->getWidget()->wasAllocated ()) { + Allocation *fla = vloat->getWidget()->getAllocation (); + *width = max (*width, + fla->x - containingBlockAllocation.x + fla->width); + *height = max (*height, + fla->y - containingBlockAllocation.y + + fla->ascent + fla->descent + + containingBlock->getStyle()->boxRestHeight ()); + + DBG_OBJ_MSGF ("resize.oofm", 1, "... (%d - %d + %d) * " + "(%d - %d + (%d + %d) + %d) => %d * %d", + fla->x, containingBlockAllocation.x, fla->width, + fla->y, containingBlockAllocation.y, + fla->ascent, fla->descent, + containingBlock->getStyle()->boxRestHeight (), + *width, *height); + } else + DBG_OBJ_MSG ("resize.oofm", 1, "... not allocated"); + } + } + + DBG_OBJ_MSG_END (); +} + +void OutOfFlowMgr::getExtremes (int *oofMinWidth, int *oofMaxWidth) +{ + *oofMinWidth = *oofMaxWidth = 0; + accumExtremes (leftFloatsCB, LEFT, oofMinWidth, oofMaxWidth); + accumExtremes (rightFloatsCB, RIGHT, oofMinWidth, oofMaxWidth); + // TODO Absolutely positioned elements +} + +void OutOfFlowMgr::accumExtremes (SortedFloatsVector *list, Side side, + int *oofMinWidth, int *oofMaxWidth) +{ + // Idea for a faster implementation: use incremental resizing? + + DBG_OBJ_MSGF ("resize", 0, "<b>accumExtremes</b> (..., %s, ...)", + side == LEFT ? "LEFT" : "RIGHT"); + DBG_OBJ_MSG_START (); + + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + + int minBorderDiff = getMinBorderDiff (vloat, side); + int maxBorderDiff = getMaxBorderDiff (vloat, side); + Extremes extr; + vloat->getWidget()->getExtremes (&extr); + + DBG_OBJ_MSGF ("resize", 1, "float %p: (%d + %d) / (%d + %d)", + vloat->getWidget(), extr.minWidth, minBorderDiff, + extr.maxWidth, maxBorderDiff); + + + *oofMinWidth = max (*oofMinWidth, extr.minWidth + minBorderDiff); + *oofMaxWidth = max (*oofMaxWidth, extr.maxWidth + maxBorderDiff); + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", *oofMinWidth, *oofMaxWidth); + } + + DBG_OBJ_MSG_END (); +} + +/** + * Minimal difference between generating block and to containing + * block, Greater or equal than 0, so dealing with 0 when it cannot + * yet be calculated is safe. (No distiction whether it is defined or + * not is necessary.) + */ +int OutOfFlowMgr::getMinBorderDiff (Float *vloat, Side side) +{ + if (vloat->generatingBlock == containingBlock) + // Simplest case: the generator is the container. + // Since the way, how left and right floats are positioned when + // there is not much space, is not symmetric, the *minimal* value + // considered for the margin/border/padding of the generating block + // is *zero* for floats on the right. + return + side == LEFT ? vloat->generatingBlock->getStyle()->boxOffsetX() : 0; + else { + if (wasAllocated (containingBlock)) { + if (wasAllocated (vloat->generatingBlock)) { + // Simple case: both containing block and generating block + // are defined. + Allocation *gba = getAllocation(vloat->generatingBlock); + Allocation *cba = getAllocation(containingBlock); + if (side == LEFT) + return gba->x - cba->x + + vloat->generatingBlock->getStyle()->boxOffsetX(); + else + // For margin/border/padding see comment above. Also, + // in the worst case, the float can take the whole CB + // (not GB!) width. Therefore: + return 0; + } else + // Generating block not yet allocation; the next + // allocation will, when necessary, trigger + // getExtremes. (TODO: Is this really the case?) + return 0; + } else + // Nothing can be done now, but the next allocation will + // trigger getExtremes. (TODO: Is this really the case?) + return 0; + } +} + +/** + * Maximal difference between generating block and to containing + * block, i. e. sum on both sides. + */ +int OutOfFlowMgr::getMaxBorderDiff (Float *vloat, Side side) +{ + if (vloat->generatingBlock == containingBlock) + // Simplest case: the generator is the container. + return vloat->generatingBlock->getStyle()->boxDiffWidth(); + else { + if (wasAllocated (containingBlock)) { + if (wasAllocated (vloat->generatingBlock)) + // Simple case: both containing block and generating block + // are defined. + return getAllocation(containingBlock)->width - + getAllocation(vloat->generatingBlock)->width + + vloat->generatingBlock->getStyle()->boxDiffWidth(); + else + // Generating block not yet allocation; the next + // allocation will, when necessary, trigger + // getExtremes. (TODO: Is this really the case?) + return 0; + } else + // Nothing can be done now, but the next allocation will + // trigger getExtremes. (TODO: Is this really the case?) + return 0; + } +} + +OutOfFlowMgr::TBInfo *OutOfFlowMgr::getTextblock (Textblock *textblock) +{ + TypedPointer<Textblock> key (textblock); + TBInfo *tbInfo = tbInfosByTextblock->get (&key); + assert (tbInfo); + return tbInfo; +} + +/** + * Get the left border for the vertical position of *y*, for a height + * of *h", based on floats; relative to the allocation of the calling + * textblock. + * + * The border includes marging/border/padding of the calling textblock + * but is 0 if there is no float, so a caller should also consider + * other borders. + */ +int OutOfFlowMgr::getLeftBorder (Textblock *textblock, int y, int h, + Textblock *lastGB, int lastExtIndex) +{ + int b = getBorder (textblock, LEFT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "left border (%p, %d, %d, %p, %d) => %d", + textblock, y, h, lastGB, lastExtIndex, b); + return b; +} + +/** + * Get the right border for the vertical position of *y*, for a height + * of *h*, based on floats. + * + * See also getLeftBorder(int, int); + */ +int OutOfFlowMgr::getRightBorder (Textblock *textblock, int y, int h, + Textblock *lastGB, int lastExtIndex) +{ + int b = getBorder (textblock, RIGHT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "right border (%p, %d, %d, %p, %d) => %d", + textblock, y, h, lastGB, lastExtIndex, b); + return b; +} + +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 (); + + SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); + int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex); + + DBG_OBJ_MSGF ("border", 1, "first = %d", first); + + if (first == -1) { + // No float. + DBG_OBJ_MSG_END (); + return 0; + } else { + // It is not sufficient to find the first float, since a line + // (with height h) may cover the region of multiple float, of + // which the widest has to be choosen. + int border = 0; + bool covers = true; + // TODO Also check against lastGB and lastExtIndex + for (int i = first; covers && i < list->size(); i++) { + Float *vloat = list->get(i); + covers = vloat->covers (textblock, y, h); + DBG_OBJ_MSGF ("border", 1, "float %d (%p) covers? %s.", + i, vloat->getWidget(), covers ? "<b>yes</b>" : "no"); + + if (covers) { + int thisBorder; + if (vloat->generatingBlock == textblock) { + int borderIn = side == LEFT ? + vloat->generatingBlock->getStyle()->boxOffsetX() : + vloat->generatingBlock->getStyle()->boxRestWidth(); + thisBorder = vloat->size.width + borderIn; + } else { + assert (wasAllocated (vloat->generatingBlock)); + assert (vloat->getWidget()->wasAllocated ()); + + Allocation *tba = getAllocation(textblock), + *fla = vloat->getWidget()->getAllocation (); + thisBorder = side == LEFT ? + fla->x + fla->width - tba->x : + tba->x + tba->width - fla->x; + } + + border = max (border, thisBorder); + DBG_OBJ_MSGF ("border", 1, "=> border = %d", border); + } + } + + DBG_OBJ_MSG_END (); + return border; + } +} + + +OutOfFlowMgr::SortedFloatsVector *OutOfFlowMgr::getFloatsListForTextblock + (Textblock *textblock, Side side) +{ + if (wasAllocated (textblock)) + return side == LEFT ? leftFloatsCB : rightFloatsCB; + else { + TBInfo *tbInfo = getTextblock (textblock); + return side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; + } +} + + +bool OutOfFlowMgr::hasFloatLeft (Textblock *textblock, int y, int h, + Textblock *lastGB, int lastExtIndex) +{ + bool b = hasFloat (textblock, LEFT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "has float left (%p, %d, %d, %p, %d) => %s", + textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false"); + return b; +} + +bool OutOfFlowMgr::hasFloatRight (Textblock *textblock, int y, int h, + Textblock *lastGB, int lastExtIndex) +{ + bool b = hasFloat (textblock, RIGHT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "has float right (%p, %d, %d, %p, %d) => %s", + textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false"); + return b; +} + +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 (); + + 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 (); + return first != -1; +} + +/** + * Returns position relative to the textblock "tb". + */ +int OutOfFlowMgr::getClearPosition (Textblock *tb) +{ + if (tb->getStyle()) { + bool left = false, right = false; + switch (tb->getStyle()->clear) { + case core::style::CLEAR_NONE: break; + case core::style::CLEAR_LEFT: left = true; break; + case core::style::CLEAR_RIGHT: right = true; break; + case core::style::CLEAR_BOTH: left = right = true; break; + default: assertNotReached (); + } + + return max (left ? getClearPosition (tb, LEFT) : 0, + right ? getClearPosition (tb, RIGHT) : 0); + } else + return 0; +} + +int OutOfFlowMgr::getClearPosition (Textblock *tb, Side side) +{ + if (!wasAllocated (tb)) + // There is no relation yet to floats generated by other + // textblocks, and this textblocks floats are unimportant for + // the "clear" property. + return 0; + + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + + int i = list->findFloatIndex (tb, 0); + if (i < 0) + return 0; + + Float *vloat = list->get(i); + // We pass this texblock and 0 (first word, or smallest external + // index, respectively), but search for the last float before this + // position. Therefore this check. + if (vloat->generatingBlock== tb) + i--; + if (i < 0) + return 0; + + vloat = list->get(i); + ensureFloatSize (vloat); + return + vloat->yReal + vloat->size.ascent + vloat->size.descent - + getAllocation(tb)->y; +} + +void OutOfFlowMgr::ensureFloatSize (Float *vloat) +{ + if (vloat->dirty || + // 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) && + vloat->cbAvailWidth != containingBlock->getAvailWidth ())) { + DBG_OBJ_MSGF ("resize.oofm", 0, + "<b>ensureFloatSize</b> (%p): recalculation", + vloat->getWidget ()); + DBG_OBJ_MSG_START (); + + // TODO Ugly. Soon to be replaced by cleaner code? See also + // comment in Textblock::calcWidgetSize. + if (vloat->getWidget()->usesHints ()) { + if (isAbsLength (vloat->getWidget()->getStyle()->width)) { + int width = absLengthVal (vloat->getWidget()->getStyle()->width); + vloat->getWidget()->setWidth (width); + DBG_OBJ_MSGF ("resize.oofm", 1, "setting absolute width: %d", + width); + } else if (isPerLength (vloat->getWidget()->getStyle()->width)) { + int width = + multiplyWithPerLength (containingBlock->getAvailWidth(), + vloat->getWidget()->getStyle()->width); + vloat->getWidget()->setWidth (width); + DBG_OBJ_MSGF ("resize.oofm", 1, + "setting percentage width: %d * %g = %d", + containingBlock->getAvailWidth(), + perLengthVal (vloat->getWidget()->getStyle()->width), + width); + } else + DBG_OBJ_MSG ("resize.oofm", 1, "setting no width: not defined"); + } 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. + Extremes extremes; + vloat->getWidget()->getExtremes (&extremes); + DBG_OBJ_MSGF ("resize.oofm", 1, "getExtremes => %d / %d", + extremes.minWidth, extremes.maxWidth); + + 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); + + // Set width ... + int width = vloat->size.width; + DBG_OBJ_MSGF ("resize.oofm", 1, "new width: %d", width); + DBG_OBJ_MSG_START (); + + // Consider the available width of the containing block (when set): + if (width > containingBlock->getAvailWidth()) { + width = containingBlock->getAvailWidth(); + DBG_OBJ_MSGF ("resize.oofm", 1, "adjusted to availWidth: %d", width); + } + // Finally, 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); + } + + DBG_OBJ_MSG_END (); + + 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->dirty = false; + + DBG_OBJ_MSGF ("resize.oofm", 1, "final size: %d * (%d + %d)", + vloat->size.width, vloat->size.ascent, vloat->size.descent); + + DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float-size>.width", + vloat->size.width); + DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float-size>.ascent", + vloat->size.ascent); + DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float-size>.descent", + vloat->size.descent); + + // "sizeChangedSinceLastAllocation" is reset in sizeAllocateEnd() + + DBG_OBJ_MSG_END (); + } +} + +void OutOfFlowMgr::getAbsolutelyPositionedSize (int *oofWidthAbsPos, + int *oofHeightAbsPos) +{ + *oofWidthAbsPos = *oofHeightAbsPos = 0; + + for (int i = 0; i < absolutelyPositioned->size(); i++) { + AbsolutelyPositioned *abspos = absolutelyPositioned->get (i); + ensureAbsolutelyPositionedSizeAndPosition (abspos); + *oofWidthAbsPos = max (*oofWidthAbsPos, abspos->xCB + abspos->width); + *oofHeightAbsPos = max (*oofHeightAbsPos, abspos->yCB + abspos->height); + } +} + +void OutOfFlowMgr::ensureAbsolutelyPositionedSizeAndPosition + (AbsolutelyPositioned *abspos) +{ + // TODO Similar to floats, changes of the available size of the + // containing block are not noticed; which is a bit more severe as + // for floats. Find a general solution for both floats and + // absolutely positioned blocks. + + // TODO Compare to ensureFloatSize: some parts are + // missing. Nevertheless, a simpler approach should be focussed on. + + 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; + } +} + +int OutOfFlowMgr::calcValueForAbsolutelyPositioned + (AbsolutelyPositioned *abspos, Length styleLen, int refLen) +{ + assert (styleLen != LENGTH_AUTO); + if (isAbsLength (styleLen)) + return absLengthVal (styleLen); + else if (isPerLength (styleLen)) + return multiplyWithPerLength (refLen, styleLen); + else { + assertNotReached (); + return 0; // compiler happiness + } +} + +void OutOfFlowMgr::sizeAllocateAbsolutelyPositioned () +{ + for (int i = 0; i < absolutelyPositioned->size(); i++) { + Allocation *cbAllocation = getAllocation(containingBlock); + AbsolutelyPositioned *abspos = absolutelyPositioned->get (i); + ensureAbsolutelyPositionedSizeAndPosition (abspos); + + Allocation childAllocation; + childAllocation.x = cbAllocation->x + abspos->xCB; + childAllocation.y = cbAllocation->y + abspos->yCB; + childAllocation.width = abspos->width; + childAllocation.ascent = abspos->height; + childAllocation.descent = 0; // TODO + + abspos->widget->sizeAllocate (&childAllocation); + + printf ("[%p] allocating child %p at: (%d, %d), %d x (%d + %d)\n", + containingBlock, abspos->widget, childAllocation.x, + childAllocation.y, childAllocation.width, childAllocation.ascent, + childAllocation.descent); + } +} + +} // namespace dw diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh new file mode 100644 index 00000000..f33913ab --- /dev/null +++ b/dw/outofflowmgr.hh @@ -0,0 +1,425 @@ +#ifndef __DW_OUTOFFLOWMGR_HH__ +#define __DW_OUTOFFLOWMGR_HH__ + +#include "core.hh" + +namespace dw { + +class Textblock; + +/** + * \brief Represents additional data for containing blocks. + */ +class OutOfFlowMgr +{ + friend class WidgetInfo; + +private: + enum Side { LEFT, RIGHT }; + + Textblock *containingBlock; + + // These two values are set by sizeAllocateStart(), and they are + // accessable also within sizeAllocateEnd(), and also for the + // containing block, for which allocation and WAS_ALLOCATED is set + // *after* sizeAllocateEnd(). See the two inline functions + // wasAllocated(Widget*) and getAllocation(Widget*) (further down) + // for usage. + core::Allocation containingBlockAllocation; + bool containingBlockWasAllocated; + + class WidgetInfo: public lout::object::Object + { + private: + bool wasAllocated; + int xCB, yCB; // relative to the containing block + int width, height; + + OutOfFlowMgr *oofm; + core::Widget *widget; + + protected: + OutOfFlowMgr *getOutOfFlowMgr () { return oofm; } + + public: + WidgetInfo (OutOfFlowMgr *oofm, core::Widget *widget); + + inline bool wasThenAllocated () { return wasAllocated; } + inline int getOldXCB () { return xCB; } + inline int getOldYCB () { return yCB; } + inline int getOldWidth () { return width; } + inline int getOldHeight () { return height; } + + inline bool isNowAllocated () { return widget->wasAllocated (); } + inline int getNewXCB () { return widget->getAllocation()->x - + oofm->containingBlockAllocation.x; } + inline int getNewYCB () { return widget->getAllocation()->y - + oofm->containingBlockAllocation.y; } + inline int getNewWidth () { return widget->getAllocation()->width; } + inline int getNewHeight () { return widget->getAllocation()->ascent + + widget->getAllocation()->descent; } + + void update (bool wasAllocated, int xCB, int yCB, int width, int height); + void updateAllocation (); + + inline core::Widget *getWidget () { return widget; } + }; + + class Float: public WidgetInfo + { + public: + class ComparePosition: public lout::object::Comparator + { + private: + OutOfFlowMgr *oofm; + Textblock *refTB; + + public: + ComparePosition (OutOfFlowMgr *oofm, Textblock *refTB) + { this->oofm = oofm; this->refTB = refTB; } + int compare(Object *o1, Object *o2); + }; + + class CompareSideSpanningIndex: public lout::object::Comparator + { + public: + int compare(Object *o1, Object *o2); + }; + + class CompareGBAndExtIndex: public lout::object::Comparator + { + private: + OutOfFlowMgr *oofm; + + public: + CompareGBAndExtIndex (OutOfFlowMgr *oofm) { this->oofm = oofm; } + int compare(Object *o1, Object *o2); + }; + + Textblock *generatingBlock; + int externalIndex; + int yReq, yReal; // relative to generator, not container + int index; /* When GB is not yet allocated: position + within TBInfo::leftFloatsGB or + TBInfo::rightFloatsGB, respectively. When GB + is allocated: position within leftFloatsCB + or rightFloatsCB, respectively, even when + the floats are still elements of + TBInfo::*FloatsGB. */ + 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. */ + bool dirty, sizeChangedSinceLastAllocation; + bool inCBList; /* Neccessary to prevent floats from being moved + twice from GB to CB list. */ + + Float (OutOfFlowMgr *oofm, core::Widget *widget, + Textblock *generatingBlock, int externalIndex); + + void intoStringBuffer(lout::misc::StringBuffer *sb); + + bool covers (Textblock *textblock, int y, int h); + }; + + /** + * This list is kept sorted. + * + * To prevent accessing methods of the base class in an + * uncontrolled way, the inheritance is private, not public; this + * means that all methods must be delegated (see iterator(), size() + * etc. below.) + * + * TODO Update comment: still sorted, but ... + * + * More: add() and change() may check order again. + */ + class SortedFloatsVector: private lout::container::typed::Vector<Float> + { + public: + enum Type { GB, CB } type; + + private: + OutOfFlowMgr *oofm; + Side side; + + public: + inline SortedFloatsVector (OutOfFlowMgr *oofm, Side side, Type type) : + lout::container::typed::Vector<Float> (1, false) + { this->oofm = oofm; this->side = side; this->type = type; } + + int findFloatIndex (Textblock *lastGB, int lastExtIndex); + int find (Textblock *textblock, int y, int start, int end); + int findFirst (Textblock *textblock, int y, int h, Textblock *lastGB, + int lastExtIndex); + int findLastBeforeSideSpanningIndex (int sideSpanningIndex); + void put (Float *vloat); + + inline lout::container::typed::Iterator<Float> iterator() + { return lout::container::typed::Vector<Float>::iterator (); } + inline int size () + { return lout::container::typed::Vector<Float>::size (); } + inline Float *get (int pos) + { return lout::container::typed::Vector<Float>::get (pos); } + inline void clear () + { lout::container::typed::Vector<Float>::clear (); } + }; + + class TBInfo: public WidgetInfo + { + public: + int availWidth; + int index; // position within "tbInfos" + + TBInfo *parent; + int parentExtIndex; + + // These two lists store all floats generated by this textblock, + // as long as this textblock is not allocates. + SortedFloatsVector *leftFloatsGB, *rightFloatsGB; + + TBInfo (OutOfFlowMgr *oofm, Textblock *textblock, + TBInfo *parent, int parentExtIndex); + ~TBInfo (); + + inline Textblock *getTextblock () { return (Textblock*)getWidget (); } + }; + + class AbsolutelyPositioned: public lout::object::Object + { + public: + core::Widget *widget; + int xCB, yCB; // relative to the containing block + int width, height; + bool dirty; + + AbsolutelyPositioned (OutOfFlowMgr *oofm, core::Widget *widget, + Textblock *generatingBlock, int externalIndex); + }; + + // These two lists store all floats, in the order in which they are + // defined. Only used for iterators. + lout::container::typed::Vector<Float> *leftFloatsAll, *rightFloatsAll; + + // These two lists store all floats whose generators are already + // allocated. + SortedFloatsVector *leftFloatsCB, *rightFloatsCB; + + lout::container::typed::HashTable<lout::object::TypedPointer + <dw::core::Widget>, Float> *floatsByWidget; + + lout::container::typed::Vector<TBInfo> *tbInfos; + lout::container::typed::HashTable<lout::object::TypedPointer <Textblock>, + TBInfo> *tbInfosByTextblock; + + lout::container::typed::Vector<AbsolutelyPositioned> *absolutelyPositioned; + + int lastLeftTBIndex, lastRightTBIndex, leftFloatsMark, rightFloatsMark; + + /** + * Variant of Widget::wasAllocated(), which can also be used within + * OOFM::sizeAllocateEnd(), and also for the generating block. + */ + inline bool wasAllocated (core::Widget *widget) { + return widget->wasAllocated () || + (widget == (core::Widget*)containingBlock && + containingBlockWasAllocated); } + + /** + * Variant of Widget::getAllocation(), which can also be used + * within OOFM::sizeAllocateEnd(), and also for the generating + * block. + */ + inline core::Allocation *getAllocation (core::Widget *widget) { + return widget == (core::Widget*)containingBlock ? + &containingBlockAllocation : widget->getAllocation (); } + + void moveExternalIndices (SortedFloatsVector *list, int oldStartIndex, + int diff); + Float *findFloatByWidget (core::Widget *widget); + + void moveFromGBToCB (Side side); + void sizeAllocateFloats (Side side); + int calcFloatX (Float *vloat, Side side, int gbX, int gbWidth, + int gbAvailWidth); + + bool hasRelationChanged (TBInfo *tbInfo,int *minFloatPos, + core::Widget **minFloat); + bool hasRelationChanged (TBInfo *tbInfo, Side side, int *minFloatPos, + core::Widget **minFloat); + bool hasRelationChanged (bool oldTBAlloc, + int oldTBx, int oldTBy, int oldTBw, int oldTBh, + int newTBx, int newTBy, int newTBw, int newTBh, + bool oldFlAlloc, + int oldFlx, int oldFly, int oldFlw, int oldFlh, + int newFlx, int newFly, int newFlw, int newFlh, + Side side, int *floatPos); + + bool isTextblockCoveredByFloat (Float *vloat, Textblock *tb, + int tbx, int tby, int tbWidth, int tbHeight, + int *floatPos); + + void drawFloats (SortedFloatsVector *list, core::View *view, + core::Rectangle *area); + void drawAbsolutelyPositioned (core::View *view, core::Rectangle *area); + core::Widget *getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y, + int level); + core::Widget *getAbsolutelyPositionedWidgetAtPoint (int x, int y, int level); + + bool collidesV (Float *vloat, Float *other, int *yReal); + + void getFloatsListsAndSide (Float *vloat, SortedFloatsVector **listSame, + SortedFloatsVector **listOpp, Side *side); + + void getFloatsSize (Side side, int *width, int *height); + void accumExtremes (SortedFloatsVector *list, Side side, int *oofMinWidth, + int *oofMaxWidth); + int getMinBorderDiff (Float *vloat, Side side); + int getMaxBorderDiff (Float *vloat, Side side); + + TBInfo *getTextblock (Textblock *textblock); + int getBorder (Textblock *textblock, Side side, int y, int h, + Textblock *lastGB, int lastExtIndex); + SortedFloatsVector *getFloatsListForTextblock (Textblock *textblock, + Side side); + bool hasFloat (Textblock *textblock, Side side, int y, int h, + Textblock *lastGB, int lastExtIndex); + + int getClearPosition (Textblock *tb, Side side); + + void ensureFloatSize (Float *vloat); + + void tellFloatPosition (core::Widget *widget, int yReq); + + void getAbsolutelyPositionedSize (int *oofWidthAbsPos, int *oofHeightAbsPos); + void ensureAbsolutelyPositionedSizeAndPosition (AbsolutelyPositioned + *abspos); + int calcValueForAbsolutelyPositioned (AbsolutelyPositioned *abspos, + core::style::Length styleLen, + int refLen); + void sizeAllocateAbsolutelyPositioned (); + + static inline bool isWidgetFloat (core::Widget *widget) + { return widget->getStyle()->vloat != core::style::FLOAT_NONE; } + static inline bool isWidgetAbsolutelyPositioned (core::Widget *widget) + { return widget->getStyle()->position == core::style::POSITION_ABSOLUTE; } + + /* + * Format for parent ref (see also below for isRefOutOfFlow, + * createRefNormalFlow, and getLineNoFromRef. + * + * Widget in flow: + * + * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + * | line number | 0 | + * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + * + * So, anything with the least signifant bit set to 1 is out of flow. + * + * Floats: + * + * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + * | left float index | 0 | 0 | 1 | + * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + * + * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + * | right float index | 1 | 0 | 1 | + * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + * + * Absolutely positioned blocks: + * + * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + * | index | 1 | 1 | + * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + */ + + inline static bool isRefFloat (int ref) + { return ref != -1 && (ref & 3) == 1; } + inline static bool isRefLeftFloat (int ref) + { return ref != -1 && (ref & 7) == 1; } + inline static bool isRefRightFloat (int ref) + { return ref != -1 && (ref & 7) == 5; } + inline static bool isRefAbsolutelyPositioned (int ref) + { return ref != -1 && (ref & 3) == 3; } + + inline static int createRefLeftFloat (int index) + { return (index << 3) | 1; } + inline static int createRefRightFloat (int index) + { return (index << 3) | 5; } + inline static int createRefAbsolutelyPositioned (int index) + { return (index << 2) | 3; } + + inline static int getFloatIndexFromRef (int ref) + { return ref == -1 ? ref : (ref >> 3); } + inline static int getAbsolutelyPositionedIndexFromRef (int ref) + { return ref == -1 ? ref : (ref >> 2); } + +public: + OutOfFlowMgr (Textblock *containingBlock); + ~OutOfFlowMgr (); + + void sizeAllocateStart (core::Allocation *containingBlockAllocation); + void sizeAllocateEnd (); + void draw (core::View *view, core::Rectangle *area); + + void markSizeChange (int ref); + void markExtremesChange (int ref); + core::Widget *getWidgetAtPoint (int x, int y, int level); + + static bool isWidgetOutOfFlow (core::Widget *widget); + static bool isWidgetHandledByOOFM (core::Widget *widget); + void addWidgetInFlow (Textblock *textblock, Textblock *parentBlock, + int externalIndex); + void addWidgetOOF (core::Widget *widget, Textblock *generatingBlock, + int externalIndex); + void moveExternalIndices (Textblock *generatingBlock, int oldStartIndex, + int diff); + + void tellPosition (core::Widget *widget, int yReq); + + void getSize (int *oofWidth, int *oofHeight); + void getExtremes (int *oofMinWidth, int *oofMaxWidth); + + int getLeftBorder (Textblock *textblock, int y, int h, Textblock *lastGB, + int lastExtIndex); + int getRightBorder (Textblock *textblock, int y, int h, Textblock *lastGB, + int lastExtIndex); + + bool hasFloatLeft (Textblock *textblock, int y, int h, Textblock *lastGB, + int lastExtIndex); + bool hasFloatRight (Textblock *textblock, int y, int h, Textblock *lastGB, + int lastExtIndex); + + int getClearPosition (Textblock *tb); + + inline static bool isRefOutOfFlow (int ref) + { return ref != -1 && (ref & 1) != 0; } + inline static int createRefNormalFlow (int lineNo) { return lineNo << 1; } + inline static int getLineNoFromRef (int ref) + { return ref == -1 ? ref : (ref >> 1); } + + // for iterators + inline int getNumWidgets () { + return leftFloatsAll->size() + rightFloatsAll->size() + + absolutelyPositioned->size(); } + + inline core::Widget *getWidget (int i) { + if (i < leftFloatsAll->size()) + return leftFloatsAll->get(i)->getWidget (); + else if (i < leftFloatsAll->size() + rightFloatsAll->size()) + return rightFloatsAll->get(i - leftFloatsAll->size())->getWidget (); + else + return absolutelyPositioned->get(i - (leftFloatsAll->size() + + rightFloatsAll->size()))->widget; + } + + inline bool affectsLeftBorder (core::Widget *widget) { + return widget->getStyle()->vloat == core::style::FLOAT_LEFT; } + inline bool affectsRightBorder (core::Widget *widget) { + return widget->getStyle()->vloat == core::style::FLOAT_RIGHT; } +}; + +} // namespace dw + +#endif // __DW_OUTOFFLOWMGR_HH__ diff --git a/dw/ruler.cc b/dw/ruler.cc index 115dfaa5..2b5288c2 100644 --- a/dw/ruler.cc +++ b/dw/ruler.cc @@ -28,17 +28,28 @@ namespace dw { Ruler::Ruler () { + setFlags (USES_HINTS); setFlags (BLOCK_LEVEL); unsetFlags (HAS_CONTENTS); + availWidth = 0; } void Ruler::sizeRequestImpl (core::Requisition *requisition) { - requisition->width = getStyle()->boxDiffWidth (); + requisition->width = + lout::misc::max (availWidth, getStyle()->boxDiffWidth ()); requisition->ascent = getStyle()->boxOffsetY (); requisition->descent = getStyle()->boxRestHeight (); } +void Ruler::setWidth (int width) +{ + if (availWidth != width) { + availWidth = width; + queueResize (0, false); + } +} + void Ruler::draw (core::View *view, core::Rectangle *area) { drawWidgetBox (view, area, false); diff --git a/dw/ruler.hh b/dw/ruler.hh index 32e859a1..863792dd 100644 --- a/dw/ruler.hh +++ b/dw/ruler.hh @@ -15,8 +15,12 @@ namespace dw { */ class Ruler: public core::Widget { +private: + int availWidth; + protected: void sizeRequestImpl (core::Requisition *requisition); + void setWidth (int width); void draw (core::View *view, core::Rectangle *area); public: diff --git a/dw/style.cc b/dw/style.cc index 8ec230a1..5edb7047 100644 --- a/dw/style.cc +++ b/dw/style.cc @@ -17,8 +17,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - - #include <stdio.h> #include <string.h> #include <unistd.h> @@ -75,6 +73,10 @@ void StyleAttrs::initValues () backgroundPositionX = createPerLength (0); backgroundPositionY = createPerLength (0); width = height = lineHeight = LENGTH_AUTO; + vloat = FLOAT_NONE; + clear = CLEAR_NONE; + position = POSITION_STATIC; + top = bottom = left = right = LENGTH_AUTO; textIndent = 0; margin.setVal (0); borderWidth.setVal (0); @@ -101,6 +103,11 @@ void StyleAttrs::resetValues () valign = VALIGN_BASELINE; textAlignChar = '.'; + vloat = FLOAT_NONE; /** \todo Correct? Check specification. */ + clear = CLEAR_NONE; /** \todo Correct? Check specification. */ + position = POSITION_STATIC; /** \todo Correct? Check specification. */ + top = bottom = left = right = LENGTH_AUTO; /** \todo Correct? Check + specification. */ backgroundColor = NULL; backgroundImage = NULL; backgroundRepeat = BACKGROUND_REPEAT; @@ -156,6 +163,13 @@ bool StyleAttrs::equals (object::Object *other) { valign == otherAttrs->valign && textAlignChar == otherAttrs->textAlignChar && textTransform == otherAttrs->textTransform && + vloat == otherAttrs->vloat && + clear == otherAttrs->clear && + position == otherAttrs->position && + top == otherAttrs->top && + bottom == otherAttrs->bottom && + left == otherAttrs->left && + right == otherAttrs->right && hBorderSpacing == otherAttrs->hBorderSpacing && vBorderSpacing == otherAttrs->vBorderSpacing && wordSpacing == otherAttrs->wordSpacing && @@ -201,6 +215,13 @@ int StyleAttrs::hashValue () { valign + textAlignChar + textTransform + + vloat + + clear + + position + + top + + bottom + + left + + right + hBorderSpacing + vBorderSpacing + wordSpacing + @@ -316,6 +337,13 @@ void Style::copyAttrs (StyleAttrs *attrs) valign = attrs->valign; textAlignChar = attrs->textAlignChar; textTransform = attrs->textTransform; + vloat = attrs->vloat; + clear = attrs->clear; + position = attrs->position; + top = attrs->top; + bottom = attrs->bottom; + left = attrs->left; + right = attrs->right; hBorderSpacing = attrs->hBorderSpacing; vBorderSpacing = attrs->vBorderSpacing; wordSpacing = attrs->wordSpacing; diff --git a/dw/style.hh b/dw/style.hh index e0ce9d89..2cc258bf 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -296,7 +296,6 @@ enum ListStylePosition { LIST_STYLE_POSITION_INSIDE, LIST_STYLE_POSITION_OUTSIDE }; - enum ListStyleType { LIST_STYLE_TYPE_DISC, LIST_STYLE_TYPE_CIRCLE, @@ -332,6 +331,13 @@ enum FontVariant { FONT_VARIANT_SMALL_CAPS }; +enum Position { + POSITION_STATIC, + POSITION_RELATIVE, + POSITION_ABSOLUTE, + POSITION_FIXED, +}; + enum TextDecoration { TEXT_DECORATION_NONE = 0, TEXT_DECORATION_UNDERLINE = 1 << 0, @@ -348,6 +354,19 @@ enum WhiteSpace { WHITE_SPACE_PRE_LINE, }; +enum FloatType { + FLOAT_NONE, + FLOAT_LEFT, + FLOAT_RIGHT +}; + +enum ClearType { + CLEAR_LEFT, + CLEAR_RIGHT, + CLEAR_BOTH, + CLEAR_NONE +}; + /** * \brief Type for representing all lengths within dw::core::style. * @@ -503,6 +522,12 @@ public: VAlignType valign; char textAlignChar; /* In future, strings will be supported. */ TextTransform textTransform; + + FloatType vloat; /* "float" is a keyword. */ + ClearType clear; + + Position position; + Length top, bottom, left, right; int hBorderSpacing, vBorderSpacing, wordSpacing; Length width, height, lineHeight, textIndent; diff --git a/dw/table.cc b/dw/table.cc index b6f7209b..565dfc9e 100644 --- a/dw/table.cc +++ b/dw/table.cc @@ -482,7 +482,7 @@ void Table::reallocChildren (int newNumCols, int newNumRows) void Table::calcCellSizes () { - if (needsResize ()) + if (needsResize () || resizeQueued ()) forceCalcCellSizes (); } @@ -640,7 +640,7 @@ void Table::apportionRowSpan () */ void Table::calcColumnExtremes () { - if (extremesChanged ()) + if (extremesChanged () || extremesQueued ()) forceCalcColumnExtremes (); } @@ -1130,7 +1130,7 @@ Table::TableIterator::TableIterator (Table *table, else if (index >= table->children->size ()) content.type = core::Content::END; else { - content.type = core::Content::WIDGET; + content.type = core::Content::WIDGET_IN_FLOW; content.widget = table->children->get(index)->cell.widget; } } @@ -1152,8 +1152,8 @@ bool Table::TableIterator::next () if (content.type == core::Content::END) return false; - // tables only contain widgets: - if ((getMask() & core::Content::WIDGET) == 0) { + // tables only contain widgets (in flow): + if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { content.type = core::Content::END; return false; } @@ -1167,7 +1167,7 @@ bool Table::TableIterator::next () } while (table->children->get(index) == NULL || table->children->get(index)->type != Child::CELL); - content.type = core::Content::WIDGET; + content.type = core::Content::WIDGET_IN_FLOW; content.widget = table->children->get(index)->cell.widget; return true; } @@ -1179,8 +1179,8 @@ bool Table::TableIterator::prev () if (content.type == core::Content::START) return false; - // tables only contain widgets: - if ((getMask() & core::Content::WIDGET) == 0) { + // tables only contain widgets (in flow): + if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { content.type = core::Content::START; return false; } @@ -1194,7 +1194,7 @@ bool Table::TableIterator::prev () } while (table->children->get(index) == NULL || table->children->get(index)->type != Child::CELL); - content.type = core::Content::WIDGET; + content.type = core::Content::WIDGET_IN_FLOW; content.widget = table->children->get(index)->cell.widget; return true; } diff --git a/dw/table.hh b/dw/table.hh index 1d14ec07..6966a163 100644 --- a/dw/table.hh +++ b/dw/table.hh @@ -71,7 +71,7 @@ namespace dw { * is the case. * * [C] Whether this function is called, depends on NEEDS_RESIZE / - * EXTREMES_CHANGED. + * RESIZE_QUEUED / EXTREMES_CHANGED / EXTREMES_QUEUED. * * * <h4>Apportionment</h4> diff --git a/dw/tablecell.cc b/dw/tablecell.cc index 8612f620..67d6cf2d 100644 --- a/dw/tablecell.cc +++ b/dw/tablecell.cc @@ -107,7 +107,7 @@ int TableCell::getValue () void TableCell::setMaxValue (int maxValue, int value) { line1Offset = maxValue - value; - queueResize (0, true); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), true); } } // namespace dw diff --git a/dw/textblock.cc b/dw/textblock.cc index 2c4ca20b..91f9a82e 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -25,13 +25,14 @@ #include "../lout/debug.hh" #include <stdio.h> -#include <math.h> +#include <math.h> // remove again? +#include <limits.h> /* * Local variables */ - /* The tooltip under mouse pointer in current textblock. No ref. hold. - * (having one per view looks not worth the extra clutter). */ +/* The tooltip under mouse pointer in current textblock. No ref. hold. + * (having one per view looks not worth the extra clutter). */ static dw::core::style::Tooltip *hoverTooltip = NULL; @@ -228,6 +229,7 @@ Textblock::Textblock (bool limitTextWidth) setFlags (USES_HINTS); setButtonSensitive(true); + containingBlock = NULL; hasListitemValue = false; innerPadding = 0; line1Offset = 0; @@ -250,15 +252,14 @@ Textblock::Textblock (bool limitTextWidth) nonTemporaryLines = 0; words = new misc::NotSoSimpleVector <Word> (1); anchors = new misc::SimpleVector <Anchor> (1); - - //DBG_OBJ_SET_NUM(this, "num_lines", num_lines); + outOfFlowMgr = NULL; wrapRefLines = wrapRefParagraphs = -1; - //DBG_OBJ_SET_NUM(this, "last_line_width", last_line_width); - //DBG_OBJ_SET_NUM(this, "last_line_par_min", last_line_par_min); - //DBG_OBJ_SET_NUM(this, "last_line_par_max", last_line_par_max); - //DBG_OBJ_SET_NUM(this, "wrap_ref", wrap_ref); + DBG_OBJ_SET_NUM ("lines.size", lines->size ()); + DBG_OBJ_SET_NUM ("words.size", words->size ()); + DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines); + DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs); hoverLink = -1; @@ -267,6 +268,8 @@ Textblock::Textblock (bool limitTextWidth) availAscent = 100; availDescent = 0; + verticalOffset = 0; + this->limitTextWidth = limitTextWidth; for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { @@ -276,6 +279,8 @@ Textblock::Textblock (bool limitTextWidth) hlEnd[layer].index = 0; hlEnd[layer].nChar = 0; } + + initNewLine (); } Textblock::~Textblock () @@ -288,8 +293,9 @@ Textblock::~Textblock () for (int i = 0; i < words->size(); i++) { Word *word = words->getRef (i); - if (word->content.type == core::Content::WIDGET) + if (word->content.type == core::Content::WIDGET_IN_FLOW) delete word->content.widget; + /** \todo Widget references? What about texts? */ removeWordImgRenderer (i); removeSpaceImgRenderer (i); @@ -309,6 +315,9 @@ Textblock::~Textblock () delete words; delete anchors; + if(outOfFlowMgr) + delete outOfFlowMgr; + /* Make sure we don't own widgets anymore. Necessary before call of parent class destructor. (???) */ words = NULL; @@ -323,7 +332,8 @@ Textblock::~Textblock () */ void Textblock::sizeRequestImpl (core::Requisition *requisition) { - PRINTF ("[%p] SIZE_REQUEST: ...\n", this); + DBG_OBJ_MSG ("resize", 0, "<b>sizeRequestImpl</b> ()"); + DBG_OBJ_MSG_START (); rewrap (); showMissingLines (); @@ -331,18 +341,18 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) if (lines->size () > 0) { Line *lastLine = lines->getRef (lines->size () - 1); requisition->width = lastLine->maxLineWidth; - - PRINTF ("[%p] SIZE_REQUEST: lastLine->maxLineWidth = %d\n", - this, lastLine->maxLineWidth); - - PRINTF ("[%p] SIZE_REQUEST: lines[0]->boxAscent = %d\n", - this, lines->getRef(0)->boxAscent); - PRINTF ("[%p] SIZE_REQUEST: lines[%d]->top = %d\n", - this, lines->size () - 1, lastLine->top); - PRINTF ("[%p] SIZE_REQUEST: lines[%d]->boxAscent = %d\n", - this, lines->size () - 1, lastLine->boxAscent); - PRINTF ("[%p] SIZE_REQUEST: lines[%d]->boxDescent = %d\n", - this, lines->size () - 1, lastLine->boxDescent); + + DBG_OBJ_MSGF ("resize", 1, "lines[%d]->maxLineWidth = %d", + lines->size () - 1, lastLine->maxLineWidth); + + DBG_OBJ_MSGF ("resize", 1, "lines[0]->boxAscent = %d", + lines->getRef(0)->boxAscent); + DBG_OBJ_MSGF ("resize", 1, "lines[%d]->top = %d", + lines->size () - 1, lastLine->top); + DBG_OBJ_MSGF ("resize", 1, "lines[%d]->boxAscent = %d", + lines->size () - 1, lastLine->boxAscent); + DBG_OBJ_MSGF ("resize", 1, "lines[%d]->boxDescent = %d", + lines->size () - 1, lastLine->boxDescent); /* Note: the breakSpace of the last line is ignored, so breaks at the end of a textblock are not visible. */ @@ -356,18 +366,42 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) requisition->descent = 0; } - PRINTF ("[%p] SIZE_REQUEST: inner padding = %d, boxDiffWidth = %d\n", - this, innerPadding, getStyle()->boxDiffWidth ()); + DBG_OBJ_MSGF ("resize", 1, "inner padding = %d, boxDiffWidth = %d", + innerPadding, getStyle()->boxDiffWidth ()); requisition->width += innerPadding + getStyle()->boxDiffWidth (); - requisition->ascent += getStyle()->boxOffsetY (); + requisition->ascent += verticalOffset + getStyle()->boxOffsetY (); requisition->descent += getStyle()->boxRestHeight (); - if (requisition->width < availWidth) + // 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. + + DBG_OBJ_MSGF ("resize", 1, "before considering OOF widgets: %d * %d + %d", + requisition->width, requisition->ascent, requisition->descent); + + if (outOfFlowMgr) { + int oofWidth, oofHeight; + outOfFlowMgr->getSize (&oofWidth, &oofHeight); + requisition->width = misc::max (requisition->width, oofWidth); + if (oofHeight > requisition->ascent + requisition->descent) + requisition->descent = oofHeight - requisition->ascent; + } + + DBG_OBJ_MSGF ("resize", 1, "before considering availWidth: %d * %d + %d", + requisition->width, requisition->ascent, requisition->descent); + + if (requisition->width < availWidth) { requisition->width = availWidth; + DBG_OBJ_MSGF ("resize", 1, "adjusting to availWidth => %d", + requisition->width); + } - PRINTF ("[%p] SIZE_REQUEST: %d x %d + %d\n", this, requisition->width, - requisition->ascent, requisition->descent); + DBG_OBJ_MSGF ("resize", 1, "=> %d * %d + %d", + requisition->width, requisition->ascent, requisition->descent); + + DBG_OBJ_MSG_END (); } /** @@ -375,7 +409,7 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) */ void Textblock::getWordExtremes (Word *word, core::Extremes *extremes) { - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { if (word->content.widget->usesHints ()) word->content.widget->getExtremes (extremes); else { @@ -405,7 +439,8 @@ void Textblock::getWordExtremes (Word *word, core::Extremes *extremes) void Textblock::getExtremesImpl (core::Extremes *extremes) { - PRINTF ("[%p] GET_EXTREMES ...\n", this); + DBG_OBJ_MSG ("resize", 0, "<b>getExtremesImpl</b>"); + DBG_OBJ_MSG_START (); fillParagraphs (); @@ -423,8 +458,22 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) extremes->minWidth += diff; extremes->maxWidth += diff; - PRINTF ("[%p] GET_EXTREMES => %d / %d\n", - this, extremes->minWidth, extremes->maxWidth); + if (outOfFlowMgr) { + int oofMinWidth, oofMaxWidth; + outOfFlowMgr->getExtremes (&oofMinWidth, &oofMaxWidth); + + DBG_OBJ_MSGF ("resize", 1, "extremes: %d / %d, corrected: %d / %d", + extremes->minWidth, extremes->maxWidth, + oofMinWidth, oofMaxWidth); + + extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth); + extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + + DBG_OBJ_MSG_END (); } @@ -436,6 +485,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) showMissingLines (); + if(outOfFlowMgr) + outOfFlowMgr->sizeAllocateStart (allocation); + int lineIndex, wordIndex; Line *line; Word *word; @@ -449,7 +501,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) { line = lines->getRef (lineIndex); - xCursor = lineXOffsetWidget (line); + xCursor = line->offsetCompleteWidget; for (wordIndex = line->firstWord; wordIndex <= line->lastWord; wordIndex++) { @@ -459,7 +511,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) redrawY = misc::min (redrawY, lineYOffsetWidget (line)); } - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { /** \todo Justification within the line is done here. */ childAllocation.x = xCursor + allocation->x; /* align=top: @@ -473,6 +525,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) lineYOffsetCanvasAllocation (line, allocation) + (line->boxAscent - word->size.ascent) - word->content.widget->getStyle()->margin.top; + childAllocation.width = word->size.width; childAllocation.ascent = word->size.ascent + word->content.widget->getStyle()->margin.top; @@ -531,11 +584,17 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) } } + if(outOfFlowMgr) + outOfFlowMgr->sizeAllocateEnd (); + for (int i = 0; i < anchors->size(); i++) { Anchor *anchor = anchors->getRef(i); int y; - if (anchor->wordIndex >= words->size()) { + if (anchor->wordIndex >= words->size() || + // Also regard not-yet-existing lines. + lines->size () <= 0 || + anchor->wordIndex > lines->getLastRef()->lastWord) { y = allocation->y + allocation->ascent + allocation->descent; } else { Line *line = lines->getRef(findLineOfWord (anchor->wordIndex)); @@ -561,47 +620,122 @@ void Textblock::resizeDrawImpl () void Textblock::markSizeChange (int ref) { - PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", this, ref, wrapRefLines); + DBG_OBJ_MSGF ("resize", 0, "<b>markSizeChange</b> (%d)", ref); + DBG_OBJ_MSG_START (); - /* By the way: ref == -1 may have two different causes: (i) flush() - calls "queueResize (-1, true)", when no rewrapping is necessary; - and (ii) a word may have parentRef == -1 , when it is not yet - added to a line. In the latter case, nothing has to be done - now, but addLine(...) will do everything necessary. */ - if (ref != -1) { - if (wrapRefLines == -1) - wrapRefLines = ref; - else - wrapRefLines = misc::min (wrapRefLines, ref); - } + if (OutOfFlowMgr::isRefOutOfFlow (ref)) { + assert (outOfFlowMgr != NULL); + outOfFlowMgr->markSizeChange (ref); + } else { + PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", + this, ref, wrapRefLines); + + /* By the way: ref == -1 may have two different causes: (i) flush() + calls "queueResize (-1, true)", when no rewrapping is necessary; + and (ii) a word may have parentRef == -1 , when it is not yet + added to a line. In the latter case, nothing has to be done + now, but addLine(...) will do everything necessary. */ + if (ref != -1) { + if (wrapRefLines == -1) + wrapRefLines = OutOfFlowMgr::getLineNoFromRef (ref); + else + wrapRefLines = misc::min (wrapRefLines, + OutOfFlowMgr::getLineNoFromRef (ref)); + } - PRINTF (" ... => %d\n", wrapRefLine); + DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines); + + // It seems that sometimes (even without floats) the lines + // structure is changed, so that wrapRefLines may refers to a + // line which does not exist anymore. Should be examined + // again. Until then, setting wrapRefLines to the same value is + // a workaround. + markExtremesChange (ref); + } - // It seems that sometimes the lines structure is changed, so that - // wrapRefLines may refers to a line which does not exist - // anymore. Should be examined again. Until then, setting - // wrapRefLines to the same value is a workaround. - markExtremesChange (ref); + DBG_OBJ_MSG_END (); } void Textblock::markExtremesChange (int ref) { - PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n", - this, ref, wrapRefParagraphs); - - /* By the way: ref == -1 may have two different causes: (i) flush() - calls "queueResize (-1, true)", when no rewrapping is necessary; - and (ii) a word may have parentRef == -1 , when it is not yet - added to a line. In the latter case, nothing has to be done - now, but addLine(...) will do everything necessary. */ - if (ref != -1) { - if (wrapRefParagraphs == -1) - wrapRefParagraphs = ref; - else - wrapRefParagraphs = misc::min (wrapRefParagraphs, ref); + DBG_OBJ_MSGF ("resize", 1, "<b>markExtremesChange</b> (%d)", ref); + DBG_OBJ_MSG_START (); + + if (OutOfFlowMgr::isRefOutOfFlow (ref)) { + assert (outOfFlowMgr != NULL); + outOfFlowMgr->markExtremesChange (ref); + } else { + PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n", + this, ref, wrapRefParagraphs); + + /* By the way: ref == -1 may have two different causes: (i) flush() + calls "queueResize (-1, true)", when no rewrapping is necessary; + and (ii) a word may have parentRef == -1 , when it is not yet + added to a line. In the latter case, nothing has to be done + now, but addLine(...) will do everything necessary. */ + if (ref != -1) { + if (wrapRefParagraphs == -1) + wrapRefParagraphs = OutOfFlowMgr::getLineNoFromRef (ref); + else + wrapRefParagraphs = + misc::min (wrapRefParagraphs, + OutOfFlowMgr::getLineNoFromRef (ref)); + } + + DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs); } - PRINTF (" ... => %d\n", wrapRefParagraphs); + DBG_OBJ_MSG_END (); +} + +void Textblock::notifySetAsTopLevel() +{ + PRINTF ("%p becomes toplevel\n", this); + containingBlock = this; + PRINTF ("-> %p is its own containing block\n", this); +} + +bool Textblock::isContainingBlock (Widget *widget) +{ + return + // Of course, only textblocks are considered as containing + // blocks. + widget->instanceOf (Textblock::CLASS_ID) && + // The second condition: that this block is "out of flow", in a + // wider sense. + (// The toplevel widget is "out of flow", since there is no + // parent, and so no context. + widget->getParent() == NULL || + // A similar reasoning applies to a widget with another parent + // than a textblock (typical example: a table cell (this is + // also a text block) within a table widget). + !widget->getParent()->instanceOf (Textblock::CLASS_ID) || + // Finally, "out of flow" in a narrower sense: floats and + // absolute positions. + OutOfFlowMgr::isWidgetOutOfFlow (widget)); +} + +void Textblock::notifySetParent () +{ + PRINTF ("%p becomes a child of %p\n", this, getParent()); + + // Search for containing Box. + containingBlock = NULL; + + for (Widget *widget = this; widget != NULL && containingBlock == NULL; + widget = widget->getParent()) + if (isContainingBlock (widget)) { + containingBlock = (Textblock*)widget; + + if (containingBlock == this) { + PRINTF ("-> %p is its own containing block\n", this); + } else { + PRINTF ("-> %p becomes containing block of %p\n", + containingBlock, this); + } + } + + assert (containingBlock != NULL); } void Textblock::setWidth (int width) @@ -609,43 +743,43 @@ void Textblock::setWidth (int width) /* If limitTextWidth is set to YES, a queueResize() may also be * necessary. */ if (availWidth != width || limitTextWidth) { - //DEBUG_MSG(DEBUG_REWRAP_LEVEL, - // "setWidth: Calling queueResize, " - // "in page with %d word(s)\n", - // words->size()); + DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width); + DBG_OBJ_MSG_START (); availWidth = width; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; redrawY = 0; + + DBG_OBJ_MSG_END (); } } void Textblock::setAscent (int ascent) { if (availAscent != ascent) { - //DEBUG_MSG(DEBUG_REWRAP_LEVEL, - // "setAscent: Calling queueResize, " - // "in page with %d word(s)\n", - // words->size()); + DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent); + DBG_OBJ_MSG_START (); availAscent = ascent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; + + DBG_OBJ_MSG_END (); } } void Textblock::setDescent (int descent) { if (availDescent != descent) { - //DEBUG_MSG(DEBUG_REWRAP_LEVEL, - // "setDescent: Calling queueResize, " - // "in page with %d word(s)\n", - // words->size()); + DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent); + DBG_OBJ_MSG_START (); availDescent = descent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; + + DBG_OBJ_MSG_END (); } } @@ -753,7 +887,8 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, wordIndex = words->size () - 1; charPos = core::SelectionState::END_OF_WORD; } else { - Line *line = lines->getRef (findLineIndex (event->yWidget)); + Line *line = + lines->getRef (findLineIndexWhenAllocated (event->yWidget)); // Pointer within the break space? if (event->yWidget > @@ -761,11 +896,11 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, // Choose this break. wordIndex = line->lastWord; charPos = core::SelectionState::END_OF_WORD; - } else if (event->xWidget < lineXOffsetWidget (line)) { + } else if (event->xWidget < line->offsetCompleteWidget) { // Left of the first word in the line. wordIndex = line->firstWord; } else { - int nextWordStartX = lineXOffsetWidget (line); + int nextWordStartX = line->offsetCompleteWidget; for (wordIndex = line->firstWord; wordIndex <= line->lastWord; @@ -860,8 +995,9 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, } } } - it = new TextblockIterator (this, core::Content::SELECTION_CONTENT, - wordIndex); + + it = new TextblockIterator (this, core::Content::maskForSelection (true), + false, wordIndex); r = selectionHandleEvent (eventType, it, charPos, link, event); it->unref (); return r; @@ -894,7 +1030,25 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size) availDescent = this->availDescent; if (widget->usesHints ()) { - widget->setWidth (availWidth); + // 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); + + PRINTF ("setWidth (%d) for the %s %p\n", + corrAvailWidth, widget->getClassName(), widget); + widget->setWidth (corrAvailWidth); widget->setAscent (availAscent); widget->setDescent (availDescent); widget->sizeRequest (size); @@ -1245,7 +1399,7 @@ void Textblock::drawSpace(int wordIndex, core::View *view, */ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) { - int xWidget = lineXOffsetWidget(line); + int xWidget = line->offsetCompleteWidget; int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; for (int wordIndex = line->firstWord; @@ -1256,10 +1410,10 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) if (xWidget + wordSize + word->hyphenWidth + word->effSpace >= area->x) { if (word->content.type == core::Content::TEXT || - word->content.type == core::Content::WIDGET) { + word->content.type == core::Content::WIDGET_IN_FLOW) { if (word->size.width > 0) { - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { core::Widget *child = word->content.widget; core::Rectangle childArea; @@ -1303,10 +1457,36 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) } /** - * Find the first line index that includes y, relative to top of widget. + * Find the first line index that includes y, which is given in widget + * coordinates. */ int Textblock::findLineIndex (int y) { + return wasAllocated () ? + findLineIndexWhenAllocated (y) : findLineIndexWhenNotAllocated (y); +} + +int Textblock::findLineIndexWhenNotAllocated (int y) +{ + if (lines->size() == 0) + return -1; + else + return findLineIndex (y, + lines->getRef(0)->boxAscent + verticalOffset + + getStyle()->boxOffsetY()); +} + +int Textblock::findLineIndexWhenAllocated (int y) +{ + assert (wasAllocated ()); + return findLineIndex (y, allocation.ascent); +} + +int Textblock::findLineIndex (int y, int ascent) +{ + core::Allocation alloc; + alloc.ascent = ascent; // More is not needed. + int maxIndex = lines->size () - 1; int step, index, low = 0; @@ -1314,12 +1494,12 @@ int Textblock::findLineIndex (int y) while ( step > 1 ) { index = low + step; if (index <= maxIndex && - lineYOffsetWidgetI (index) <= y) + lineYOffsetWidgetIAllocation (index, &alloc) <= y) low = index; step = (step + 1) >> 1; } - if (low < maxIndex && lineYOffsetWidgetI (low + 1) <= y) + if (low < maxIndex && lineYOffsetWidgetIAllocation (low + 1, &alloc) <= y) low++; /* @@ -1340,12 +1520,13 @@ int Textblock::findLineIndex (int y) */ int Textblock::findLineOfWord (int wordIndex) { - int high = lines->size () - 1, index, low = 0; - - // TODO regard also not-yet-existing lines? - if (wordIndex < 0 || wordIndex >= words->size ()) + if (wordIndex < 0 || wordIndex >= words->size () || + // Also regard not-yet-existing lines. + lines->size () <= 0 || wordIndex > lines->getLastRef()->lastWord) return -1; + int high = lines->size () - 1, index, low = 0; + while (true) { index = (low + high) / 2; if (wordIndex >= lines->getRef(index)->firstWord) { @@ -1396,14 +1577,14 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) *inSpace = false; - if ((lineIndex = findLineIndex (y)) >= lines->size ()) + if ((lineIndex = findLineIndexWhenAllocated (y)) >= lines->size ()) return NULL; line = lines->getRef (lineIndex); yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; if (yWidgetBase + line->boxDescent <= y) return NULL; - xCursor = lineXOffsetWidget (line); + xCursor = line->offsetCompleteWidget; for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { word = words->getRef (wordIndex); lastXCursor = xCursor; @@ -1438,9 +1619,17 @@ void Textblock::draw (core::View *view, core::Rectangle *area) int lineIndex; Line *line; - drawWidgetBox (view, area, false); + // Instead of drawWidgetBox, use drawBox to include verticalOffset. + if (getParent() == NULL) { + // The toplevel (parent == NULL) widget is a special case, which + // we leave to drawWidgetBox; verticalOffset will here always 0. + assert (verticalOffset == 0); + drawWidgetBox (view, area, false); + } else + drawBox (view, getStyle(), area, 0, verticalOffset, allocation.width, + getHeight() - verticalOffset, false); - lineIndex = findLineIndex (area->y); + lineIndex = findLineIndexWhenAllocated (area->y); for (; lineIndex < lines->size (); lineIndex++) { line = lines->getRef (lineIndex); @@ -1449,6 +1638,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area) drawLine (line, view, area); } + + if(outOfFlowMgr) + outOfFlowMgr->draw(view, area); } /** @@ -1458,6 +1650,7 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent, short flags, core::style::Style *style) { words->increase (); + DBG_OBJ_SET_NUM ("words.size", words->size ()); int wordNo = words->size () - 1; initWord (wordNo); fillWord (wordNo, width, ascent, descent, flags, style); @@ -1936,6 +2129,16 @@ void Textblock::addText0 (const char *text, size_t len, short flags, word->content.type = core::Content::TEXT; word->content.text = layout->textZone->strndup(text, len); + DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", "TEXT"); + DBG_OBJ_ARRATTRSET_STR ("words", words->size () - 1, + "text/widget/breakSpace", word->content.text); + + // The following debug message may be useful to identify the + // different textblocks. + + //if (words->size() == 1) + // printf ("[%p] first word: '%s'\n", this, text); + processWord (words->size () - 1); } @@ -1944,28 +2147,59 @@ void Textblock::addText0 (const char *text, size_t len, short flags, */ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) { - Word *word; - core::Requisition size; - /* We first assign -1 as parent_ref, since the call of widget->size_request * will otherwise let this Textblock be rewrapped from the beginning. * (parent_ref is actually undefined, but likely has the value 0.) At the, * end of this function, the correct value is assigned. */ widget->parentRef = -1; - PRINTF ("%p becomes child of %p\n", widget, this); - - widget->setParent (this); widget->setStyle (style); - calcWidgetSize (widget, &size); - word = addWord (size.width, size.ascent, size.descent, 0, style); + PRINTF ("adding the %s %p to %p (word %d) ...\n", + widget->getClassName(), widget, this, words->size()); - word->content.type = core::Content::WIDGET; - word->content.widget = widget; + if (containingBlock->outOfFlowMgr == NULL) { + containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock); + DBG_OBJ_ASSOC (containingBlock, containingBlock->outOfFlowMgr); + } + + if (OutOfFlowMgr::isWidgetHandledByOOFM (widget)) { + PRINTF (" -> out of flow.\n"); + + widget->setParent (containingBlock); + widget->setGenerator (this); + containingBlock->outOfFlowMgr->addWidgetOOF (widget, this, + words->size ()); + Word *word = addWord (0, 0, 0, 0, style); + word->content.type = core::Content::WIDGET_OOF_REF; + word->content.widget = widget; + word->style = style; + + DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", + "WIDGET_OOF_REF"); + DBG_OBJ_ARRATTRSET_PTR ("words", words->size () - 1, + "text/widget/breakSpace", word->content.widget); + } else { + PRINTF (" -> within flow.\n"); - //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1, - // word->content.widget); + widget->setParent (this); + + // TODO Replace (perhaps) later "textblock" by "OOF aware widget". + if (widget->instanceOf (Textblock::CLASS_ID)) + containingBlock->outOfFlowMgr->addWidgetInFlow ((Textblock*)widget, + this, words->size ()); + + core::Requisition size; + calcWidgetSize (widget, &size); + Word *word = addWord (size.width, size.ascent, size.descent, 0, style); + word->content.type = core::Content::WIDGET_IN_FLOW; + word->content.widget = widget; + + DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", + "WIDGET_IN_FLOW"); + DBG_OBJ_ARRATTRSET_PTR ("words", words->size () - 1, + "text/widget/breakSpace", word->content.widget); + } processWord (words->size () - 1); //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref", @@ -2067,8 +2301,10 @@ void Textblock::fillSpace (int wordNo, core::style::Style *style) // TODO: This line does not work: addBreakOption (word, style); - // Do not override a previously set break penalty. - if (!word->content.space) { + if (// Do not override a previously set break penalty: + !word->content.space && + // OOF references are considered specially, and must not have a space: + word->content.type != core::Content::WIDGET_OOF_REF) { setBreakOption (word, style, 0, 0, false); word->content.space = true; @@ -2140,7 +2376,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) /* A break may not be the first word of a page, or directly after the bullet/number (which is the first word) in a list item. (See - also comment in Dw_page_size_request.) */ + also comment in sizeRequest.) */ if (words->size () == 0 || (hasListitemValue && words->size () == 1)) { /* This is a bit hackish: If a break is added as the @@ -2149,23 +2385,23 @@ void Textblock::addParbreak (int space, core::style::Style *style) a widget is used as a text box (lists, blockquotes, list items etc) -- then we simply adjust the break before, in a way that the space is in any case visible. */ - Widget *widget; - - /* Find the widget where to adjust the breakSpace. */ - for (widget = this; - widget->getParent() && - widget->getParent()->instanceOf (Textblock::CLASS_ID); + /* Find the widget where to adjust the breakSpace. (Only + consider normal flow, no floats etc.) */ + for (Widget *widget = this; + widget->getParent() != NULL && + widget->getParent()->instanceOf (Textblock::CLASS_ID) && + !OutOfFlowMgr::isRefOutOfFlow (widget->parentRef); widget = widget->getParent ()) { Textblock *textblock2 = (Textblock*)widget->getParent (); int index = textblock2->hasListitemValue ? 1 : 0; bool isfirst = (textblock2->words->getRef(index)->content.type - == core::Content::WIDGET + == core::Content::WIDGET_IN_FLOW && textblock2->words->getRef(index)->content.widget == widget); if (!isfirst) { - /* The page we searched for has been found. */ + /* The text block we searched for has been found. */ Word *word2; - int lineno = widget->parentRef; + int lineno = OutOfFlowMgr::getLineNoFromRef (widget->parentRef); if (lineno > 0 && (word2 = @@ -2174,7 +2410,8 @@ void Textblock::addParbreak (int space, core::style::Style *style) word2->content.type == core::Content::BREAK) { if (word2->content.breakSpace < space) { word2->content.breakSpace = space; - textblock2->queueResize (lineno, false); + textblock2->queueResize + (OutOfFlowMgr::createRefNormalFlow (lineno), false); textblock2->mustQueueResize = false; } } @@ -2182,6 +2419,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) } /* Otherwise continue to examine parents. */ } + /* Return in any case. */ return; } @@ -2205,6 +2443,11 @@ void Textblock::addParbreak (int space, core::style::Style *style) word->content.type = core::Content::BREAK; word->badnessAndPenalty.setPenalty (PENALTY_FORCE_BREAK); word->content.breakSpace = space; + + DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", "BREAK"); + DBG_OBJ_ARRATTRSET_NUM ("words", words->size () - 1, + "text/widget/breakSpace", word->content.breakSpace); + processWord (words->size () - 1); } @@ -2230,10 +2473,14 @@ void Textblock::addLinebreak (core::style::Style *style) word->content.type = core::Content::BREAK; word->badnessAndPenalty.setPenalty (PENALTY_FORCE_BREAK); word->content.breakSpace = 0; + + DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", "BREAK"); + DBG_OBJ_ARRATTRSET_NUM ("words", words->size () - 1, + "text/widget/breakSpace", word->content.breakSpace); + processWord (words->size () - 1); } - /** * \brief Search recursively through widget. * @@ -2242,6 +2489,10 @@ void Textblock::addLinebreak (core::style::Style *style) */ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) { + //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n", + // 3 * level, "", getClassName (), this, allocation.x, allocation.y, + // allocation.width, allocation.ascent, allocation.descent); + int lineIndex, wordIndex; Line *line; @@ -2252,7 +2503,15 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) return NULL; } - lineIndex = findLineIndex (y - allocation.y); + // First, search for widgets out of flow, notably floats, since + // there are cases where they overlap child textblocks. Should + // later be refined using z-index. + Widget *oofWidget = + outOfFlowMgr ? outOfFlowMgr->getWidgetAtPoint (x, y, level) : NULL; + if (oofWidget) + return oofWidget; + + lineIndex = findLineIndexWhenAllocated (y - allocation.y); if (lineIndex < 0 || lineIndex >= lines->size ()) { return this; @@ -2263,7 +2522,7 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { Word *word = words->getRef (wordIndex); - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { core::Widget * childAtPoint; childAtPoint = word->content.widget->getWidgetAtPoint (x, y, level + 1); @@ -2303,12 +2562,14 @@ void Textblock::handOverBreak (core::style::Style *style) */ void Textblock::flush () { - PRINTF ("[%p] FLUSH => %s (parentRef = %d)\n", - this, mustQueueResize ? "true" : "false", parentRef); - if (mustQueueResize) { + DBG_OBJ_MSG ("resize", 0, "<b>flush</b> (mustQueueResize set)"); + DBG_OBJ_MSG_START (); + queueResize (-1, true); mustQueueResize = false; + + DBG_OBJ_MSG_END (); } } @@ -2344,7 +2605,7 @@ void Textblock::changeLinkColor (int link, int newColor) old_style->unref(); break; } - case core::Content::WIDGET: + case core::Content::WIDGET_IN_FLOW: { core::Widget *widget = word->content.widget; styleAttrs = *widget->getStyle(); styleAttrs.color = core::style::Color::create (layout, @@ -2396,4 +2657,315 @@ void Textblock::queueDrawRange (int index1, int index2) } } +void Textblock::setVerticalOffset (int verticalOffset) +{ + if (this->verticalOffset != verticalOffset) { + this->verticalOffset = verticalOffset; + mustQueueResize = true; + queueDraw (); // Could perhaps be optimized. + } +} + +/** + * Called by dw::OutOfFlowMgr when the border has changed due to a + * float (or some floats). + * + * "y", which given in widget coordinates, denotes the minimal + * position (when more than one float caused this), "vloat" the + * floating widget belonging to "y". + */ +void Textblock::borderChanged (int y, Widget *vloat) +{ + DBG_OBJ_MSGF ("resize", 0, "<b>borderChanged</b> (%d, %p)", y, vloat); + DBG_OBJ_MSG_START (); + + int lineIndex = findLineIndex (y); + DBG_OBJ_MSGF ("resize", 1, "Line index: %d (of %d).", + lineIndex, lines->size ()); + + // Nothing to do at all, when lineIndex >= lines->size (), + // i. e. the change is below the bottom of this widget. + if (lineIndex < lines->size ()) { + int wrapLineIndex; + if (lineIndex < 0) + // Rewrap all. + wrapLineIndex = 0; + else + wrapLineIndex = lineIndex; + + DBG_OBJ_MSGF ("resize", 1, "Rewrapping from line %d (of %d).", + wrapLineIndex, lines->size ()); + + if (vloat->getGenerator() == this) { + bool found = false; + // Sometimes, the respective word is not yet part of a + // line. Nothing to do, but because of the assertion below + // (and also for performace reasons) this should be + // considered. TODO: Integrate this below. + for (int wordIndex = + lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0; + !found && wordIndex < words->size(); wordIndex++) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF && + word->content.widget == vloat) + found = true; + } + + // We search for the line of the float reference. There are + // two cases when this is not the line corresponsing to y: + // + // (1) When the float was moved down, due to collisions with + // other floats: in this case, the line number gets + // smaller (since the float reference is before). + // + // (2) In some cases, the line number may become larger, due + // to the per-line optimization of the words: initially, + // lines->size() - 1 is assigned, but it may happen that + // the float reference is put into another line. + // + // Only in the first case, a correction is neccessary, but a + // test for the second case is useful. (TODO: I've forgotten + // why a correction is neccessary.) + // + // Searched is done in the following order: + // + // - wrapLineIndex, + // - wrapLineIndex - 1, + // - wrapLineIndex + 1, + // - wrapLineIndex - 2, + // - wrapLineIndex + 2, + // + // etc. until either the float reference has been found or + // all lines have been searched (the latter triggers an + // abortion). + + bool exceedsBeginning = false, exceedsEnd = false; + for (int i = 0; !found; i++) { + bool exceeds; + int lineIndex2; + if (i % 2 == 0) { + // even: +0, +1, +2, ... + lineIndex2 = wrapLineIndex + i / 2; + if (i > 0) + exceeds = exceedsEnd = lineIndex2 >= lines->size (); + else + exceeds = exceedsEnd = false; + } else { + // odd: -1, -2, ... + lineIndex2 = wrapLineIndex - (i + 1) / 2; + exceeds = exceedsBeginning = lineIndex2 < 0; + } + + if (exceedsBeginning && exceedsEnd) + break; + + if (!exceeds) { + Line *line = lines->getRef (lineIndex2); + for (int wordIndex = line->firstWord; + !found && wordIndex <= line->lastWord; wordIndex++) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF && + word->content.widget == vloat) { + found = true; + // Correct only by smaller values (case (1) above): + wrapLineIndex = misc::min (wrapLineIndex, lineIndex2); + } + } + } + } + + if (!found) + printBorderChangedErrorAndAbort (y, vloat, wrapLineIndex); + } + + DBG_OBJ_MSGF ("resize", 1, "Corrected to line %d.", wrapLineIndex); + + queueResize (OutOfFlowMgr::createRefNormalFlow (wrapLineIndex), true); + + // Notice that the line no. wrapLineIndex may not exist yet. + if (wrapLineIndex == 0) + lastWordDrawn = misc::min (lastWordDrawn, -1); + else + lastWordDrawn = misc::min (lastWordDrawn, + lines->getRef(wrapLineIndex - 1)->lastWord); + } + + DBG_OBJ_MSG_END (); +} + +void Textblock::printBorderChangedErrorAndAbort (int y, Widget *vloat, + int wrapLineIndex) +{ + // TODO Should be adjusted to recent changes in borderChanged(). + + printf ("*** This call failed! ***\n"); + printf ("[%p] borderChanged (%d, %p (%s))\n", + this, y, vloat, vloat->getClassName()); + printf (" Initial wrapLineIndex = line %d (of %d lines). " + "Has to be corrected.\n", wrapLineIndex, lines->size ()); + + printf (" search non-existing line (from %d to %d):\n", + lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0, + words->size() - 1); + for (int wordIndex = + lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0; + wordIndex < words->size(); wordIndex++) { + printf (" word %d: ", wordIndex); + printWordShort (words->getRef (wordIndex)); + printf ("\n"); + } + + for (int lineIndex2 = wrapLineIndex; lineIndex2 >= 0; + lineIndex2--) { + Line *line = lines->getRef (lineIndex2); + printf (" searching existing line %d (from %d to %d):\n", + lineIndex2, line->firstWord, line->lastWord); + for (int wordIndex = line->firstWord; + wordIndex <= line->lastWord; wordIndex++) { + printf (" word %d: ", wordIndex); + printWordShort (words->getRef (wordIndex)); + printf ("\n"); + } + } + + bool found2 = false; + for (int lineIndex2 = wrapLineIndex + 1; + !found2 && lineIndex2 < lines->size (); lineIndex2++) { + Line *line = lines->getRef (lineIndex2); + printf (" could have searched existing line %d " + "(from %d to %d):\n", + lineIndex2, line->firstWord, line->lastWord); + for (int wordIndex = line->firstWord; + !found2 && wordIndex <= line->lastWord; wordIndex++) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF && + word->content.widget == vloat) { + found2 = true; + printf (" word %d: ", wordIndex); + printWordShort (word); + printf ("\n"); + } + } + } + + lout::misc::assertNotReached (); +} + +Textblock *Textblock::getTextblockForLine (Line *line) +{ + return getTextblockForLine (line->firstWord, line->lastWord); +} + +Textblock *Textblock::getTextblockForLine (int lineNo) +{ + int firstWord = lineNo == 0 ? 0 :lines->getRef(lineNo - 1)->lastWord + 1; + int lastWord = lineNo < lines->size() ? + lines->getRef(lineNo)->lastWord : words->size() - 1; + return getTextblockForLine (firstWord, lastWord); +} + +Textblock *Textblock::getTextblockForLine (int firstWord, int lastWord) +{ + if (firstWord < words->size ()) { + //printf ("[%p] GET_TEXTBLOCK_FOR_LINE (%d, %d)\n", + // this, firstWord, lastWord); + + // A textblock is always between two line breaks, and so the + // first word of the line. + Word *word = words->getRef (firstWord); + + if (word->content.type == core::Content::WIDGET_IN_FLOW && + word->content.widget->instanceOf (Textblock::CLASS_ID)) { + //printf (" word %d: ", firstWord); + //printWordShort (word); + //printf ("\n"); + + return (Textblock*)word->content.widget; + } + } + + //printf (" nothing\n"); + return NULL; +} + +/** + * Includes margin, border, and padding. + */ +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 (); + + int result; + + if (lineNo == 0) { + result = verticalOffset + getStyle()->boxOffsetY(); + DBG_OBJ_MSGF ("line.yoffset", 1, "first line: %d + %d = %d", + verticalOffset, getStyle()->boxOffsetY(), result); + } else { + Line *prevLine = lines->getRef (lineNo - 1); + result = verticalOffset + getStyle()->boxOffsetY() + + prevLine->top + prevLine->boxAscent + prevLine->boxDescent + + prevLine->breakSpace; + DBG_OBJ_MSGF ("line.yoffset", 1, + "other line: %d + %d + %d + (%d + %d) + %d = %d", + verticalOffset, getStyle()->boxOffsetY(), + prevLine->top, prevLine->boxAscent, prevLine->boxDescent, + prevLine->breakSpace, result); + } + + DBG_OBJ_MSG_END (); + + 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 (); + + int result; + + if (lineNo < lines->size()) { + // An existing line. + + Line *line = lines->getRef (lineNo); + + // This is sometimes called within addLine, so that the + // condition above is true, but line->boxAscent and + // line->boxDescent are not set appropriately. We have to + // accumulate the heights then. + + if (line->finished) { + DBG_OBJ_MSGF ("line.height", 1, + "exists and is finished; height = %d + %d = %d", + line->boxAscent, line->boxDescent, + line->boxAscent + line->boxDescent); + result = line->boxAscent + line->boxDescent; + } else { + DBG_OBJ_MSG ("line.height", 1, "exist but is not finished"); + result = misc::max (1, newLineAscent + newLineDescent); + } + } else if (lineNo == lines->size()) { + // The line to be constructed: some words exist, but not the + // line. Accumulate the word heights. + + // Old comment: Furthermore, this is in some cases incomplete: + // see doc/dw-out-of-flow.doc. -- Still the case? + + DBG_OBJ_MSG ("line.height", 1, "does not exist"); + result = misc::max (1, newLineAscent + newLineDescent); + } else + result = 1; + + DBG_OBJ_MSGF ("line.height", 0, "result = %d", result); + DBG_OBJ_MSG_END (); + + return result; +} + } // namespace dw diff --git a/dw/textblock.hh b/dw/textblock.hh index b85937ba..e29b93d5 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -4,13 +4,16 @@ #include <limits.h> #include "core.hh" +#include "outofflowmgr.hh" #include "../lout/misc.hh" -// These were used when improved line breaking and hyphenation were -// implemented. Should be cleaned up; perhaps reactivate RTFL again. +// These were used when improved line breaking and hyphenation were implemented. +// Should be, bit by bit, replaced by RTFL (see ../lout/debug.hh). #define PRINTF(fmt, ...) #define PUTCHAR(ch) +//#define DEBUG + namespace dw { /** @@ -18,10 +21,11 @@ namespace dw { * of paragraphs. * * <div style="border: 2px solid #ffff00; margin-top: 0.5em; - * margin-bottom: 0.5em; padding: 0.5em 1em; - * background-color: #ffffe0"><b>Info:</b> The recent changes (line - * breaking and hyphenation) have not yet been incorporated into this - * documentation. See \ref dw-line-breaking.</div> + * margin-bottom: 0.5em; padding: 0.5em 1em; background-color: + * #ffffe0"><b>Info:</b> The recent changes (line breaking and + * hyphenation on one hand, floats on the other hand) have not yet + * been incorporated into this documentation. See \ref + * dw-line-breaking and \ref dw-out-of-flow.</div> * * <h3>Signals</h3> * @@ -47,9 +51,9 @@ namespace dw { * * \image html dw-textblock-collapsing-spaces-1-1.png * - * are combined like this: + * are combined like this: * - * \image html dw-textblock-collapsing-spaces-1-2.png + * \image html dw-textblock-collapsing-spaces-1-2.png * * <li> a) If one paragraph is the first paragraph within another, the upper * space of these paragraphs collapse. b) The analogue is the case for the @@ -73,9 +77,9 @@ namespace dw { * automatically. See the code of dw::Textblock::addParBreak. * * <li> To collapse spaces according to rule 2b, - * dw::Textblock::addParBreak::handOverBreak must be called for - * the \em inner widget. The HTML parser does this in - * Html_eventually_pop_dw. + * dw::Textblock::addParBreak::handOverBreak must be called for + * the \em inner widget. The HTML parser does this in + * Html_eventually_pop_dw. * </ul> * * @@ -236,6 +240,9 @@ private: static const char *hyphenDrawChar; + Textblock *containingBlock; + OutOfFlowMgr *outOfFlowMgr; + protected: /** * \brief Implementation used for words. @@ -308,10 +315,21 @@ protected: int firstWord; /* first word's index in word vector */ int lastWord; /* last word's index in word vector */ - /* "top" is always relative to the top of the first line, i.e. - * page->lines[0].top is always 0. */ - int top, boxAscent, boxDescent, contentAscent, contentDescent, - breakSpace, leftOffset; + + int top; /* "top" is always relative to the top + of the first line, i.e. + page->lines[0].top is always 0. */ + int boxAscent; /* Maximum of all ascents of the words + in this line. This is the actual + ascent of the line. */ + int boxDescent; /* Maximum of all decents of the words + in this line. This is the actual + descent of the line. */ + int contentAscent; /* ??? */ + int contentDescent; /* ??? */ + int breakSpace; /* Space between this line and the next one. */ + int leftOffset; /* ??? */ + int offsetCompleteWidget; /* ??? */ /* This is similar to descent, but includes the bottom margins of the * widgets within this line. */ @@ -323,6 +341,11 @@ protected: * changed line breaking), the values were accumulated up to the * last line, not this line.*/ int maxLineWidth; + + /* Set to false at the beginning of addLine(), and to true at + * the end. Should be checked by some methods which are called + * by addLine(). */ + bool finished; }; struct Word @@ -409,13 +432,14 @@ protected: class TextblockIterator: public core::Iterator { private: + bool oofm; int index; public: TextblockIterator (Textblock *textblock, core::Content::Type mask, bool atEnd); TextblockIterator (Textblock *textblock, core::Content::Type mask, - int index); + bool oofm, int index); lout::object::Object *clone(); int compareTo(lout::object::Comparable *other); @@ -425,6 +449,7 @@ protected: 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); + void print (); }; friend class TextblockIterator; @@ -479,7 +504,31 @@ protected: /* These values are set by set_... */ int availWidth, availAscent, availDescent; - int wrapRefLines, wrapRefParagraphs; /* [0 based] */ + // Additional vertical offset, used for the "clear" attribute. + int verticalOffset; + + int wrapRefLines, wrapRefParagraphs; /* 0-based. Important: Both + are the line numbers, not + the value stored in + parentRef. */ + + // These four values are calculated by containingBlock->outOfFlowMgr + // (when defined; otherwise, they are false, or 0, respectively), for + // the newly constructed line, only when needed: when a new line is + // added, or if something in the line currently constucted has + // changed, e. g. a float has been added. + + bool newLineHasFloatLeft, newLineHasFloatRight; + int newLineLeftBorder, newLineRightBorder; /* As returned by + outOfFlowMgr->get...Border, + or 0, if outOfFlowMgr + is NULL */ + + // Ascent and descent of the newly constructed line, i. e. maximum + // of all words ascent/descent since the end of the last line. Not + // neccessary the ascent and descent of the newly added line, since + // not all words are added to it. + int newLineAscent, newLineDescent; lout::misc::SimpleVector <Line> *lines; lout::misc::SimpleVector <Paragraph> *paragraphs; @@ -492,7 +541,6 @@ protected: int hoverLink; /* The link under the mouse pointer */ - void queueDrawRange (int index1, int index2); void getWordExtremes (Word *word, core::Extremes *extremes); void justifyLine (Line *line, int diff); @@ -500,8 +548,10 @@ protected: void calcWidgetSize (core::Widget *widget, core::Requisition *size); void rewrap (); void fillParagraphs (); + void initNewLine (); void showMissingLines (); void removeTemporaryLines (); + void setVerticalOffset (int verticalOffset); void decorateText (core::View *view, core::style::Style *style, core::style::Color::Shading shading, @@ -520,6 +570,9 @@ protected: int xWidget, int yWidgetBase); void drawLine (Line *line, core::View *view, core::Rectangle *area); int findLineIndex (int y); + int findLineIndexWhenNotAllocated (int y); + int findLineIndexWhenAllocated (int y); + int findLineIndex (int y, int ascent); int findLineOfWord (int wordIndex); int findParagraphOfWord (int wordIndex); Word *findWord (int x, int y, bool *inSpace); @@ -543,25 +596,17 @@ protected: core::Requisition *size, bool isStart, bool isEnd); /** - * \brief Returns the x offset (the indentation plus any offset needed for - * centering or right justification) for the line. - * - * The offset returned is relative to the page *content* (i.e. without - * border etc.). + * Of nested text blocks, only the most inner one must regard the + * borders of floats. */ - inline int lineXOffsetContents (Line *line) + inline bool mustBorderBeRegarded (Line *line) { - return innerPadding + line->leftOffset + - (line == lines->getFirstRef() ? line1OffsetEff : 0); + return getTextblockForLine (line) == NULL; } - /** - * \brief Like lineXOffset, but relative to the allocation (i.e. - * including border etc.). - */ - inline int lineXOffsetWidget (Line *line) + inline bool mustBorderBeRegarded (int lineNo) { - return lineXOffsetContents (line) + getStyle()->boxOffsetX (); + return getTextblockForLine (lineNo) == NULL; } inline int lineYOffsetWidgetAllocation (Line *line, @@ -581,7 +626,7 @@ protected: inline int lineYOffsetCanvasAllocation (Line *line, core::Allocation *allocation) { - return allocation->y + lineYOffsetWidgetAllocation(line, allocation); + return allocation->y + lineYOffsetWidgetAllocation (line, allocation); } /** @@ -597,6 +642,13 @@ protected: return lineYOffsetWidget (lines->getRef (lineIndex)); } + inline int lineYOffsetWidgetIAllocation (int lineIndex, + core::Allocation *allocation) + { + return lineYOffsetWidgetAllocation (lines->getRef (lineIndex), + allocation); + } + inline int lineYOffsetCanvasI (int lineIndex) { return lineYOffsetCanvas (lines->getRef (lineIndex)); @@ -612,6 +664,14 @@ protected: (Word::DIV_CHAR_AT_EOL | Word::PERM_DIV_CHAR)) ? 1 : 0; } + Textblock *getTextblockForLine (Line *line); + Textblock *getTextblockForLine (int lineNo); + Textblock *getTextblockForLine (int firstWord, int lastWord); + void printBorderChangedErrorAndAbort (int y, Widget *vloat, + int wrapLineIndex); + int yOffsetOfPossiblyMissingLine (int lineNo); + int heightOfPossiblyMissingLine (int lineNo); + bool sendSelectionEvent (core::SelectionState::EventType eventType, core::MousePositionEvent *event); @@ -619,11 +679,15 @@ protected: int *maxOfMinWidth, int *sumOfMaxWidth); void processWord (int wordIndex); virtual bool wordWrap (int wordIndex, bool wrapAll); + bool wrapWordInFlow (int wordIndex, bool wrapAll); + void checkPossibleLineHeightChange (int wordIndex); + bool wrapWordOofRef (int wordIndex, bool wrapAll); + void updateBorders (int wordIndex, bool left, bool right); int searchMinBap (int firstWord, int lastWordm, int penaltyIndex, bool correctAtEnd); int considerHyphenation (int firstIndex, int breakPos); bool isHyphenationCandidate (Word *word); - + void handleWordExtremes (int wordIndex); void correctLastWordExtremes (); @@ -645,6 +709,8 @@ protected: void markSizeChange (int ref); void markExtremesChange (int ref); + void notifySetAsTopLevel(); + void notifySetParent(); void setWidth (int width); void setAscent (int ascent); void setDescent (int descent); @@ -664,6 +730,7 @@ protected: core::style::Style *style, int numBreaks, int *breakPos, core::Requisition *wordSize); + static bool isContainingBlock (Widget *widget); public: static int CLASS_ID; @@ -699,8 +766,66 @@ public: void changeLinkColor (int link, int newColor); void changeWordStyle (int from, int to, core::style::Style *style, bool includeFirstSpace, bool includeLastSpace); + + void borderChanged (int y, core::Widget *vloat); + inline int getAvailWidth () { return availWidth; } + inline int getAvailAscent () { return availAscent; } + inline int getAvailDescent () { return availDescent; } }; +#define DBG_SET_WORD(n) \ + D_STMT_START { \ + switch (words->getRef(n)->content.type) { \ + case ::dw::core::Content::TEXT: \ + DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "TEXT"); \ + DBG_OBJ_ARRATTRSET_STR ("words", n, "text/widget/breakSpace", \ + words->getRef(n)->content.text); \ + break; \ + case ::dw::core::Content::WIDGET_IN_FLOW: \ + DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "WIDGET_IN_FLOW"); \ + DBG_OBJ_ARRATTRSET_PTR ("words", n, "text/widget/breakSpace", \ + words->getRef(n)->content.widget); \ + break; \ + case ::dw::core::Content::WIDGET_OOF_REF: \ + DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "WIDGET_OOF_REF"); \ + DBG_OBJ_ARRATTRSET_PTR ("words", n, "text/widget/breakSpace", \ + words->getRef(n)->content.widget); \ + break; \ + case ::dw::core::Content::BREAK: \ + DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "TEXT"); \ + DBG_OBJ_ARRATTRSET_NUM ("words", n, "text/widget/breakSpace", \ + words->getRef(n)->content.breakSpace); \ + break; \ + default: \ + DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "???"); \ + DBG_OBJ_ARRATTRSET_SYM ("words", n, "text/widget/breakSpace", "???"); \ + } \ + } D_STMT_END + +#define DBG_MSG_WORD(aspect, prio, prefix, n, suffix) \ + D_STMT_START { \ + switch (words->getRef(n)->content.type) { \ + case ::dw::core::Content::TEXT: \ + DBG_OBJ_MSGF (aspect, prio, prefix "TEXT / \"%s\"" suffix, \ + words->getRef(n)->content.text); \ + break; \ + case ::dw::core::Content::WIDGET_IN_FLOW: \ + DBG_OBJ_MSGF (aspect, prio, prefix "WIDGET_IN_FLOW / %p" suffix, \ + words->getRef(n)->content.widget); \ + break; \ + case ::dw::core::Content::WIDGET_OOF_REF: \ + DBG_OBJ_MSGF (aspect, prio, prefix "WIDGET_OOF_REF / %p" suffix, \ + words->getRef(n)->content.widget); \ + break; \ + case ::dw::core::Content::BREAK: \ + DBG_OBJ_MSGF (aspect, prio, prefix "BREAK / %d" suffix, \ + words->getRef(n)->content.breakSpace); \ + break; \ + default: \ + DBG_OBJ_MSG (aspect, prio, prefix "??? / ???"); \ + } \ + } D_STMT_END + } // namespace dw #endif // __DW_TEXTBLOCK_HH__ diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc index 7531ecd5..c26b7f6e 100644 --- a/dw/textblock_iterator.cc +++ b/dw/textblock_iterator.cc @@ -35,20 +35,34 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, bool atEnd): core::Iterator (textblock, mask, atEnd) { - index = atEnd ? textblock->words->size () : -1; + if (atEnd) { + if (textblock->outOfFlowMgr) { + oofm = true; + index = textblock->outOfFlowMgr->getNumWidgets(); + } else { + oofm = false; + index = textblock->words->size(); + } + } else { + oofm = false; + index = -1; + } + content.type = atEnd ? core::Content::END : core::Content::START; } Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, core::Content::Type mask, - int index): + bool oofm, int index): core::Iterator (textblock, mask, false) { + this->oofm = oofm; this->index = index; + // TODO To be completely exact, oofm should be considered here. if (index < 0) content.type = core::Content::START; - else if (index >= textblock->words->size ()) + else if (index >= textblock->words->size()) content.type = core::Content::END; else content = textblock->words->getRef(index)->content; @@ -56,12 +70,20 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, object::Object *Textblock::TextblockIterator::clone() { - return new TextblockIterator ((Textblock*)getWidget(), getMask(), index); + return + new TextblockIterator ((Textblock*)getWidget(), getMask(), oofm, index); } int Textblock::TextblockIterator::compareTo(object::Comparable *other) { - return index - ((TextblockIterator*)other)->index; + TextblockIterator *otherTI = (TextblockIterator*)other; + + if (oofm && !otherTI->oofm) + return +1; + else if (!oofm && otherTI->oofm) + return -1; + else + return index - otherTI->index; } bool Textblock::TextblockIterator::next () @@ -71,15 +93,52 @@ bool Textblock::TextblockIterator::next () if (content.type == core::Content::END) return false; + short type; + do { index++; - if (index >= textblock->words->size ()) { - content.type = core::Content::END; - return false; + + if (oofm) { + // Iterating over OOFM. + if (index >= textblock->outOfFlowMgr->getNumWidgets()) { + // End of OOFM list reached. + content.type = core::Content::END; + return false; + } + type = core::Content::WIDGET_OOF_CONT; + } else { + // Iterating over words list. + if (index < textblock->words->size ()) + // Still words left. + type = textblock->words->getRef(index)->content.type; + else { + // End of words list reached. + if (textblock->outOfFlowMgr) { + oofm = true; + index = 0; + if (textblock->outOfFlowMgr->getNumWidgets() > 0) + // Start with OOFM widgets. + type = core::Content::WIDGET_OOF_CONT; + else { + // No OOFM widgets (number is 0). + content.type = core::Content::END; + return false; + } + } else { + // No OOFM widgets (no OOFM agt all). + content.type = core::Content::END; + return false; + } + } } - } while ((textblock->words->getRef(index)->content.type & getMask()) == 0); + } while ((type & getMask()) == 0); + + if (oofm) { + content.type = core::Content::WIDGET_OOF_CONT; + content.widget = textblock->outOfFlowMgr->getWidget (index); + } else + content = textblock->words->getRef(index)->content; - content = textblock->words->getRef(index)->content; return true; } @@ -90,132 +149,188 @@ bool Textblock::TextblockIterator::prev () if (content.type == core::Content::START) return false; + short type; + do { index--; - if (index < 0) { - content.type = core::Content::START; - return false; + + if (oofm) { + // Iterating over OOFM. + if (index >= 0) + // Still widgets left. + type = core::Content::WIDGET_OOF_CONT; + else { + // Beginning of OOFM list reached. Continue with words. + oofm = false; + index = textblock->words->size() - 1; + if (index < 0) { + // There are no words. (Actually, this case should not + // happen: When there are OOF widgets, ther must be OOF + // references, or widgets in flow, which contain + // references. + content.type = core::Content::END; + return false; + } + type = textblock->words->getRef(index)->content.type; + } + } else { + // Iterating over words list. + if (index < 0) { + // Beginning of words list reached. + content.type = core::Content::START; + return false; + } + type = textblock->words->getRef(index)->content.type; } - } while ((textblock->words->getRef(index)->content.type & getMask()) == 0); + } while ((type & getMask()) == 0); + + if (oofm) { + content.type = core::Content::WIDGET_OOF_CONT; + content.type = false; + content.widget = textblock->outOfFlowMgr->getWidget (index); + } else + content = textblock->words->getRef(index)->content; - content = textblock->words->getRef(index)->content; return true; } void Textblock::TextblockIterator::highlight (int start, int end, core::HighlightLayer layer) { - Textblock *textblock = (Textblock*)getWidget(); - int index1 = index, index2 = index; - - int oldStartIndex = textblock->hlStart[layer].index; - int oldStartChar = textblock->hlStart[layer].nChar; - int oldEndIndex = textblock->hlEnd[layer].index; - int oldEndChar = textblock->hlEnd[layer].nChar; - - if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) { - /* nothing is highlighted */ - textblock->hlStart[layer].index = index; - textblock->hlEnd[layer].index = index; - } - - if (textblock->hlStart[layer].index >= index) { - index2 = textblock->hlStart[layer].index; - textblock->hlStart[layer].index = index; - textblock->hlStart[layer].nChar = start; - } - - if (textblock->hlEnd[layer].index <= index) { - index2 = textblock->hlEnd[layer].index; - textblock->hlEnd[layer].index = index; - textblock->hlEnd[layer].nChar = end; + if (!oofm) { + Textblock *textblock = (Textblock*)getWidget(); + int index1 = index, index2 = index; + + int oldStartIndex = textblock->hlStart[layer].index; + int oldStartChar = textblock->hlStart[layer].nChar; + int oldEndIndex = textblock->hlEnd[layer].index; + int oldEndChar = textblock->hlEnd[layer].nChar; + + if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) { + /* nothing is highlighted */ + textblock->hlStart[layer].index = index; + textblock->hlEnd[layer].index = index; + } + + if (textblock->hlStart[layer].index >= index) { + index2 = textblock->hlStart[layer].index; + textblock->hlStart[layer].index = index; + textblock->hlStart[layer].nChar = start; + } + + if (textblock->hlEnd[layer].index <= index) { + index2 = textblock->hlEnd[layer].index; + textblock->hlEnd[layer].index = index; + textblock->hlEnd[layer].nChar = end; + } + + if (oldStartIndex != textblock->hlStart[layer].index || + oldStartChar != textblock->hlStart[layer].nChar || + oldEndIndex != textblock->hlEnd[layer].index || + oldEndChar != textblock->hlEnd[layer].nChar) + textblock->queueDrawRange (index1, index2); } - if (oldStartIndex != textblock->hlStart[layer].index || - oldStartChar != textblock->hlStart[layer].nChar || - oldEndIndex != textblock->hlEnd[layer].index || - oldEndChar != textblock->hlEnd[layer].nChar) - textblock->queueDrawRange (index1, index2); + // TODO What about OOF widgets? } void Textblock::TextblockIterator::unhighlight (int direction, core::HighlightLayer layer) { - Textblock *textblock = (Textblock*)getWidget(); - int index1 = index, index2 = index; - - if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) - return; - - int oldStartIndex = textblock->hlStart[layer].index; - int oldStartChar = textblock->hlStart[layer].nChar; - int oldEndIndex = textblock->hlEnd[layer].index; - int oldEndChar = textblock->hlEnd[layer].nChar; - - if (direction == 0) { - index1 = textblock->hlStart[layer].index; - index2 = textblock->hlEnd[layer].index; - textblock->hlStart[layer].index = 1; - textblock->hlEnd[layer].index = 0; - } else if (direction > 0 && textblock->hlStart[layer].index <= index) { - index1 = textblock->hlStart[layer].index; - textblock->hlStart[layer].index = index + 1; - textblock->hlStart[layer].nChar = 0; - } else if (direction < 0 && textblock->hlEnd[layer].index >= index) { - index1 = textblock->hlEnd[layer].index; - textblock->hlEnd[layer].index = index - 1; - textblock->hlEnd[layer].nChar = INT_MAX; + if (!oofm) { + Textblock *textblock = (Textblock*)getWidget(); + int index1 = index, index2 = index; + + if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) + return; + + int oldStartIndex = textblock->hlStart[layer].index; + int oldStartChar = textblock->hlStart[layer].nChar; + int oldEndIndex = textblock->hlEnd[layer].index; + int oldEndChar = textblock->hlEnd[layer].nChar; + + if (direction == 0) { + index1 = textblock->hlStart[layer].index; + index2 = textblock->hlEnd[layer].index; + textblock->hlStart[layer].index = 1; + textblock->hlEnd[layer].index = 0; + } else if (direction > 0 && textblock->hlStart[layer].index <= index) { + index1 = textblock->hlStart[layer].index; + textblock->hlStart[layer].index = index + 1; + textblock->hlStart[layer].nChar = 0; + } else if (direction < 0 && textblock->hlEnd[layer].index >= index) { + index1 = textblock->hlEnd[layer].index; + textblock->hlEnd[layer].index = index - 1; + textblock->hlEnd[layer].nChar = INT_MAX; + } + + if (oldStartIndex != textblock->hlStart[layer].index || + oldStartChar != textblock->hlStart[layer].nChar || + oldEndIndex != textblock->hlEnd[layer].index || + oldEndChar != textblock->hlEnd[layer].nChar) + textblock->queueDrawRange (index1, index2); } - if (oldStartIndex != textblock->hlStart[layer].index || - oldStartChar != textblock->hlStart[layer].nChar || - oldEndIndex != textblock->hlEnd[layer].index || - oldEndChar != textblock->hlEnd[layer].nChar) - textblock->queueDrawRange (index1, index2); + // TODO What about OOF widgets? } void Textblock::TextblockIterator::getAllocation (int start, int end, core::Allocation *allocation) { Textblock *textblock = (Textblock*)getWidget(); - int lineIndex = textblock->findLineOfWord (index); - Line *line = textblock->lines->getRef (lineIndex); - Word *word = textblock->words->getRef (index); - - allocation->x = - textblock->allocation.x + textblock->lineXOffsetWidget (line); - for (int i = line->firstWord; i < index; i++) { - Word *w = textblock->words->getRef(i); - allocation->x += w->size.width + w->effSpace; - } - if (start > 0 && word->content.type == core::Content::TEXT) { - allocation->x += textblock->textWidth (word->content.text, 0, start, - word->style, - word->flags & Word::WORD_START, - (word->flags & Word::WORD_END) - && word->content.text[start] == 0); - } - allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent - - word->size.ascent; - - allocation->width = word->size.width; - if (word->content.type == core::Content::TEXT) { - int wordEnd = strlen(word->content.text); - - if (start > 0 || end < wordEnd) { - end = misc::min(end, wordEnd); /* end could be INT_MAX */ - allocation->width = - textblock->textWidth (word->content.text, start, end - start, - word->style, - (word->flags & Word::WORD_START) - && start == 0, - (word->flags & Word::WORD_END) - && word->content.text[end] == 0); + if (oofm) { + // TODO Consider start and end? + *allocation = + *(textblock->outOfFlowMgr->getWidget(index)->getAllocation()); + } else { + int lineIndex = textblock->findLineOfWord (index); + Line *line = textblock->lines->getRef (lineIndex); + Word *word = textblock->words->getRef (index); + + allocation->x = + textblock->allocation.x + line->offsetCompleteWidget; + + for (int i = line->firstWord; i < index; i++) { + Word *w = textblock->words->getRef(i); + allocation->x += w->size.width + w->effSpace; + } + if (start > 0 && word->content.type == core::Content::TEXT) { + allocation->x += textblock->textWidth (word->content.text, 0, start, + word->style, + word->flags & Word::WORD_START, + (word->flags & Word::WORD_END) + && word->content.text[start] + == 0); + } + allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent - + word->size.ascent; + + allocation->width = word->size.width; + if (word->content.type == core::Content::TEXT) { + int wordEnd = strlen(word->content.text); + + if (start > 0 || end < wordEnd) { + end = misc::min(end, wordEnd); /* end could be INT_MAX */ + allocation->width = + textblock->textWidth (word->content.text, start, end - start, + word->style, + (word->flags & Word::WORD_START) + && start == 0, + (word->flags & Word::WORD_END) + && word->content.text[end] == 0); + } } + allocation->ascent = word->size.ascent; + allocation->descent = word->size.descent; } - allocation->ascent = word->size.ascent; - allocation->descent = word->size.descent; +} + +void Textblock::TextblockIterator::print () +{ + Iterator::print (); + printf (", oofm = %s, index = %d", oofm ? "true" : "false", index); + } } // namespace dw diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc index c07dc602..93f71b93 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -23,6 +23,7 @@ #include "textblock.hh" #include "hyphenator.hh" #include "../lout/msg.h" +#include "../lout/debug.hh" #include "../lout/misc.hh" #include <stdio.h> @@ -232,21 +233,7 @@ void Textblock::BadnessAndPenalty::print () void Textblock::printWordShort (Word *word) { - switch(word->content.type) { - case core::Content::TEXT: - printf ("\"%s\"", word->content.text); - break; - case core::Content::WIDGET: - printf ("<widget: %p (%s)>", - word->content.widget, word->content.widget->getClassName()); - break; - case core::Content::BREAK: - printf ("<break>"); - break; - default: - printf ("<?>"); - break; - } + core::Content::print (&(word->content)); } void Textblock::printWordFlags (short flags) @@ -338,12 +325,24 @@ void Textblock::justifyLine (Line *line, int diff) Textblock::Line *Textblock::addLine (int firstWord, int lastWord, bool temporary) { - PRINTF ("[%p] ADD_LINE (%d, %d) => %d\n", - this, firstWord, lastWord, lines->size ()); + DBG_OBJ_MSGF ("construct.line", 0, "<b>addLine</b> (%d, %d) => %d", + firstWord, lastWord, lines->size ()); + DBG_OBJ_MSG_START (); + + int lineWidth; + if (lastWord >= firstWord) { + DBG_MSG_WORD ("construct.line", 1, "<i>first word:</i> ", firstWord, ""); + DBG_MSG_WORD ("construct.line", 1, "<i>last word:</i> ", lastWord, ""); + + Word *lastWordOfLine = words->getRef(lastWord); + PRINTF (" words[%d]->totalWidth = %d\n", lastWord, + lastWordOfLine->totalWidth); + // Word::totalWidth includes the hyphen (which is what we want here). + lineWidth = lastWordOfLine->totalWidth; + } else + // empty line + lineWidth = 0; - Word *lastWordOfLine = words->getRef(lastWord); - // Word::totalWidth includes the hyphen (which is what we want here). - int lineWidth = lastWordOfLine->totalWidth; // "lineWidth" is relative to leftOffset, so we may have to add // "line1OffsetEff" (remember: this is, for list items, negative). if (lines->size () == 0) @@ -353,13 +352,9 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, accumulateWordExtremes (firstWord, lastWord, &maxOfMinWidth, &sumOfMaxWidth); - PRINTF (" words[%d]->totalWidth = %d\n", lastWord, - lastWordOfLine->totalWidth); - - PRINTF ("[%p] ##### LINE ADDED: %d, from %d to %d #####\n", - this, lines->size (), firstWord, lastWord); - lines->increase (); + DBG_OBJ_SET_NUM ("lines.size", lines->size ()); + if(!temporary) { // If the last line was temporary, this will be temporary, too, even // if not requested. @@ -379,6 +374,7 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, line->marginDescent = 0; line->breakSpace = 0; line->leftOffset = 0; + line->finished = false; alignLine (lineIndex); for (int i = line->firstWord; i < line->lastWord; i++) { @@ -400,17 +396,30 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, for(int i = line->firstWord; i <= line->lastWord; i++) accumulateWordForLine (lineIndex, i); - PRINTF (" line[%d].top = %d\n", lines->size () - 1, line->top); - PRINTF (" line[%d].boxAscent = %d\n", lines->size () - 1, line->boxAscent); - PRINTF (" line[%d].boxDescent = %d\n", - lines->size () - 1, line->boxDescent); - PRINTF (" line[%d].contentAscent = %d\n", lines->size () - 1, - line->contentAscent); - PRINTF (" line[%d].contentDescent = %d\n", - lines->size () - 1, line->contentDescent); - - PRINTF (" line[%d].maxLineWidth = %d\n", - lines->size () - 1, line->maxLineWidth); + // Especially empty lines (possible when there are floats) have + // zero height, which may cause endless loops. For this reasons, + // 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); + //DBG_OBJ_MSGF ("construct.line", 1, "contentAscent = %d\n", + // line->contentAscent); + //DBG_OBJ_MSGF ("construct.line", 1, "contentDescent = %d\n", + // 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); mustQueueResize = true; @@ -421,7 +430,7 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, //words->getRef(line->lastWord)->badnessAndPenalty.print (); //printf ("\n"); - int xWidget = lineXOffsetWidget(line); + int xWidget = line->offsetCompleteWidget; for (int i = firstWord; i <= lastWord; i++) { Word *word = words->getRef (i); if (word->wordImgRenderer) @@ -431,6 +440,11 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, xWidget += word->size.width + word->effSpace; } + line->finished = true; + + initNewLine (); + + DBG_OBJ_MSG_END (); return line; } @@ -507,18 +521,18 @@ void Textblock::processWord (int wordIndex) */ bool Textblock::wordWrap (int wordIndex, bool wrapAll) { - PRINTF ("[%p] WORD_WRAP (%d, %s)\n", - this, wordIndex, wrapAll ? "true" : "false"); + DBG_OBJ_MSGF ("construct.word", 0, "<b>wordWrap</b> (%d, %s)", + wordIndex, wrapAll ? "true" : "false"); + DBG_OBJ_MSG_START (); - Word *word; - bool wordListChanged = false; + DBG_MSG_WORD ("construct.word", 1, "<i>wrapped word:</i> ", wordIndex, ""); if (!wrapAll) removeTemporaryLines (); initLine1Offset (wordIndex); - word = words->getRef (wordIndex); + Word *word = words->getRef (wordIndex); word->effSpace = word->origSpace; accumulateWordData (wordIndex); @@ -527,43 +541,128 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll) //printWord (word); //printf ("\n"); + bool b; + switch (word->content.type) { + case core::Content::WIDGET_OOF_REF: + b = wrapWordOofRef (wordIndex, wrapAll); + break; + + default: + b = wrapWordInFlow (wordIndex, wrapAll); + break; + } + + DBG_OBJ_MSG_END (); + + return b; +} + +bool Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) +{ + DBG_OBJ_MSGF ("construct.word", 0, "<b>wrapWordInFlow</b> (%d, %s)", + wordIndex, wrapAll ? "true" : "false"); + DBG_OBJ_MSG_START (); + + Word *word = words->getRef (wordIndex); + bool wordListChanged = false; + int penaltyIndex = calcPenaltyIndexForNewLine (); + checkPossibleLineHeightChange (wordIndex); + bool newLine; do { + // This variable, thereWillBeMoreSpace, is set to true, if, due + // to floats, this line is smaller than following lines will be + // (and, at the end, there will be surely lines without + // floats). If this is the case, lines may, in an extreme case, + // 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 + // lines empty does not make sense without floats, since there will + // be no possibility with more space anymore.) + + bool regardBorder = mustBorderBeRegarded (lines->size ()); + bool thereWillBeMoreSpace = regardBorder ? + newLineHasFloatLeft || newLineHasFloatRight : + false; + + DBG_OBJ_MSGF ("construct.word", 1, + "thereWillBeMoreSpace = %s ? %s || %s : false = %s", + regardBorder ? "true" : "false", + newLineHasFloatLeft ? "true" : "false", + newLineHasFloatRight ? "true" : "false", + thereWillBeMoreSpace ? "true" : "false"); + + bool tempNewLine = false; int firstIndex = lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1; int searchUntil; - if (wrapAll && wordIndex >= firstIndex && wordIndex == words->size() -1) { + if (wordIndex < firstIndex) + // Current word is already part of a line (ending with + // firstIndex - 1), so no new line has to be added. + newLine = false; + else if (wrapAll && wordIndex >= firstIndex && + wordIndex == words->size() -1) { newLine = true; searchUntil = wordIndex; tempNewLine = true; - PRINTF (" NEW LINE: last word\n"); + DBG_OBJ_MSG ("construct.word", 1, "<b>new line:</b> last word"); } else if (wordIndex >= firstIndex && // TODO: lineMustBeBroken should be independent of // the penalty index? word->badnessAndPenalty.lineMustBeBroken (penaltyIndex)) { newLine = true; searchUntil = wordIndex; - PRINTF (" NEW LINE: forced break\n"); + DBG_OBJ_MSG ("construct.word", 1, "<b>new line:</b> forced break"); } else { // Break the line when too tight, but only when there is a // possible break point so far. (TODO: I've forgotten the // original bug which is fixed by this.) + + // Exception of the latter rule: thereWillBeMoreSpace; see + // above, where it is defined. + + DBG_OBJ_MSGF ("construct.word", 1, + "possible line break between %d and %d?", + firstIndex, wordIndex - 1); + DBG_OBJ_MSG_START (); + bool possibleLineBreak = false; - for (int i = firstIndex; !possibleLineBreak && i <= wordIndex - 1; i++) + for (int i = firstIndex; + !(thereWillBeMoreSpace || possibleLineBreak) + && i <= wordIndex - 1; + i++) { + DBG_OBJ_MSGF ("construct.word", 2, "examining word %d", i); if (words->getRef(i)->badnessAndPenalty - .lineCanBeBroken (penaltyIndex)) + .lineCanBeBroken (penaltyIndex)) { + DBG_MSG_WORD ("construct.word", 2, "break possible for word:", + i, ""); possibleLineBreak = true; + } + } - if (possibleLineBreak && word->badnessAndPenalty.lineTooTight ()) { + DBG_OBJ_MSG_END (); + DBG_OBJ_MSGF ("construct.word", 1, "=> %s", + possibleLineBreak ? "true" : "false"); + + DBG_OBJ_MSGF ("construct.word", 1, "word->... too tight: %s", + word->badnessAndPenalty.lineTooTight () ? + "true" : "false"); + + if ((thereWillBeMoreSpace || possibleLineBreak) + && word->badnessAndPenalty.lineTooTight ()) { newLine = true; searchUntil = wordIndex - 1; - PRINTF (" NEW LINE: line too tight\n"); - } else + DBG_OBJ_MSG ("construct.word", 1, + "<b>new line:</b> line too tight"); + } else { + DBG_OBJ_MSG ("construct.word", 1, "no <b>new line</b>"); newLine = false; + } } if(!newLine && !wrapAll) @@ -575,65 +674,89 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll) // newLine is calculated as "true". mustQueueResize = true; + PRINTF ("[%p] special case? newLine = %s, wrapAll = %s => " + "mustQueueResize = %s\n", this, newLine ? "true" : "false", + wrapAll ? "true" : "false", mustQueueResize ? "true" : "false"); + if(newLine) { accumulateWordData (wordIndex); int wordIndexEnd = wordIndex; bool lineAdded; do { - int breakPos = - searchMinBap (firstIndex, searchUntil, penaltyIndex, wrapAll); - int hyphenatedWord = considerHyphenation (firstIndex, breakPos); - - //printf ("[%p] breakPos = %d (", this, breakPos); - //printWordShort (words->getRef (breakPos)); - //printf ("), hyphenatedWord = %d", hyphenatedWord); - //if (hyphenatedWord != -1) { - // printf (" ("); - // printWordShort (words->getRef (hyphenatedWord)); - // printf (")"); - //} - //printf ("\n"); - - if(hyphenatedWord == -1) { - addLine (firstIndex, breakPos, tempNewLine); - PRINTF ("[%p] new line %d (%s), from %d to %d\n", - this, lines->size() - 1, - tempNewLine ? "temporally" : "permanently", - firstIndex, breakPos); + DBG_OBJ_MSG ("construct.word", 1, "<i>line adding loop cycle</i>"); + DBG_OBJ_MSG_START (); + + if (firstIndex > searchUntil) { + // empty line + DBG_OBJ_MSG ("construct.word", 1, "empty line"); + assert (searchUntil == firstIndex - 1); + addLine (firstIndex, firstIndex - 1, tempNewLine); + checkPossibleLineHeightChange (firstIndex); lineAdded = true; penaltyIndex = calcPenaltyIndexForNewLine (); } else { - // TODO hyphenateWord() should return whether something has - // changed at all. So that a second run, with - // !word->canBeHyphenated, is unnecessary. - // TODO Update: for this, searchUntil == 0 should be checked. - PRINTF ("[%p] old searchUntil = %d ...\n", this, searchUntil); - int n = hyphenateWord (hyphenatedWord); - searchUntil += n; - if (hyphenatedWord <= wordIndex) - wordIndexEnd += n; - PRINTF ("[%p] -> new searchUntil = %d ...\n", this, searchUntil); - lineAdded = false; + DBG_OBJ_MSG ("construct.word", 1, "non-empty line"); + + int breakPos = + searchMinBap (firstIndex, searchUntil, penaltyIndex, wrapAll); + int hyphenatedWord = considerHyphenation (firstIndex, breakPos); + + DBG_OBJ_MSGF ("construct.word", 1, "breakPos = %d", breakPos); + DBG_MSG_WORD ("construct.word", 1, "<i>break at word:</i> ", + breakPos, ""); + DBG_OBJ_MSGF ("construct.word", 1, "hyphenatedWord = %d", + hyphenatedWord); + if (hyphenatedWord != -1) + DBG_MSG_WORD ("construct.word", 1, + "<i>hyphenate at word:</i> ", + hyphenatedWord, ""); - // update word pointer as hyphenateWord() can trigger a - // reorganization of the words structure - word = words->getRef (wordIndex); + if(hyphenatedWord == -1) { + addLine (firstIndex, breakPos, tempNewLine); + PRINTF ("[%p] new line %d (%s), from %d to %d\n", + this, lines->size() - 1, + tempNewLine ? "temporally" : "permanently", + firstIndex, breakPos); + lineAdded = true; + penaltyIndex = calcPenaltyIndexForNewLine (); + } else { + // TODO hyphenateWord() should return whether + // something has changed at all. So that a second + // run, with !word->canBeHyphenated, is unnecessary. + // TODO Update: for this, searchUntil == 0 should be + // checked. + DBG_OBJ_MSGF ("construct.word", 1, "old searchUntil = %d", + searchUntil); + int n = hyphenateWord (hyphenatedWord); + searchUntil += n; + if (hyphenatedWord <= wordIndex) + wordIndexEnd += n; + DBG_OBJ_MSGF ("construct.word", 1, "new searchUntil = %d", + searchUntil); + lineAdded = false; + + // update word pointer as hyphenateWord() can trigger a + // reorganization of the words structure + word = words->getRef (wordIndex); - if (n > 0 && hyphenatedWord <= wordIndex) - wordListChanged = true; + if (n > 0 && hyphenatedWord <= wordIndex) + wordListChanged = true; + } + + DBG_OBJ_MSGF ("construct.word", 1, + "accumulating again from %d to %d\n", + breakPos + 1, wordIndexEnd); + for(int i = breakPos + 1; i <= wordIndexEnd; i++) + accumulateWordData (i); } - - PRINTF ("[%p] accumulating again from %d to %d\n", - this, breakPos + 1, wordIndexEnd); - for(int i = breakPos + 1; i <= wordIndexEnd; i++) - accumulateWordData (i); + DBG_OBJ_MSG_END (); } while(!lineAdded); } } while (newLine); - if(word->content.type == core::Content::WIDGET) { + if(word->content.type == core::Content::WIDGET_IN_FLOW) { // Set parentRef for the child, when necessary. // // parentRef is set for the child already, when a line is @@ -650,16 +773,132 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll) firstWordWithoutLine = lines->getLastRef()->lastWord + 1; if (wordIndex >= firstWordWithoutLine) { - word->content.widget->parentRef = lines->size (); + word->content.widget->parentRef = + OutOfFlowMgr::createRefNormalFlow (lines->size ()); PRINTF ("The %s %p is assigned parentRef = %d.\n", word->content.widget->getClassName(), word->content.widget, word->content.widget->parentRef); } } + DBG_OBJ_MSG_END (); + return wordListChanged; } +/** + * Check wheather the newly wrapped word will change the height of the + * newly constructed line. If yes, the borders due to floats may + * change. (Line height is an argument to the calculation of borders.) + * + * Also update newLineAscent and newLineDescent with values of the new + * word. + */ +void Textblock::checkPossibleLineHeightChange (int wordIndex) +{ + DBG_OBJ_MSGF ("construct.line.border", 0, + "<b>checkPossibleLineHeightChange</b> (%d)", wordIndex); + DBG_OBJ_MSG_START (); + + Word *w = words->getRef (wordIndex); + bool heightIncreased = + containingBlock->outOfFlowMgr && + (w->size.ascent > newLineAscent || w->size.descent > newLineDescent); + + DBG_OBJ_MSGF ("construct.line.border", 1, + "(old) line height = %d + %d, word height = %d + %d", + newLineAscent, newLineDescent, w->size.ascent, + w->size.descent); + + newLineAscent = misc::max (newLineAscent, w->size.ascent); + newLineDescent = misc::max (newLineDescent, w->size.descent); + + DBG_OBJ_SET_NUM ("newLineAscent", newLineAscent); + DBG_OBJ_SET_NUM ("newLineDescent", newLineDescent); + + DBG_OBJ_MSGF ("construct.line.border", 1, + "height increased? %s. (new) line height = %d + %d", + heightIncreased ? "yes" : "no", newLineAscent, newLineDescent); + + if (heightIncreased) + updateBorders (wordIndex, true, true); + + DBG_OBJ_MSG_END (); +} + +bool Textblock::wrapWordOofRef (int wordIndex, bool wrapAll) +{ + DBG_OBJ_MSGF ("construct.word", 0, "<b>wrapWordOofRef</b> (%d, %s)", + wordIndex, wrapAll ? "true" : "false"); + DBG_OBJ_MSG_START (); + + assert (containingBlock->outOfFlowMgr); + + int y = yOffsetOfPossiblyMissingLine (lines->size ()); + Widget *widget = words->getRef(wordIndex)->content.widget; + containingBlock->outOfFlowMgr->tellPosition (widget, y); + + // For better performance: Ignore OOF references which do not have + // an effect on borders (e. g. soon absolute positions); and also + // distinguish between left and right border. + + bool left = containingBlock->outOfFlowMgr->affectsLeftBorder (widget); + bool right = containingBlock->outOfFlowMgr->affectsRightBorder (widget); + if (left || right) + updateBorders (wordIndex, left, right); + + + DBG_OBJ_MSG_END (); + + return false; // Actually, the words list is never changed here. +} + +/** + * Recalculate borders (due to floats) for new line. + */ +void Textblock::updateBorders (int wordIndex, bool left, bool right) +{ + DBG_OBJ_MSGF ("construct.line.border", 0, + "<b>updateBorders</b> (%d, %s, %s)", + wordIndex, left ? "true" : "false", right ? "true" : "false"); + DBG_OBJ_MSG_START (); + + assert (left || right); + + int y = yOffsetOfPossiblyMissingLine (lines->size ()); + int h = heightOfPossiblyMissingLine (lines->size ()); + + if (left) { + newLineHasFloatLeft = + containingBlock->outOfFlowMgr->hasFloatLeft (this, y, h, this, + wordIndex); + newLineHasFloatRight = + containingBlock->outOfFlowMgr->hasFloatRight (this, y, h, this, + wordIndex); + } + + if (right) { + newLineLeftBorder = + containingBlock->outOfFlowMgr->getLeftBorder (this, y, h, this, + wordIndex); + newLineRightBorder = + containingBlock->outOfFlowMgr->getRightBorder (this, y, h, this, + wordIndex); + } + + int firstIndex = + lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1; + + // Notice, that firstIndex may be larger than wordIndex, due to + // hyphenation. This means, that the word with the index + // wordIndex is already part of a line. Nothing to do then. + + for (int i = firstIndex; i <= wordIndex; i++) + accumulateWordData (i); + + DBG_OBJ_MSG_END (); +} + int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex, bool correctAtEnd) { @@ -763,7 +1002,6 @@ bool Textblock::isHyphenationCandidate (Word *word) Hyphenator::isHyphenationCandidate (word->content.text); } - /** * Counter part to wordWrap(), but for extremes, not size calculation. */ @@ -882,10 +1120,20 @@ int Textblock::hyphenateWord (int wordIndex) PRINTF ("[%p] %d words ...\n", this, words->size ()); words->insert (wordIndex, numBreaks); + + DBG_IF_RTFL { + for (int i = wordIndex + numBreaks; i < words->size (); i++) + DBG_SET_WORD (i); + } + for (int i = 0; i < numBreaks; i++) initWord (wordIndex + i); PRINTF ("[%p] ... => %d words\n", this, words->size ()); + if (containingBlock->outOfFlowMgr) + containingBlock->outOfFlowMgr->moveExternalIndices (this, wordIndex, + numBreaks); + // Adjust anchor indexes. for (int i = 0; i < anchors->size (); i++) { Anchor *anchor = anchors->getRef (i); @@ -909,6 +1157,10 @@ int Textblock::hyphenateWord (int wordIndex) end - start); PRINTF (" [%d] -> '%s'\n", wordIndex + i, w->content.text); + DBG_OBJ_ARRATTRSET_SYM ("words", wordIndex + i, "type", "TEXT"); + DBG_OBJ_ARRATTRSET_STR ("words", wordIndex + i, + "text/widget/breakSpace", w->content.text); + // Note: there are numBreaks + 1 word parts. if (i == 0) w->flags |= Word::WORD_START; @@ -941,13 +1193,20 @@ int Textblock::hyphenateWord (int wordIndex) } } - accumulateWordData (wordIndex + i); - //printf ("[%p] %d: hyphenated word part: ", this, wordIndex + i); //printWordWithFlags (w); //printf ("\n"); } + // AccumulateWordData() will calculate the width, which depends + // on the borders (possibly limited by floats), which depends on + // the widgeds so far. For this reason, it is important to first + // make all words consistent before calling + // accumulateWordData(); therefore the second loop. + + for (int i = 0; i < numBreaks + 1; i++) + accumulateWordData (wordIndex + i); + PRINTF (" finished\n"); //delete origword->content.text; TODO: Via textZone? @@ -966,8 +1225,12 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) Line *line = lines->getRef (lineIndex); Word *word = words->getRef (wordIndex); - PRINTF (" %d + %d / %d + %d\n", line->boxAscent, line->boxDescent, + PRINTF ("[%p] ACCUMULATE_WORD_FOR_LINE (%d, %d): %d + %d / %d + %d\n", + this, lineIndex, wordIndex, line->boxAscent, line->boxDescent, word->size.ascent, word->size.descent); + //printf (" "); + //printWord (word); + //printf ("\n"); line->boxAscent = misc::max (line->boxAscent, word->size.ascent); line->boxDescent = misc::max (line->boxDescent, word->size.descent); @@ -982,7 +1245,7 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) len += word->style->font->ascent / 3; line->contentDescent = misc::max (line->contentDescent, len); - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { int collapseMarginTop = 0; line->marginDescent = @@ -1006,7 +1269,8 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) + word->content.widget->getStyle()->margin.top - collapseMarginTop); - word->content.widget->parentRef = lineIndex; + word->content.widget->parentRef = + OutOfFlowMgr::createRefNormalFlow (lineIndex); } else { line->marginDescent = misc::max (line->marginDescent, line->boxDescent); @@ -1021,6 +1285,10 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) void Textblock::accumulateWordData (int wordIndex) { + DBG_OBJ_MSGF ("construct.word", 1, "<b>accumulateWordData</b> (%d)", + wordIndex); + DBG_OBJ_MSG_START (); + // Typically, the word in question is in the last line; in any case // quite at the end of the text, so that linear search is actually // the fastest option. @@ -1035,14 +1303,14 @@ void Textblock::accumulateWordData (int wordIndex) firstWordOfLine = lines->getRef(lineIndex - 1)->lastWord + 1; Word *word = words->getRef (wordIndex); - PRINTF ("[%p] ACCUMULATE_WORD_DATA (%d); lineIndex = %d: ...\n", - this, wordIndex, lineIndex); + DBG_OBJ_MSGF ("construct.word", 2, "lineIndex = %d", lineIndex); int availWidth = calcAvailWidth (lineIndex); - PRINTF (" (%s existing line %d starts with word %d)\n", - lineIndex < lines->size () ? "already" : "not yet", - lineIndex, firstWordOfLine); + DBG_OBJ_MSGF ("construct.word", 2, + "(%s existing line %d starts with word %d; availWidth = %d)", + lineIndex < lines->size () ? "already" : "not yet", + lineIndex, firstWordOfLine, availWidth); if (wordIndex == firstWordOfLine) { // first word of the (not neccessarily yet existing) line @@ -1076,23 +1344,43 @@ void Textblock::accumulateWordData (int wordIndex) //printf (" => "); //printWord (word); //printf ("\n"); + + DBG_OBJ_MSG_END (); } int Textblock::calcAvailWidth (int lineIndex) { - int availWidth = - this->availWidth - getStyle()->boxDiffWidth() - innerPadding; + DBG_OBJ_MSGF ("construct.word", 1, "<b>calcAvailWidth</b> (%d <i>of %d</i>)", + lineIndex, lines->size()); + DBG_OBJ_MSG_START (); + + int availWidth = this->availWidth - innerPadding; if (limitTextWidth && layout->getUsesViewport () && - availWidth > layout->getWidthViewport () - 10) + // margin/border/padding will be subtracted later, via OOFM. + availWidth - getStyle()->boxDiffWidth() + > layout->getWidthViewport () - 10) availWidth = layout->getWidthViewport () - 10; if (lineIndex == 0) availWidth -= line1OffsetEff; - //PRINTF("[%p] CALC_AVAIL_WIDTH => %d - %d - %d = %d\n", - // this, this->availWidth, getStyle()->boxDiffWidth(), innerPadding, - // availWidth); + int leftBorder, rightBorder; + if (mustBorderBeRegarded (lineIndex)) { + leftBorder = newLineLeftBorder; + rightBorder = newLineRightBorder; + } else + leftBorder = rightBorder = 0; + + leftBorder = misc::max (leftBorder, getStyle()->boxOffsetX()); + rightBorder = misc::max (rightBorder, getStyle()->boxRestWidth()); + + availWidth -= (leftBorder + rightBorder); + + DBG_OBJ_MSGF ("construct.word", 2, "=> %d - %d - (%d + %d) = %d\n", + this->availWidth, innerPadding, leftBorder, rightBorder, + availWidth); + DBG_OBJ_MSG_END (); return availWidth; } @@ -1108,7 +1396,7 @@ void Textblock::initLine1Offset (int wordIndex) } else { int indent = 0; - if (word->content.type == core::Content::WIDGET && + if (word->content.type == core::Content::WIDGET_IN_FLOW && word->content.widget->blockLevel() == true) { /* don't use text-indent when nesting blocks */ } else { @@ -1180,43 +1468,53 @@ void Textblock::alignLine (int lineIndex) */ void Textblock::rewrap () { - PRINTF ("[%p] REWRAP: wrapRef = %d\n", this, wrapRef); + DBG_OBJ_MSG ("construct.line", 0, "<b>rewrap</b> ()"); + DBG_OBJ_MSG_START (); if (wrapRefLines == -1) - /* page does not have to be rewrapped */ - return; - - /* All lines up from wrapRef will be rebuild from the word list, - * the line list up from this position is rebuild. */ - lines->setSize (wrapRefLines); - nonTemporaryLines = misc::min (nonTemporaryLines, wrapRefLines); - - int firstWord; - if (lines->size () > 0) - firstWord = lines->getLastRef()->lastWord + 1; - else - firstWord = 0; + DBG_OBJ_MSG ("construct.line", 0, "does not have to be rewrapped"); + else { + // All lines up from wrapRef will be rebuild from the word list, + // the line list up from this position is rebuild. + lines->setSize (wrapRefLines); + DBG_OBJ_SET_NUM ("lines.size", lines->size ()); + nonTemporaryLines = misc::min (nonTemporaryLines, wrapRefLines); + + initNewLine (); + + int firstWord; + if (lines->size () > 0) { + Line *lastLine = lines->getLastRef(); + firstWord = lastLine->lastWord + 1; + } else + firstWord = 0; + + DBG_OBJ_MSGF ("construct.line", 0, "starting with word %d", firstWord); - for (int i = firstWord; i < words->size (); i++) { - Word *word = words->getRef (i); + for (int i = firstWord; i < words->size (); i++) { + Word *word = words->getRef (i); - if (word->content.type == core::Content::WIDGET) - calcWidgetSize (word->content.widget, &word->size); - - wordWrap (i, false); + if (word->content.type == core::Content::WIDGET_IN_FLOW) + calcWidgetSize (word->content.widget, &word->size); + + wordWrap (i, false); + + // Somewhat historical, but still important, note: + // + // For the case that something else is done with this word, it + // is important that wordWrap() may insert some new words; since + // NotSoSimpleVector is used for the words list, the internal + // structure may have changed, so getRef() must be called again. + // + // So this is necessary: word = words->getRef (i); + } - // Somewhat historical, but still important, note: - // - // For the case that something else is done with this word, it - // is important that wordWrap() may insert some new words; since - // NotSoSimpleVector is used for the words list, the internal - // structure may have changed, so getRef() must be called again. - // - // So this is necessary: word = words->getRef (i); + // Next time, the page will not have to be rewrapped. + wrapRefLines = -1; + DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines); } - - /* Next time, the page will not have to be rewrapped. */ - wrapRefLines = -1; + + DBG_OBJ_MSG_END (); } /** @@ -1229,9 +1527,12 @@ void Textblock::fillParagraphs () // Notice that wrapRefParagraphs refers to the lines, not to the paragraphs. int firstWordOfLine; - if (lines->size () > 0 && wrapRefParagraphs > 0) - firstWordOfLine = lines->getRef(wrapRefParagraphs - 1)->lastWord + 1; - else + if (lines->size () > 0 && wrapRefParagraphs > 0) { + // Sometimes, wrapRefParagraphs is larger than lines->size(), due to + // floats? (Has to be clarified.) + int lineNo = misc::min (wrapRefParagraphs, lines->size ()) - 1; + firstWordOfLine = lines->getRef(lineNo)->lastWord + 1; + } else firstWordOfLine = 0; int parNo; @@ -1264,22 +1565,93 @@ void Textblock::fillParagraphs () handleWordExtremes (i); wrapRefParagraphs = -1; + DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs); +} + +void Textblock::initNewLine () +{ + DBG_OBJ_MSG ("construct.line", 0, "<b>initNewLine</b> ()"); + DBG_OBJ_MSG_START (); + + // At the very beginning, in Textblock::Textblock, where this + // method is called, containingBlock is not yet defined. + + if (containingBlock && containingBlock->outOfFlowMgr) { + if (lines->size () == 0) { + int clearPosition = + containingBlock->outOfFlowMgr->getClearPosition (this); + setVerticalOffset (misc::max (clearPosition, 0)); + } + + int y = yOffsetOfPossiblyMissingLine (lines->size ()); + int h = heightOfPossiblyMissingLine (lines->size ()); + int lastRef = lines->size() > 0 ? lines->getLastRef()->lastWord : -1; + + newLineHasFloatLeft = + containingBlock->outOfFlowMgr->hasFloatLeft (this, y, h, this, + lastRef); + newLineHasFloatRight = + containingBlock->outOfFlowMgr->hasFloatRight (this, y, h, this, + lastRef); + newLineLeftBorder = + containingBlock->outOfFlowMgr->getLeftBorder (this, y, h, this, + lastRef); + newLineRightBorder = + containingBlock->outOfFlowMgr->getRightBorder (this, y, h, this, + lastRef); + + DBG_OBJ_MSGF ("construct.line", 0, + "%d (%s) / %d (%s), at %d (%d), until %d\n", + newLineLeftBorder, newLineHasFloatLeft ? "true" : "false", + newLineRightBorder, newLineHasFloatRight ? "true" : "false", + y, h, lastRef); + } else { + newLineHasFloatLeft = newLineHasFloatRight = false; + newLineLeftBorder = newLineRightBorder = 0; + + DBG_OBJ_MSG ("construct.line", 0, "<i>no CB of OOFM</i>"); + } + + newLineAscent = newLineDescent = 0; + + DBG_OBJ_SET_NUM ("newLineAscent", newLineAscent); + DBG_OBJ_SET_NUM ("newLineDescent", newLineDescent); + + DBG_OBJ_MSG_END (); } void Textblock::showMissingLines () { - int firstWordToWrap = lines->size () > 0 ? - lines->getRef(lines->size () - 1)->lastWord + 1 : 0; - PRINTF ("[%p] SHOW_MISSING_LINES: wrap from %d to %d\n", - this, firstWordToWrap, words->size () - 1); + DBG_OBJ_MSG ("construct.line", 0, "<b>showMissingLines</b> ()"); + DBG_OBJ_MSG_START (); + int firstWordToWrap = + lines->size () > 0 ? lines->getLastRef()->lastWord + 1 : 0; + DBG_OBJ_MSGF ("construct.line", 1, "firstWordToWrap = %d (of %d)", + firstWordToWrap, words->size ()); + for (int i = firstWordToWrap; i < words->size (); i++) wordWrap (i, true); + + // In some cases, there are some words of type WIDGET_OOF_REF left, which + // are not added to line, since addLine() is only called within + // wrapWordInFlow(), but not within wrapWordOofRef(). The missing line + // is created here. + + int firstWordNotInLine = + lines->size () > 0 ? lines->getLastRef()->lastWord + 1: 0; + DBG_OBJ_MSGF ("construct.line", 1, "firstWordNotInLine = %d (of %d)", + firstWordNotInLine, words->size ()); + if (firstWordNotInLine < words->size ()) + addLine (firstWordNotInLine, words->size () -1, true); + + DBG_OBJ_MSG_END (); } void Textblock::removeTemporaryLines () { lines->setSize (nonTemporaryLines); + DBG_OBJ_SET_NUM ("lines.size", lines->size ()); } int Textblock::getSpaceShrinkability(struct Word *word) diff --git a/dw/types.cc b/dw/types.cc index 86836bc1..56af66d1 100644 --- a/dw/types.cc +++ b/dw/types.cc @@ -268,5 +268,90 @@ void Region::addRectangle (Rectangle *rPointer) rectangleList->append (r); } +Content::Type Content::maskForSelection (bool followReferences) +{ + Content::Type widgetMask = (Content::Type) + (Content::WIDGET_IN_FLOW | + (followReferences ? Content::WIDGET_OOF_REF : Content::WIDGET_OOF_CONT)); + return (Content::Type)(Content::SELECTION_CONTENT | widgetMask); +} + +void Content::intoStringBuffer(Content *content, misc::StringBuffer *sb) +{ + switch(content->type) { + case START: + sb->append ("<start>"); + break; + case END: + sb->append ("<end>"); + break; + case TEXT: + sb->append ("\""); + sb->append (content->text); + sb->append ("\""); + break; + case WIDGET_IN_FLOW: + sb->append ("<widget in flow: "); + sb->appendPointer (content->widget); + sb->append (" ("); + sb->append (content->widget->getClassName()); + sb->append (")>"); + break; + case WIDGET_OOF_REF: + sb->append ("<widget oof ref: "); + sb->appendPointer (content->widget); + sb->append (" ("); + sb->append (content->widget->getClassName()); + sb->append (")>"); + break; + case WIDGET_OOF_CONT: + sb->append ("<widget oof cont: "); + sb->appendPointer (content->widget); + sb->append (" ("); + sb->append (content->widget->getClassName()); + sb->append (")>"); + break; + case BREAK: + sb->append ("<break>"); + break; + default: + sb->append ("<"); + sb->appendInt (content->type); + sb->append ("?>"); + break; + } +} + +void Content::maskIntoStringBuffer(Type mask, misc::StringBuffer *sb) +{ + sb->append ((mask & START) ? "st" : "--"); + sb->append (":"); + sb->append ((mask & END) ? "en" : "--"); + sb->append (":"); + sb->append ((mask & TEXT) ? "tx" : "--"); + sb->append (":"); + sb->append ((mask & WIDGET_IN_FLOW) ? "wf" : "--"); + sb->append (":"); + sb->append ((mask & WIDGET_OOF_REF) ? "Wr" : "--"); + sb->append (":"); + sb->append ((mask & WIDGET_OOF_CONT) ? "Wc" : "--"); + sb->append (":"); + sb->append ((mask & BREAK) ? "br" : "--"); +} + +void Content::print (Content *content) +{ + misc::StringBuffer sb; + intoStringBuffer (content, &sb); + printf ("%s", sb.getChars ()); +} + +void Content::printMask (Type mask) +{ + misc::StringBuffer sb; + maskIntoStringBuffer (mask, &sb); + printf ("%s", sb.getChars ()); +} + } // namespace core } // namespace dw diff --git a/dw/types.hh b/dw/types.hh index f04fc138..e910d296 100644 --- a/dw/types.hh +++ b/dw/types.hh @@ -188,11 +188,27 @@ struct Content START = 1 << 0, END = 1 << 1, TEXT = 1 << 2, - WIDGET = 1 << 3, - BREAK = 1 << 4, + + /** \brief widget in normal flow, so that _this_ widget + (containing this content) is both container (parent) and + generator */ + WIDGET_IN_FLOW = 1 << 3, + + /** \brief widget out of flow (OOF); _this_ widget (containing + this content) is only the container (parent), but _not_ + generator */ + WIDGET_OOF_CONT = 1 << 4, + + /** \brief reference to a widget out of flow (OOF); _this_ + widget (containing this content) is only the generator + (parent), but _not_ container */ + WIDGET_OOF_REF = 1 << 5, + BREAK = 1 << 6, + ALL = 0xff, REAL_CONTENT = 0xff ^ (START | END), - SELECTION_CONTENT = TEXT | WIDGET | BREAK + SELECTION_CONTENT = TEXT | BREAK, // WIDGET_* must be set additionally + ANY_WIDGET = WIDGET_IN_FLOW | WIDGET_OOF_CONT | WIDGET_OOF_REF, }; /* Content is embedded in struct Word therefore we @@ -205,6 +221,13 @@ struct Content Widget *widget; int breakSpace; }; + + static Content::Type maskForSelection (bool followReferences); + + static void intoStringBuffer(Content *content, lout::misc::StringBuffer *sb); + static void maskIntoStringBuffer(Type mask, lout::misc::StringBuffer *sb); + static void print (Content *content); + static void printMask (Type mask); }; } // namespace core @@ -271,7 +271,7 @@ void ComplexButtonResource::LayoutReceiver::canvasSizeChanged (int width, /** * \todo Verify that this is correct. */ - resource->queueResize (resource->childWidget->extremesChanged ()); + resource->queueResize (resource->childWidget->extremesQueued ()); } ComplexButtonResource::ComplexButtonResource () diff --git a/dw/widget.cc b/dw/widget.cc index 385bdb97..8147e3d7 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -70,7 +70,7 @@ Widget::Widget () registerName ("dw::core::Widget", &CLASS_ID); flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS); - parent = NULL; + parent = generator = NULL; layout = NULL; allocation.x = -1; @@ -150,15 +150,17 @@ void Widget::setParent (Widget *parent) buttonSensitive = parent->buttonSensitive; DBG_OBJ_ASSOC_PARENT (parent); - //printf ("The %s %p becomes a child of the %s %p\n", // getClassName(), this, parent->getClassName(), parent); + + notifySetParent(); } void Widget::queueDrawArea (int x, int y, int width, int height) { /** \todo Maybe only the intersection? */ - layout->queueDraw (x + allocation.x, y + allocation.y, width, height); + if (layout) + layout->queueDraw (x + allocation.x, y + allocation.y, width, height); _MSG("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height); } @@ -167,40 +169,89 @@ void Widget::queueDrawArea (int x, int y, int width, int height) */ void Widget::queueResize (int ref, bool extremesChanged) { + // queueResize() can be called recursively; calls are queued, so + // that actualQueueResize() is clean. + + if (queueResizeEntered ()) + layout->queueQueueResizeList->put (new Layout::QueueResizeItem + (this, ref, extremesChanged)); + else { + actualQueueResize (ref, extremesChanged); + + while (layout != NULL && layout->queueQueueResizeList->size () > 0) { + Layout::QueueResizeItem *item = layout->queueQueueResizeList->get (0); + item->widget->actualQueueResize (item->ref, item->extremesChanged); + layout->queueQueueResizeList->remove (0); // hopefully not too large + } + } +} + +void Widget::actualQueueResize (int ref, bool extremesChanged) +{ + assert (!queueResizeEntered ()); + + DBG_OBJ_MSGF ("resize", 0, "<b>queueResize</b> (%d, %s)", + ref, extremesChanged ? "true" : "false"); + DBG_OBJ_MSG_START (); + + enterQueueResize (); + Widget *widget2, *child; - //printf("The %stop-level %s %p with parentRef = %d has changed its size.\n", - // parent ? "non-" : "", getClassName(), this, parentRef); + //printf("The %stop-level %s %p with parentRef = %d has changed its size. " + // "Layout = %p.\n", + // parent ? "non-" : "", getClassName(), this, parentRef, layout); - setFlags (NEEDS_RESIZE); - setFlags (NEEDS_ALLOCATE); - markSizeChange (ref); + Flags resizeFlag, extremesFlag; + + if (layout) { + // If RESIZE_QUEUED is set, this widget is already in the list. + if (!resizeQueued ()) + layout->queueResizeList->put (this); + + resizeFlag = RESIZE_QUEUED; + extremesFlag = EXTREMES_QUEUED; + } else { + resizeFlag = NEEDS_RESIZE; + extremesFlag = EXTREMES_CHANGED; + } + setFlags (resizeFlag); + setFlags (ALLOCATE_QUEUED); + markSizeChange (ref); + if (extremesChanged) { - setFlags (EXTREMES_CHANGED); + setFlags (extremesFlag); markExtremesChange (ref); } - - for (widget2 = parent, child = this; - widget2; - child = widget2, widget2 = widget2->parent) { - widget2->setFlags (NEEDS_RESIZE); - widget2->markSizeChange (child->parentRef); - widget2->setFlags (NEEDS_ALLOCATE); - - //printf (" Setting DW_NEEDS_RESIZE and NEEDS_ALLOCATE for the " + + 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 (EXTREMES_CHANGED); + widget2->setFlags (extremesFlag); widget2->markExtremesChange (child->parentRef); } } if (layout) layout->queueResize (); + + leaveQueueResize (); + + DBG_OBJ_MSG_END (); } @@ -210,6 +261,28 @@ void Widget::queueResize (int ref, bool extremesChanged) */ void Widget::sizeRequest (Requisition *requisition) { + assert (!queueResizeEntered ()); + + DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>"); + DBG_OBJ_MSG_START (); + + enterSizeRequest (); + + //printf ("The %stop-level %s %p with parentRef = %d: needsResize: %s, " + // "resizeQueued = %s\n", + // parent ? "non-" : "", getClassName(), this, parentRef, + // needsResize () ? "true" : "false", + // resizeQueued () ? "true" : "false"); + + if (resizeQueued ()) { + // This method is called outside of Layout::resizeIdle. + setFlags (NEEDS_RESIZE); + unsetFlags (RESIZE_QUEUED); + // The widget is not taken out of Layout::queueResizeList, since + // other *_QUEUED flags may still be set and processed in + // Layout::resizeIdle. + } + if (needsResize ()) { /** \todo Check requisition == &(this->requisition) and do what? */ sizeRequestImpl (requisition); @@ -221,6 +294,13 @@ void Widget::sizeRequest (Requisition *requisition) DBG_OBJ_SET_NUM ("requisition.descent", requisition->descent); } else *requisition = this->requisition; + + //printf (" ==> Result: %d x (%d + %d)\n", + // requisition->width, requisition->ascent, requisition->descent); + + leaveSizeRequest (); + + DBG_OBJ_MSG_END (); } /** @@ -228,6 +308,22 @@ void Widget::sizeRequest (Requisition *requisition) */ void Widget::getExtremes (Extremes *extremes) { + assert (!queueResizeEntered ()); + + DBG_OBJ_MSG ("resize", 0, "<b>getExtremes</b>"); + DBG_OBJ_MSG_START (); + + enterGetExtremes (); + + if (extremesQueued ()) { + // This method is called outside of Layout::resizeIdle. + setFlags (EXTREMES_CHANGED); + unsetFlags (EXTREMES_QUEUED); + // The widget is not taken out of Layout::queueResizeList, since + // other *_QUEUED flags may still be set and processed in + // Layout::resizeIdle. + } + if (extremesChanged ()) { getExtremesImpl (extremes); this->extremes = *extremes; @@ -237,6 +333,10 @@ void Widget::getExtremes (Extremes *extremes) DBG_OBJ_SET_NUM ("extremes.maxWidth", extremes->maxWidth); } else *extremes = this->extremes; + + leaveGetExtremes (); + + DBG_OBJ_MSG_END (); } /** @@ -245,6 +345,28 @@ void Widget::getExtremes (Extremes *extremes) */ void Widget::sizeAllocate (Allocation *allocation) { + assert (!queueResizeEntered ()); + assert (!sizeRequestEntered ()); + 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 (); + + enterSizeAllocate (); + + /*printf ("The %stop-level %s %p is allocated:\n", + parent ? "non-" : "", getClassName(), this); + printf (" old = (%d, %d, %d + (%d + %d))\n", + this->allocation.x, this->allocation.y, this->allocation.width, + this->allocation.ascent, this->allocation.descent); + printf (" new = (%d, %d, %d + (%d + %d))\n", + allocation->x, allocation->y, allocation->width, allocation->ascent, + allocation->descent); + printf (" NEEDS_ALLOCATE = %s\n", needsAllocate () ? "true" : "false");*/ + if (needsAllocate () || allocation->x != this->allocation.x || allocation->y != this->allocation.y || @@ -285,6 +407,10 @@ void Widget::sizeAllocate (Allocation *allocation) } /*unsetFlags (NEEDS_RESIZE);*/ + + leaveSizeAllocate (); + + DBG_OBJ_MSG_END (); } bool Widget::buttonPress (EventButton *event) @@ -507,6 +633,25 @@ int Widget::getLevel () } /** + * \brief Get the level of the widget within the tree, regarting the + * generators, not the parents. + * + * The root widget has the level 0. + */ +int Widget::getGeneratorLevel () +{ + Widget *widget = this; + int level = 0; + + while (widget->getGenerator ()) { + level++; + widget = widget->getGenerator (); + } + + return level; +} + +/** * \brief Get the widget with the highest level, which is a direct ancestor of * widget1 and widget2. */ @@ -566,7 +711,9 @@ Widget *Widget::getWidgetAtPoint (int x, int y, int level) * is such a child, it is returned. Otherwise, this widget is returned. */ childAtPoint = NULL; - it = iterator (Content::WIDGET, false); + it = iterator ((Content::Type) + (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT), + false); while (childAtPoint == NULL && it->next ()) childAtPoint = it->getContent()->widget->getWidgetAtPoint (x, y, @@ -627,6 +774,25 @@ void Widget::markExtremesChange (int ref) { } +/** + * \brief This method is called after a widget has been set as the top of a + * widget tree. + * + * A widget may override this method when it is necessary to be notified. + */ +void Widget::notifySetAsTopLevel() +{ +} + +/** + * \brief This method is called after a widget has been added to a parent. + * + * A widget may override this method when it is necessary to be notified. + */ +void Widget::notifySetParent() +{ +} + void Widget::setWidth (int width) { } diff --git a/dw/widget.hh b/dw/widget.hh index 34b35efa..8ebc2767 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -27,23 +27,46 @@ class Widget: public lout::identity::IdentifiableObject protected: enum Flags { /** + * \todo Comment this. + */ + RESIZE_QUEUED = 1 << 0, + + /** + * \todo Comment this. + */ + EXTREMES_QUEUED = 1 << 1, + + /** * \brief Set, when dw::core::Widget::requisition is not up to date * anymore. + * + * \todo Update, see RESIZE_QUEUED. */ - NEEDS_RESIZE = 1 << 0, + NEEDS_RESIZE = 1 << 2, /** * \brief Only used internally, set to enforce size allocation. * - * (I've forgotten the case, for which this is necessary.) + * In some cases, the size of a widget remains the same, but the + * children are allocated at different positions and in + * different sizes, so that a simple comparison of old and new + * allocation is insufficient. Therefore, this flag is set + * (indirectly, as ALLOCATE_QUEUED) in queueResize. */ - NEEDS_ALLOCATE = 1 << 1, + NEEDS_ALLOCATE = 1 << 3, + + /** + * \todo Comment this. + */ + ALLOCATE_QUEUED = 1 << 4, /** * \brief Set, when dw::core::Widget::extremes is not up to date * anymore. + * + * \todo Update, see RESIZE_QUEUED. */ - EXTREMES_CHANGED = 1 << 2, + EXTREMES_CHANGED = 1 << 5, /** * \brief Set by the widget itself (in the constructor), when set... @@ -51,7 +74,7 @@ protected: * * Will hopefully be removed, after redesigning the size model. */ - USES_HINTS = 1 << 3, + USES_HINTS = 1 << 6, /** * \brief Set by the widget itself (in the constructor), when it contains @@ -59,19 +82,19 @@ protected: * * Will hopefully be removed, after redesigning the size model. */ - HAS_CONTENTS = 1 << 4, + 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 << 5, + WAS_ALLOCATED = 1 << 8, /** * \brief Set for block-level widgets (as opposed to inline widgets) */ - BLOCK_LEVEL = 1 << 6, + BLOCK_LEVEL = 1 << 9, }; /** @@ -101,6 +124,14 @@ private: * \brief The parent widget, NULL for top-level widgets. */ Widget *parent; + + /** + * \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. + */ + Widget *generator; + style::Style *style; Flags flags; @@ -133,6 +164,8 @@ private: */ bool buttonSensitiveSet; + void actualQueueResize (int ref, bool extremesChanged); + public: /** * \brief This value is defined by the parent widget, and used for @@ -200,6 +233,9 @@ protected: */ virtual void markExtremesChange (int ref); + virtual void notifySetAsTopLevel(); + virtual void notifySetParent(); + virtual bool buttonPressImpl (EventButton *event); virtual bool buttonReleaseImpl (EventButton *event); virtual bool motionNotifyImpl (EventMotion *event); @@ -249,14 +285,37 @@ public: inline void setDeleteCallback(DW_Callback_t func, void *data) { deleteCallbackFunc = func; deleteCallbackData = data; } +private: + bool resizeIdleEntered () { return layout && layout->resizeIdleCounter; } + + void enterQueueResize () { if (layout) layout->queueResizeCounter++; } + void leaveQueueResize () { if (layout) layout->queueResizeCounter--; } + bool queueResizeEntered () { return layout && layout->queueResizeCounter; } + + void enterSizeAllocate () { if (layout) layout->sizeAllocateCounter++; } + void leaveSizeAllocate () { if (layout) layout->sizeAllocateCounter--; } + bool sizeAllocateEntered () { return layout && layout->sizeAllocateCounter; } + + void enterSizeRequest () { if (layout) layout->sizeRequestCounter++; } + void leaveSizeRequest () { if (layout) layout->sizeRequestCounter--; } + bool sizeRequestEntered () { return layout && layout->sizeRequestCounter; } + + void enterGetExtremes () { if (layout) layout->getExtremesCounter++; } + void leaveGetExtremes () { if (layout) layout->getExtremesCounter--; } + bool getExtremesEntered () { return layout && layout->getExtremesCounter; } + + public: static int CLASS_ID; Widget (); ~Widget (); + inline bool resizeQueued () { return flags & RESIZE_QUEUED; } + inline bool extremesQueued () { return flags & EXTREMES_QUEUED; } inline bool needsResize () { return flags & NEEDS_RESIZE; } inline bool needsAllocate () { return flags & NEEDS_ALLOCATE; } + 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; } @@ -265,6 +324,8 @@ public: void setParent (Widget *parent); + void setGenerator (Widget *generator) { this->generator = generator; } + inline style::Style *getStyle () { return style; } /** \todo I do not like this. */ inline Allocation *getAllocation () { return &allocation; } @@ -302,8 +363,11 @@ public: inline Widget *getParent () { return parent; } Widget *getTopLevel (); int getLevel (); + int getGeneratorLevel (); Widget *getNearestCommonAncestor (Widget *otherWidget); + inline Widget *getGenerator () { return generator ? generator : parent; } + inline Layout *getLayout () { return layout; } virtual Widget *getWidgetAtPoint (int x, int y, int level); diff --git a/lout/Makefile.am b/lout/Makefile.am index bef9696e..f2219360 100644 --- a/lout/Makefile.am +++ b/lout/Makefile.am @@ -1,5 +1,6 @@ AM_CPPFLAGS = \ - -I$(top_srcdir) + -I$(top_srcdir) \ + -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/lout"' noinst_LIBRARIES = liblout.a diff --git a/lout/container.cc b/lout/container.cc index d3385137..dcac4726 100644 --- a/lout/container.cc +++ b/lout/container.cc @@ -22,6 +22,7 @@ #include "container.hh" #include "misc.hh" +#include "debug.hh" namespace lout { @@ -188,9 +189,10 @@ void Vector::remove(int pos) /** * Sort the elements in the vector. Assumes that all elements are Comparable's. */ -void Vector::sort() +void Vector::sort(Comparator *comparator) { - qsort (array, numElements, sizeof(Object*), Comparable::compareFun); + Comparator::compareFunComparator = comparator; + qsort (array, numElements, sizeof(Object*), Comparator::compareFun); } /** @@ -202,44 +204,58 @@ void Vector::sort() * size of the array. (This is the value which can be used for * insertion; see insertSortet()). */ -int Vector::bsearch(Object *key, bool mustExist) +int Vector::bsearch(Object *key, bool mustExist, int start, int end, + Comparator *comparator) { // The case !mustExist is not handled by bsearch(3), so here is a // new implementation. - if (numElements == 0) - return mustExist ? -1 : 0; - - int high = numElements - 1, low = 0; - - while (true) { - int index = (low + high) / 2; - int c = ((Comparable*) key)->compareTo ((Comparable*)array[index]); - if (c == 0) - return index; - else { - if (low >= high) { - if (mustExist) - return -1; + + DBG_OBJ_MSGF ("container", 0, + "<b>bsearch</b> (<i>key</i>, %s, %d, %d, <i>comparator</i>) " + "[size is %d]", + mustExist ? "true" : "false", start, end, size ()); + DBG_OBJ_MSG_START (); + + int result = -123; // Compiler happiness: GCC 4.7 does not handle this? + + if (start > end) { + DBG_OBJ_MSG ("container", 1, "empty"); + result = mustExist ? -1 : start; + } else { + int low = start, high = end; + bool found = false; + + while (!found) { + int index = (low + high) / 2; + int c = comparator->compare (key, array[index]); + DBG_OBJ_MSGF ("container", 1, + "searching within %d and %d; compare key with #%d => %d", + low, high, index, c); + if (c == 0) { + found = true; + result = index; + } else { + if (low >= high) { + if (mustExist) { + found = true; + result = -1; + } else { + found = true; + result = c > 0 ? index + 1 : index; + } + } + + if (c < 0) + high = index - 1; else - return c > 0 ? index + 1 : index; + low = index + 1; } - - if (c < 0) - high = index - 1; - else - low = index + 1; } - } - + } - /* - void *result = ::bsearch (&key, array, numElements, sizeof (Object*), - Comparable::compareFun); - if (result) - return (Object**)result - array; - else - return -1; - */ + DBG_OBJ_MSGF ("container", 1, "result = %d", result); + DBG_OBJ_MSG_END (); + return result; } Object *Vector::VectorIterator::getNext() diff --git a/lout/container.hh b/lout/container.hh index 14803140..03800efd 100644 --- a/lout/container.hh +++ b/lout/container.hh @@ -137,16 +137,23 @@ public: * Notice that insertion is not very efficient, unless the position * is rather at the end. */ - inline void insertSorted(object::Object *newElement) - { insert (newElement, bsearch (newElement, false)); } + inline void insertSorted(object::Object *newElement, + object::Comparator *comparator = + &object::standardComparator) + { insert (newElement, bsearch (newElement, false, comparator)); } void remove(int pos); inline object::Object *get(int pos) const { return (pos >= 0 && pos < numElements) ? array[pos] : NULL; } inline int size() { return numElements; } void clear(); - void sort(); - int bsearch(Object *key, bool mustExist); + void sort(object::Comparator *comparator = &object::standardComparator); + int bsearch(Object *key, bool mustExist, int start, int end, + object::Comparator *comparator = &object::standardComparator); + inline int bsearch(Object *key, bool mustExist, + object::Comparator *comparator = + &object::standardComparator) + { return bsearch (key, mustExist, 0, size () - 1, comparator); } }; @@ -406,16 +413,28 @@ public: { ((untyped::Vector*)this->base)->put(newElement, newPos); } inline void insert(T *newElement, int pos) { ((untyped::Vector*)this->base)->insert(newElement, pos); } - inline void insertSorted(T *newElement) - { ((untyped::Vector*)this->base)->insertSorted(newElement); } + inline void insertSorted(T *newElement, + object::Comparator *comparator = + &object::standardComparator) + { ((untyped::Vector*)this->base)->insertSorted(newElement, comparator); } inline void remove(int pos) { ((untyped::Vector*)this->base)->remove(pos); } inline T *get(int pos) const { return (T*)((untyped::Vector*)this->base)->get(pos); } inline int size() const { return ((untyped::Vector*)this->base)->size(); } inline void clear() { ((untyped::Vector*)this->base)->clear(); } - inline void sort() { ((untyped::Vector*)this->base)->sort(); } - inline int bsearch(T *key, bool mustExist) - { return ((untyped::Vector*)this->base)->bsearch(key, mustExist); } + inline void sort(object::Comparator *comparator = + &object::standardComparator) + { ((untyped::Vector*)this->base)->sort(comparator); } + inline int bsearch(T *key, bool mustExist, int start, int end, + object::Comparator *comparator = + &object::standardComparator) + { return ((untyped::Vector*)this->base)->bsearch(key, mustExist, start, end, + comparator); } + inline int bsearch(T *key, bool mustExist, + object::Comparator *comparator = + &object::standardComparator) + { return ((untyped::Vector*)this->base)->bsearch(key, mustExist, + comparator); } }; diff --git a/lout/debug.hh b/lout/debug.hh index 083234f8..a2393470 100644 --- a/lout/debug.hh +++ b/lout/debug.hh @@ -17,6 +17,8 @@ #define D_STMT_START do #define D_STMT_END while (0) +#define D_STMT_NOP D_STMT_START { } D_STMT_END + # ifdef DEBUG_LEVEL # define DEBUG_MSG(level, ...) \ D_STMT_START { \ @@ -38,10 +40,12 @@ #include <unistd.h> #include <stdio.h> +#define DBG_IF_RTFL if(1) + // "\n" at the beginning just in case that the previous line is not finished // yet. #define RTFL_PREFIX_FMT "\n[rtfl]%s:%d:%d:" -#define RTFL_PREFIX_ARGS __FILE__, __LINE__, getpid() +#define RTFL_PREFIX_ARGS CUR_WORKING_DIR "/" __FILE__, __LINE__, getpid() #define DBG_OBJ_MSG(aspect, prio, msg) \ D_STMT_START { \ @@ -50,6 +54,15 @@ fflush (stdout); \ } D_STMT_END +// Variant which does not use "this", but an explicitly passed +// object. Should be applied to other macros. (Also, for use in C.) +#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:%s\n", \ + RTFL_PREFIX_ARGS, obj, aspect, prio, msg); \ + fflush (stdout); \ + } D_STMT_END + #define DBG_OBJ_MSGF(aspect, prio, fmt, ...) \ D_STMT_START { \ printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:" fmt "\n", \ @@ -57,6 +70,14 @@ fflush (stdout); \ } D_STMT_END +// See DBG_OBJ_MSG_O. +#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:" fmt "\n", \ + RTFL_PREFIX_ARGS, obj, aspect, prio, __VA_ARGS__); \ + fflush (stdout); \ + } D_STMT_END + #define DBG_OBJ_MSG_START() \ D_STMT_START { \ printf (RTFL_PREFIX_FMT "obj-msg-start:%p\n", \ @@ -64,6 +85,14 @@ fflush (stdout); \ } D_STMT_END +// See DBG_OBJ_MSG_O. +#define DBG_OBJ_MSG_START_O(obj) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg-start:%p\n", \ + RTFL_PREFIX_ARGS, obj); \ + fflush (stdout); \ + } D_STMT_END + #define DBG_OBJ_MSG_END() \ D_STMT_START { \ printf (RTFL_PREFIX_FMT "obj-msg-end:%p\n", \ @@ -71,6 +100,14 @@ fflush (stdout); \ } D_STMT_END +// See DBG_OBJ_MSG_O. +#define DBG_OBJ_MSG_END_O(obj) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-msg-end:%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", \ @@ -124,13 +161,27 @@ fflush (stdout); \ } D_STMT_END -#define DBG_OBJ_SET_STR(var, val) \ +#define DBG_OBJ_SET_NUM_O(obj, var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%d\n", \ + RTFL_PREFIX_ARGS, obj, var, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_SET_SYM(var, val) \ D_STMT_START { \ printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \ RTFL_PREFIX_ARGS, this, var, val); \ fflush (stdout); \ } D_STMT_END +#define DBG_OBJ_SET_STR(var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:\"%s\"\n", \ + RTFL_PREFIX_ARGS, this, var, val); \ + fflush (stdout); \ + } D_STMT_END + #define DBG_OBJ_SET_PTR(var, val) \ D_STMT_START { \ printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%p\n", \ @@ -140,22 +191,57 @@ #define DBG_OBJ_ARRSET_NUM(var, ind, val) \ D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:" var ".%d:%d\n", \ - RTFL_PREFIX_ARGS, this, ind, val); \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%d\n", \ + RTFL_PREFIX_ARGS, this, var, ind, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRSET_SYM(var, ind, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%s\n", \ + RTFL_PREFIX_ARGS, this, var, ind, val); \ fflush (stdout); \ } D_STMT_END #define DBG_OBJ_ARRSET_STR(var, ind, val) \ D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:" var ".%d:%s\n", \ - RTFL_PREFIX_ARGS, this, ind, val); \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:\"%s\"\n", \ + RTFL_PREFIX_ARGS, this, var, ind, val); \ fflush (stdout); \ } D_STMT_END #define DBG_OBJ_ARRSET_PTR(var, ind, val) \ D_STMT_START { \ - printf (RTFL_PREFIX_FMT "obj-set:%p:" var ".%d:%p\n", \ - RTFL_PREFIX_ARGS, this, ind, val); \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%p\n", \ + RTFL_PREFIX_ARGS, this, var, ind, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%d\n", \ + RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%s\n", \ + RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:\"%s\"\n", \ + RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ + fflush (stdout); \ + } D_STMT_END + +#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%p\n", \ + RTFL_PREFIX_ARGS, this, var, ind, attr, val); \ fflush (stdout); \ } D_STMT_END @@ -168,23 +254,36 @@ #else /* DBG_RTFL */ -#define DBG_OBJ_MSG(aspect, prio, msg) -#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) -#define DBG_OBJ_MSG_START() -#define DBG_OBJ_MSG_END() -#define DBG_OBJ_CREATE(klass) -#define DBG_OBJ_DELETE() -#define DBG_OBJ_BASECLASS(klass) -#define DBG_OBJ_ASSOC_PARENT(parent) -#define DBG_OBJ_ASSOC_CHILD(child) -#define DBG_OBJ_ASSOC(parent, child) -#define DBG_OBJ_SET_NUM(var, val) -#define DBG_OBJ_SET_STR(var, val) -#define DBG_OBJ_SET_PTR(var, val) -#define DBG_OBJ_ARRSET_NUM(var, ind, val) -#define DBG_OBJ_ARRSET_STR(var, ind, val) -#define DBG_OBJ_ARRSET_PTR(var, ind, val) -#define DBG_OBJ_COLOR(klass, color) +#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_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/identity.cc b/lout/identity.cc index 6fe679b4..61f59ace 100644 --- a/lout/identity.cc +++ b/lout/identity.cc @@ -17,8 +17,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - - #include "identity.hh" #include <stdio.h> @@ -41,6 +39,22 @@ IdentifiableObject::Class::Class (IdentifiableObject::Class *parent, int id, this->className = className; } +void IdentifiableObject::Class::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append ("<class "); + sb->append (className); + sb->append (" ("); + sb->appendInt (id); + sb->append (")"); + + if (parent) { + sb->append (", parent: "); + parent->intoStringBuffer (sb); + } + + sb->append (">"); +} + HashTable <ConstString, IdentifiableObject::Class> *IdentifiableObject::classesByName = new HashTable<ConstString, IdentifiableObject::Class> (true, true); @@ -55,7 +69,9 @@ IdentifiableObject::IdentifiableObject () void IdentifiableObject::intoStringBuffer(misc::StringBuffer *sb) { - sb->append("<instance of "); + sb->append("<instance "); + sb->appendPointer(this); + sb->append(" of "); sb->append(getClassName()); sb->append(">"); } @@ -78,6 +94,7 @@ void IdentifiableObject::registerName (const char *className, int *classId) } this->classId = klass->id; + *classId = klass->id; currentlyConstructedClass = klass; } diff --git a/lout/identity.hh b/lout/identity.hh index 1f0b4bdf..df42b204 100644 --- a/lout/identity.hh +++ b/lout/identity.hh @@ -106,6 +106,8 @@ private: const char *className; Class (Class *parent, int id, const char *className); + + void intoStringBuffer(misc::StringBuffer *sb); }; static container::typed::HashTable <object::ConstString, @@ -121,7 +123,7 @@ protected: public: IdentifiableObject (); - virtual void intoStringBuffer(misc::StringBuffer *sb); + void intoStringBuffer(misc::StringBuffer *sb); /** * \brief Returns the class identifier. diff --git a/lout/misc.hh b/lout/misc.hh index 6a04c89a..2bb28b12 100644 --- a/lout/misc.hh +++ b/lout/misc.hh @@ -515,6 +515,11 @@ public: * about memory management. */ inline void append(const char *str) { appendNoCopy(strdup(str)); } + inline void appendInt(int n) + { char buf[32]; sprintf (buf, "%d", n); append (buf); } + inline void appendPointer(void *p) + { char buf[32]; sprintf (buf, "%p", p); append (buf); } + inline void appendBool(bool b) { append (b ? "true" : "false"); } void appendNoCopy(char *str); const char *getChars(); void clear (); diff --git a/lout/object.cc b/lout/object.cc index 99b5902d..74328d22 100644 --- a/lout/object.cc +++ b/lout/object.cc @@ -94,7 +94,9 @@ const char *Object::toString() */ void Object::intoStringBuffer(misc::StringBuffer *sb) { - sb->append("<not further specified object>"); + sb->append("<not further specified object "); + sb->appendPointer(this); + sb->append(">"); } /** @@ -107,29 +109,44 @@ size_t Object::sizeOf() } // ---------------- -// Comparable +// Comparator // ---------------- +Comparator *Comparator::compareFunComparator = NULL; + /** * \brief This static method may be used as compare function for * qsort(3) and bsearch(3), for an array of Object* (Object*[] or * Object**). + * + * "compareFunComparator" should be set before. + * + * \todo Not reentrant. Consider switching to reentrant variants + * (qsort_r), and compare function with an additional argument. */ -int Comparable::compareFun(const void *p1, const void *p2) +int Comparator::compareFun(const void *p1, const void *p2) { - Comparable *c1 = *(Comparable**)p1; - Comparable *c2 = *(Comparable**)p2; + return compareFunComparator->compare (*(Object**)p1, *(Object**)p2); +} + +// ------------------------ +// StandardComparator +// ------------------------ - if (c1 && c2) - return ((c1)->compareTo(c2)); - else if (c1) +int StandardComparator::compare(Object *o1, Object *o2) +{ + if (o1 && o2) + return ((Comparable*)o1)->compareTo ((Comparable*)o2); + else if (o1) return 1; - else if (c2) + else if (o2) return -1; else return 0; } +StandardComparator standardComparator; + // ------------- // Pointer // ------------- diff --git a/lout/object.hh b/lout/object.hh index fd612863..5a4935c5 100644 --- a/lout/object.hh +++ b/lout/object.hh @@ -42,10 +42,11 @@ class Comparable: public Object { public: /** - * \brief Compare two objects c1 and c2. + * \brief Compare two objects, this and other. * - * Return a value < 0, when c1 is less than c2, a value > 0, when c1 - * is greater than c2, or 0, when c1 and c2 are equal. + * Return a value < 0, when this is less than other, a value > 0, + * when this is greater than other, or 0, when this and other are + * equal. * * If c1.equals(c2) (as defined in Object), c1.compareTo(c2) must * be 0, but, unlike you may expect, the reversed is not @@ -55,10 +56,43 @@ public: * care about. */ virtual int compareTo(Comparable *other) = 0; +}; + +/** + * \brief Used for other orders as the one defined by Comparable. + * + * Compared objects must not neccessary be instances of Comparable. + */ +class Comparator: public Object +{ +public: + /** + * \brief Compare two objects o1 and o2. + * + * Return a value < 0, when o1 is less than o2, a value > 0, when o1 + * is greater than o2, or 0, when o1 and o2 are equal. + * + * If o1.equals(o2) (as defined in Object), compare(o1, o2) must be + * 0, but, unlike you may expect, the reversed is not necessarily + * true. This method returns 0, if, according to the rules for + * sorting, there is no difference, but there may still be + * differences (not relevant for sorting), which "equals" will care + * about. + */ + virtual int compare(Object *o1, Object *o2) = 0; + static Comparator *compareFunComparator; static int compareFun(const void *p1, const void *p2); }; +class StandardComparator: public Comparator +{ +public: + int compare(Object *o1, Object *o2); +}; + +extern StandardComparator standardComparator; + /** * \brief An object::Object wrapper for void pointers. */ diff --git a/src/Makefile.am b/src/Makefile.am index 65a42cad..16398f21 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,9 @@ AM_CPPFLAGS= \ -I$(top_srcdir) \ -DDILLO_SYSCONF='"$(sysconfdir)/"' \ -DDILLO_DOCDIR='"$(docdir)/"' \ + -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/src"' @LIBJPEG_CPPFLAGS@ + AM_CFLAGS = @LIBPNG_CFLAGS@ AM_CXXFLAGS = @LIBPNG_CFLAGS@ @LIBFLTK_CXXFLAGS@ diff --git a/src/cssparser.cc b/src/cssparser.cc index fcff7688..07412a8d 100644 --- a/src/cssparser.cc +++ b/src/cssparser.cc @@ -72,6 +72,10 @@ static const char *const Css_border_width_enum_vals[] = { "thin", "medium", "thick", NULL }; +static const char *const Css_clear_enum_vals[] = { + "left", "right", "both", "none", NULL +}; + static const char *const Css_cursor_enum_vals[] = { "crosshair", "default", "pointer", "move", "e-resize", "ne-resize", "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize", @@ -84,6 +88,10 @@ static const char *const Css_display_enum_vals[] = { "table-cell", NULL }; +static const char *const Css_float_enum_vals[] = { + "none", "left", "right", NULL +}; + static const char *const Css_font_size_enum_vals[] = { "large", "larger", "medium", "small", "smaller", "xx-large", "xx-small", "x-large", "x-small", NULL @@ -121,6 +129,10 @@ static const char *const Css_list_style_type_enum_vals[] = { "katakana-iroha", "none", NULL }; +static const char *const Css_position_enum_vals[] = { + "static", "relative", "absolute", "fixed", NULL +}; + static const char *const Css_text_align_enum_vals[] = { "left", "right", "center", "justify", "string", NULL }; @@ -182,9 +194,9 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { Css_border_style_enum_vals}, {"border-top-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, Css_border_width_enum_vals}, - {"bottom", {CSS_TYPE_UNUSED}, NULL}, + {"bottom", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL}, {"caption-side", {CSS_TYPE_UNUSED}, NULL}, - {"clear", {CSS_TYPE_UNUSED}, NULL}, + {"clear", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_clear_enum_vals}, {"clip", {CSS_TYPE_UNUSED}, NULL}, {"color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL}, {"content", {CSS_TYPE_STRING, CSS_TYPE_UNUSED}, NULL}, @@ -194,7 +206,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { {"direction", {CSS_TYPE_UNUSED}, NULL}, {"display", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_display_enum_vals}, {"empty-cells", {CSS_TYPE_UNUSED}, NULL}, - {"float", {CSS_TYPE_UNUSED}, NULL}, + {"float", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_float_enum_vals}, {"font-family", {CSS_TYPE_SYMBOL, CSS_TYPE_UNUSED}, NULL}, {"font-size", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, Css_font_size_enum_vals}, @@ -239,9 +251,9 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { {"padding-left", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL}, {"padding-right", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL}, {"padding-top", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL}, - {"position", {CSS_TYPE_UNUSED}, NULL}, + {"position", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_position_enum_vals}, {"quotes", {CSS_TYPE_UNUSED}, NULL}, - {"right", {CSS_TYPE_UNUSED}, NULL}, + {"right", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL}, {"text-align", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_text_align_enum_vals}, {"text-decoration", {CSS_TYPE_MULTI_ENUM, CSS_TYPE_UNUSED}, Css_text_decoration_enum_vals}, @@ -249,7 +261,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { {"text-shadow", {CSS_TYPE_UNUSED}, NULL}, {"text-transform", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_text_transform_enum_vals}, - {"top", {CSS_TYPE_UNUSED}, NULL}, + {"top", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL}, {"unicode-bidi", {CSS_TYPE_UNUSED}, NULL}, {"vertical-align",{CSS_TYPE_ENUM, CSS_TYPE_UNUSED},Css_vertical_align_vals}, {"visibility", {CSS_TYPE_UNUSED}, NULL}, diff --git a/src/dillo.cc b/src/dillo.cc index d73b855a..4d96988f 100644 --- a/src/dillo.cc +++ b/src/dillo.cc @@ -382,6 +382,14 @@ int main(int argc, char **argv) DBG_OBJ_COLOR("#ffa0a0", "dw::core::*"); DBG_OBJ_COLOR("#ffe0a0", "dw::core::style::*"); + DBG_OBJ_COLOR ("#80ffa0", "dw::Image"); + DBG_OBJ_COLOR ("#f0ff80", "dw::Textblock"); + DBG_OBJ_COLOR ("#d0ff80", "dw::OutOfFlowMgr"); + DBG_OBJ_COLOR ("#e0ff80", "dw::AlignedTextblock"); + DBG_OBJ_COLOR ("#b0ff80", "dw::ListItem"); + DBG_OBJ_COLOR ("#80ff80", "dw::TableCell"); + DBG_OBJ_COLOR ("#80ffc0", "dw::Table"); + uint_t opt_id; uint_t options_got = 0; uint32_t xid = 0; diff --git a/src/html.cc b/src/html.cc index 53eadafe..3319b35e 100644 --- a/src/html.cc +++ b/src/html.cc @@ -361,7 +361,8 @@ static void Html_add_textblock(DilloHtml *html, int space) Textblock *textblock = new Textblock (prefs.limit_text_width); HT2TB(html)->addParbreak (space, html->wordStyle ()); - HT2TB(html)->addWidget (textblock, html->style ()); + HT2TB(html)->addWidget (textblock, html->style ()); /* Works also for floats + etc. */ HT2TB(html)->addParbreak (space, html->wordStyle ()); S_TOP(html)->textblock = html->dw = textblock; S_TOP(html)->hand_over_break = true; diff --git a/src/styleengine.cc b/src/styleengine.cc index 25e2e600..ffc0b0f6 100644 --- a/src/styleengine.cc +++ b/src/styleengine.cc @@ -607,6 +607,12 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props, computeValue (&attrs->hBorderSpacing, p->value.intVal,attrs->font); computeValue (&attrs->vBorderSpacing, p->value.intVal,attrs->font); break; + case CSS_PROPERTY_BOTTOM: + computeLength (&attrs->bottom, p->value.intVal, attrs->font); + break; + case CSS_PROPERTY_CLEAR: + attrs->clear = (ClearType) p->value.intVal; + break; case CSS_PROPERTY_COLOR: attrs->color = Color::create (layout, p->value.intVal); break; @@ -616,6 +622,12 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props, case CSS_PROPERTY_DISPLAY: attrs->display = (DisplayType) p->value.intVal; break; + case CSS_PROPERTY_FLOAT: + attrs->vloat = (FloatType) p->value.intVal; + break; + case CSS_PROPERTY_LEFT: + computeLength (&attrs->left, p->value.intVal, attrs->font); + break; case CSS_PROPERTY_LINE_HEIGHT: if (p->type == CSS_TYPE_ENUM) { //only valid enum value is "normal" attrs->lineHeight = dw::core::style::LENGTH_AUTO; @@ -667,6 +679,12 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props, case CSS_PROPERTY_PADDING_RIGHT: computeValue (&attrs->padding.right, p->value.intVal, attrs->font); break; + case CSS_PROPERTY_POSITION: + attrs->position = (Position) p->value.intVal; + break; + case CSS_PROPERTY_RIGHT: + computeLength (&attrs->right, p->value.intVal, attrs->font); + break; case CSS_PROPERTY_TEXT_ALIGN: attrs->textAlign = (TextAlignType) p->value.intVal; break; @@ -679,6 +697,9 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props, case CSS_PROPERTY_TEXT_TRANSFORM: attrs->textTransform = (TextTransform) p->value.intVal; break; + case CSS_PROPERTY_TOP: + computeLength (&attrs->top, p->value.intVal, attrs->font); + break; case CSS_PROPERTY_VERTICAL_ALIGN: attrs->valign = (VAlignType) p->value.intVal; break; diff --git a/test/Makefile.am b/test/Makefile.am index 156c8667..35689ec3 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -7,6 +7,7 @@ noinst_PROGRAMS = \ dw-anchors-test \ dw-example \ dw-find-test \ + dw-float-test \ dw-links \ dw-links2 \ dw-image-background \ @@ -52,6 +53,14 @@ dw_find_test_LDADD = \ $(top_builddir)/lout/liblout.a \ @LIBFLTK_LIBS@ +dw_float_test_SOURCES = dw_float_test.cc +dw_float_test_LDADD = \ + ../dw/libDw-widgets.a \ + ../dw/libDw-fltk.a \ + ../dw/libDw-core.a \ + ../lout/liblout.a \ + @LIBFLTK_LIBS@ + dw_links_SOURCES = dw_links.cc dw_links_LDADD = \ $(top_builddir)/dw/libDw-widgets.a \ diff --git a/test/containers.cc b/test/containers.cc index 993a299d..9b93f158 100644 --- a/test/containers.cc +++ b/test/containers.cc @@ -4,6 +4,16 @@ using namespace lout::object; using namespace lout::container::typed; +class ReverseComparator: public Comparator +{ +private: + Comparator *reversed; + +public: + ReverseComparator (Comparator *reversed) { this->reversed = reversed; } + int compare(Object *o1, Object *o2) { return - reversed->compare (o1, o2); } +}; + void testHashSet () { puts ("--- testHashSet ---"); @@ -38,6 +48,8 @@ void testHashTable () void testVector1 () { + ReverseComparator reverse (&standardComparator); + puts ("--- testVector (1) ---"); Vector<String> v (true, 1); @@ -47,6 +59,9 @@ void testVector1 () v.put (new String ("three")); puts (v.toString()); + v.sort (&reverse); + puts (v.toString()); + v.sort (); puts (v.toString()); } @@ -95,12 +110,31 @@ void testVector2 () } } +void testVector3 () +{ + // Regression test: resulted once incorrently (0, 2, 3), should + // result in (1, 2, 3). + + puts ("--- testVector (3) ---"); + + Vector<String> v (true, 1); + String k ("omega"); + + v.put (new String ("alpha")); + printf (" -> %d\n", v.bsearch (&k, false)); + v.put (new String ("beta")); + printf (" -> %d\n", v.bsearch (&k, false)); + v.put (new String ("gamma")); + printf (" -> %d\n", v.bsearch (&k, false)); +} + int main (int argc, char *argv[]) { testHashSet (); testHashTable (); testVector1 (); testVector2 (); + testVector3 (); return 0; } diff --git a/test/dw_float_test.cc b/test/dw_float_test.cc new file mode 100644 index 00000000..70273e89 --- /dev/null +++ b/test/dw_float_test.cc @@ -0,0 +1,145 @@ +#include <FL/Fl.H> +#include <FL/Fl_Window.H> + +#include "../dw/core.hh" +#include "../dw/fltkcore.hh" +#include "../dw/fltkviewport.hh" +#include "../dw/textblock.hh" + +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace dw::fltk; + +static Textblock *firstFloat; +static Style *wordStyle; + +static void addTextToFloatTimeout (void *data) +{ + printf("addTextToFloatTimeout\n"); + + const char *fWords[] = { "This", "is", "a", "float,", "which", "is", + "set", "aside", "from", "the", "main", + "text.", NULL }; + + for(int k = 0; fWords[k]; k++) { + firstFloat->addText(fWords[k], wordStyle); + firstFloat->addSpace(wordStyle); + } + + firstFloat->flush(); + + Fl::repeat_timeout (2, addTextToFloatTimeout, NULL); +} + +int main(int argc, char **argv) +{ + FltkPlatform *platform = new FltkPlatform (); + Layout *layout = new Layout (platform); + + Fl_Window *window = new Fl_Window(400, 600, "Dw Floats Example"); + window->begin(); + + FltkViewport *viewport = new FltkViewport (0, 0, 400, 600); + 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; + styleAttrs.font = core::style::Font::create (layout, &fontAttrs); + + styleAttrs.color = Color::create (layout, 0x000000); + styleAttrs.backgroundColor = Color::create (layout, 0xffffff); + + Style *widgetStyle = Style::create (&styleAttrs); + + styleAttrs.borderWidth.setVal (1); + styleAttrs.setBorderColor (Color::create (layout, 0x808080)); + styleAttrs.setBorderStyle (BORDER_DASHED); + styleAttrs.width = createAbsLength(100); + styleAttrs.vloat = FLOAT_LEFT; + Style *leftFloatStyle = Style::create (&styleAttrs); + + styleAttrs.width = createAbsLength(80); + styleAttrs.vloat = FLOAT_RIGHT; + Style *rightFloatStyle = Style::create (&styleAttrs); + + Textblock *textblock = new Textblock (false); + textblock->setStyle (widgetStyle); + layout->setWidget (textblock); + + widgetStyle->unref(); + + styleAttrs.borderWidth.setVal (0); + styleAttrs.width = LENGTH_AUTO; + styleAttrs.vloat = FLOAT_NONE; + styleAttrs.margin.setVal (0); + styleAttrs.backgroundColor = NULL; + + wordStyle = Style::create (&styleAttrs); + + for(int i = 1; i <= 10; i++) { + char buf[16]; + snprintf(buf, sizeof(buf), "%d%s", + i, (i == 1 ? "st" : (i == 2 ? "nd" : (i == 3 ? "rd" : "th")))); + + const char *words[] = { "This", "is", "the", buf, "paragraph.", + "Here", "comes", "some", "more", "text", + "to", "demonstrate", "word", "wrapping.", + NULL }; + + for(int j = 0; words[j]; j++) { + textblock->addText(words[j], wordStyle); + textblock->addSpace(wordStyle); + + if ((i == 3 || i == 5) && j == 8) { + textblock->addText("[float]", wordStyle); + textblock->addSpace(wordStyle); + + Textblock *vloat = new Textblock (false); + textblock->addWidget(vloat, i == 3 ? leftFloatStyle : rightFloatStyle); + + const char *fWords[] = { "This", "is", "a", "float,", "which", "is", + "set", "aside", "from", "the", "main", + "text.", NULL }; + + vloat->addText(i == 3 ? "Left:" : "Right:", wordStyle); + vloat->addSpace(wordStyle); + + for(int k = 0; fWords[k]; k++) { + vloat->addText(fWords[k], wordStyle); + vloat->addSpace(wordStyle); + } + + vloat->flush (); + + if(i == 3) + firstFloat = vloat; + } + } + + textblock->addParbreak(10, wordStyle); + } + + leftFloatStyle->unref(); + rightFloatStyle->unref(); + + textblock->flush (); + + window->resizable(viewport); + window->show(); + Fl::add_timeout (2, addTextToFloatTimeout, NULL); + int errorCode = Fl::run(); + + wordStyle->unref(); + delete layout; + + return errorCode; +} diff --git a/test/floats-and-absolute.html b/test/floats-and-absolute.html new file mode 100644 index 00000000..658ab16b --- /dev/null +++ b/test/floats-and-absolute.html @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> + <head> + <title>Floats And Absolute Positions</title> + <style type="text/css"> + div.main { + margin: 0 0 0 100px; + top: 3cm; + position: absolute; + } + + div.margin { + position: absolute; + top: 3cm; + width: 120px; + } + </style> + </head> + <body> + <h1>Floats And Absolute Positions</h1> + <div class="main"> + <img style="float: left" src="http://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Library_of_Ashurbanipal_The_Flood_Tablet.jpg/218px-Library_of_Ashurbanipal_The_Flood_Tablet.jpg" /> + <p>Sed ut perspiciatis, unde omnis iste natus error sit + voluptatem accusantium doloremque laudantium, totam rem + aperiam eaque ipsa, quae ab illo inventore veritatis et quasi + architecto beatae vitae dicta sunt, explicabo. nemo enim ipsam + voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, + sed quia consequuntur magni dolores eos, qui ratione + voluptatem sequi nesciunt, neque porro quisquam est, qui + dolorem ipsum, quia dolor sit, amet, consectetur, adipisci + velit, sed quia non numquam eius modi tempora incidunt, ut + labore et dolore magnam aliquam quaerat voluptatem. ut enim ad + minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi + consequatur? quis autem vel eum iure reprehenderit, qui in ea + voluptate velit esse, quam nihil molestiae consequatur, vel + illum, qui dolorem eum fugiat, quo voluptas nulla + pariatur?</p> + <p>Ἐν ἀρχῇ ἦν ὁ Λόγος, καὶ ὁ Λόγος ἦν πρὸς τὸν Θεόν, καὶ Θεὸς ἦν + ὁ Λόγος. Οὗτος ἦν ἐν ἀρχῇ πρὸς τὸν Θεόν. πάντα δι' αὐτοῦ + ἐγένετο, καὶ χωρὶς αὐτοῦ ἐγένετο οὐδὲ ἕν ὃ γέγονεν. ἐν αὐτῷ + ζωὴ ἦν, καὶ ἡ ζωὴ ἦν τὸ φῶς τῶν ἀνθρώπων. καὶ τὸ φῶς ἐν τῇ + σκοτίᾳ φαίνει, καὶ ἡ σκοτία αὐτὸ οὐ κατέλαβεν.</p> + </div> + <div class="margin">Margin, actually on the left side.</div> + <body> +</html> + + + diff --git a/test/floats-and-margins.html b/test/floats-and-margins.html new file mode 100644 index 00000000..ac64b9e1 --- /dev/null +++ b/test/floats-and-margins.html @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> + <head> + <title>Floats And Margins</title> + <style type="text/css"> + </style> + </head> + <body> + <p> + <img src="http://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Library_of_Ashurbanipal_The_Flood_Tablet.jpg/200px-Library_of_Ashurbanipal_The_Flood_Tablet.jpg" style="float: left; margin: 1cm 1cm 1cm 0" /> + Ἐν ἀρχῇ ἦν ὁ Λόγος, καὶ ὁ Λόγος ἦν πρὸς τὸν Θεόν, καὶ Θεὸς ἦν ὁ + Λόγος. Οὗτος ἦν ἐν ἀρχῇ πρὸς τὸν Θεόν. πάντα δι' αὐτοῦ ἐγένετο, + καὶ χωρὶς αὐτοῦ ἐγένετο οὐδὲ ἕν ὃ γέγονεν. ἐν αὐτῷ ζωὴ ἦν, καὶ ἡ + ζωὴ ἦν τὸ φῶς τῶν ἀνθρώπων. καὶ τὸ φῶς ἐν τῇ σκοτίᾳ φαίνει, καὶ + ἡ σκοτία αὐτὸ οὐ κατέλαβεν. + </p> + <p style="margin: 0 3cm"> + <img src="http://upload.wikimedia.org/wikipedia/commons/thumb/2/26/GilgameshTablet.jpg/200px-GilgameshTablet.jpg" style="float: right; margin: 1cm 0 1cm 1cm"/> + Sed ut perspiciatis, unde omnis iste natus error sit voluptatem + accusantium doloremque laudantium, totam rem aperiam eaque ipsa, + quae ab illo inventore veritatis et quasi architecto beatae + vitae dicta sunt, explicabo. nemo enim ipsam voluptatem, quia + voluptas sit, aspernatur aut odit aut fugit, sed quia + consequuntur magni dolores eos, qui ratione voluptatem sequi + nesciunt, neque porro quisquam est, qui dolorem ipsum, quia + dolor sit, amet, consectetur, adipisci velit, sed quia non + numquam eius modi tempora incidunt, ut labore et dolore magnam + aliquam quaerat voluptatem. ut enim ad minima veniam, quis + nostrum exercitationem ullam corporis suscipit laboriosam, nisi + ut aliquid ex ea commodi consequatur? quis autem vel eum iure + reprehenderit, qui in ea voluptate velit esse, quam nihil + molestiae consequatur, vel illum, qui dolorem eum fugiat, quo + voluptas nulla pariatur? + </p> + <body> +</html> + + + diff --git a/test/floats-table.html b/test/floats-table.html new file mode 100644 index 00000000..77d77563 --- /dev/null +++ b/test/floats-table.html @@ -0,0 +1,24 @@ +<p>Demonstrating how to include floats into witdth extremes. + +<table> + <tr> + <td style="border: 1px dashed black"> + <div style="float:left; border: 1px dashed black">Somelongwordwhichmustnotbebrokensotakingmuchspaceinatablecolumn</div> + Some short text. + <td style="border: 1px dashed black"> + Sed ut perspiciatis, unde omnis iste natus error sit voluptatem + accusantium doloremque laudantium, totam rem aperiam eaque ipsa, + quae ab illo inventore veritatis et quasi architecto beatae + vitae dicta sunt, explicabo. nemo enim ipsam voluptatem, quia + voluptas sit, aspernatur aut odit aut fugit, sed quia + consequuntur magni dolores eos, qui ratione voluptatem sequi + nesciunt, neque porro quisquam est, qui dolorem ipsum, quia + dolor sit, amet, consectetur, adipisci velit, sed quia non + numquam eius modi tempora incidunt, ut labore et dolore magnam + aliquam quaerat voluptatem. ut enim ad minima veniam, quis + nostrum exercitationem ullam corporis suscipit laboriosam, nisi + ut aliquid ex ea commodi consequatur? quis autem vel eum iure + reprehenderit, qui in ea voluptate velit esse, quam nihil + molestiae consequatur, vel illum, qui dolorem eum fugiat, quo + voluptas nulla pariatur? +</table> diff --git a/test/floats-worm.html b/test/floats-worm.html new file mode 100644 index 00000000..9e050933 --- /dev/null +++ b/test/floats-worm.html @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> + <head> + <title>Floats and iterators: search for "worm" and pay attention + to the order.</title> + <style type="text/css"> + .float1, .float2 { + padding: 0.5em; + border: 1px dashed #404040; + width: 30%; + } + .float1 { + float: left; + margin: 0.5em 0.5em 0.5em 0; + } + .float2 { + float: right; + margin: 0.5em 0 0.5em 0.5em; + } + + </style> + </head> + <body> + <div class="float1">1: apple apple worm apple apple</div> + <p>2: apple apple apple apple apple apple apple apple apple apple + apple apple apple apple apple apple apple apple apple apple + apple apple apple apple worm apple apple apple apple apple apple + apple apple apple apple apple apple apple apple apple apple + apple apple apple apple apple apple apple apple</p> + <div class="float2">3: apple apple worm apple apple</div> + <p>4: apple apple apple apple apple apple apple apple apple apple + apple apple apple apple apple apple apple apple apple apple + apple apple apple apple worm apple apple apple apple apple apple + apple apple apple apple apple apple apple apple apple apple + apple apple apple apple apple apple apple apple</p> + <div class="float1">5: apple apple worm apple apple</div> + <body> +</html> + + + diff --git a/test/floats1.html b/test/floats1.html new file mode 100644 index 00000000..eae30c4a --- /dev/null +++ b/test/floats1.html @@ -0,0 +1,46 @@ +<div>First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First +paragraph. <div style="float: left; border: 1px dashed black">Left +float. Left float. Left float. Left float. Left float. Left +float. Left float. Left float. Left float. Left float. Left +float. Left float. Left float. Left float. Left float. Left +float. Left float. Left float. Left float. Left float.</div> First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph.</div> +<div><div>Second paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph.<div style="float: +right; border: 1px dashed black">Right float. Right float. Right +float. Right float. Right float. Right float.</div> Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph.</div></div> diff --git a/test/floats2.html b/test/floats2.html new file mode 100644 index 00000000..b7978706 --- /dev/null +++ b/test/floats2.html @@ -0,0 +1,17 @@ +Sed ut perspiciatis, unde omnis iste natus error sit voluptatem +accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae +ab illo inventore veritatis et quasi architecto beatae vitae dicta +sunt, explicabo. +<div style="float:left; border: 1px dashed black">Some text in a +float.<br /><img src="http://www.dillo.org/dw/html/not-so-simple-container.png" /></div> +nemo enim ipsam voluptatem, quia voluptas sit, +aspernatur aut odit aut fugit, sed quia consequuntur magni dolores +eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, +qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit, +sed quia non numquam eius modi tempora incidunt, ut labore et dolore +magnam aliquam quaerat voluptatem. ut enim ad minima veniam, quis +nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut +aliquid ex ea commodi consequatur? quis autem vel eum iure +reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae +consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla +pariatur? diff --git a/test/floats3.html b/test/floats3.html new file mode 100644 index 00000000..4d57e453 --- /dev/null +++ b/test/floats3.html @@ -0,0 +1,16 @@ +<p>Sed ut perspiciatis, unde omnis iste natus error sit voluptatem +accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae +ab illo inventore veritatis et quasi architecto beatae vitae dicta +sunt, explicabo.</p> +<div style="float:left; border: 1px dashed black">Some text in a +float.<br /><img src="http://www.dillo.org/dw/html/not-so-simple-container.png" /></div> +<p>nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit +aut fugit, sed quia consequuntur magni dolores eos, qui ratione +voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem +ipsum, quia dolor sit, amet, consectetur, adipisci velit, sed quia non +numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam +quaerat voluptatem. ut enim ad minima veniam, quis nostrum +exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex +ea commodi consequatur? quis autem vel eum iure reprehenderit, qui in +ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, +qui dolorem eum fugiat, quo voluptas nulla pariatur?</p> diff --git a/test/floats4.html b/test/floats4.html new file mode 100644 index 00000000..965ed68d --- /dev/null +++ b/test/floats4.html @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> + <head> + <title>Floats 4</title> + <style type="text/css"> + .border { + background-color: #e0e0ff; + padding: 1cm; + } + .float1, .float2 { + margin: 1cm; + padding: 1cm; + border: 1px dashed red; + background-color: #f0fff0; + float: right; + } + .float1 { + float: left; + } + .float2 { + float: right; + } + + .wide { + margin: 1cm 0; + padding: 1cm; + border: 1px dashed red; + background-color: #ffffd0; + width: 40cm; + } + </style> + </head> + <body class="border"> + <div class="float2">Some text in a float.</div> + + <p>Sed ut perspiciatis, unde omnis iste natus error sit voluptatem + accusantium doloremque laudantium, totam rem aperiam eaque ipsa, + quae ab illo inventore veritatis et quasi architecto beatae + vitae dicta sunt, explicabo. nemo enim ipsam voluptatem, quia + voluptas sit, aspernatur aut odit aut fugit, sed quia + consequuntur magni dolores eos, qui ratione voluptatem sequi + nesciunt, neque porro quisquam est, qui dolorem ipsum, quia + dolor sit, amet, consectetur, adipisci velit, sed quia non + numquam eius modi tempora incidunt, ut labore et dolore magnam + aliquam quaerat voluptatem. ut enim ad minima veniam, quis + nostrum exercitationem ullam corporis suscipit laboriosam, nisi + ut aliquid ex ea commodi consequatur? quis autem vel eum iure + reprehenderit, qui in ea voluptate velit esse, quam nihil + molestiae consequatur, vel illum, qui dolorem eum fugiat, quo + voluptas nulla pariatur?</p> + + <table class="wide"><tbody><tr><td>Ἐν ἀρχῇ ἦν ὁ Λόγος, καὶ ὁ Λόγος + ἦν πρὸς τὸν Θεόν, καὶ Θεὸς ἦν ὁ Λόγος. Οὗτος ἦν ἐν ἀρχῇ πρὸς τὸν + Θεόν. πάντα δι' αὐτοῦ ἐγένετο, καὶ χωρὶς αὐτοῦ ἐγένετο οὐδὲ ἕν ὃ + γέγονεν. ἐν αὐτῷ ζωὴ ἦν, καὶ ἡ ζωὴ ἦν τὸ φῶς τῶν ἀνθρώπων. καὶ + τὸ φῶς ἐν τῇ σκοτίᾳ φαίνει, καὶ ἡ σκοτία αὐτὸ οὐ + κατέλαβεν.</tbody></tr></td></table> + <body> +</html> + + + diff --git a/test/floats5.html b/test/floats5.html new file mode 100644 index 00000000..c8c6564a --- /dev/null +++ b/test/floats5.html @@ -0,0 +1,16 @@ +Sed ut perspiciatis, unde omnis iste natus error sit voluptatem +accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae +ab illo inventore veritatis et quasi architecto beatae vitae dicta +sunt, explicabo. +<div style="float:left; border: 1px dashed black"><img src="http://www.dillo.org/Icons/ProgramIcon16.png" /></div> +nemo enim ipsam voluptatem, quia voluptas sit, +aspernatur aut odit aut fugit, sed quia consequuntur magni dolores +eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, +qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit, +sed quia non numquam eius modi tempora incidunt, ut labore et dolore +magnam aliquam quaerat voluptatem. ut enim ad minima veniam, quis +nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut +aliquid ex ea commodi consequatur? quis autem vel eum iure +reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae +consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla +pariatur? |