diff options
52 files changed, 4972 insertions, 503 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/doc/Makefile.am b/doc/Makefile.am index dad95629..591ede4c 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -26,6 +26,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 \ Cache.txt \ Cookies.txt \ Dillo.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.doc b/doc/dw-out-of-flow.doc new file mode 100644 index 00000000..1cedbdda --- /dev/null +++ b/doc/dw-out-of-flow.doc @@ -0,0 +1,177 @@ +/** \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 + +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 preiosly calculated size. Each widget must allocate its + children, although the condition for the toplevel widget is not + necessary, but 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, but generally the sibling of an +anchestor) to determine the borders, which is done in +dw::Textblock::sizeRequestImpl. + +In this situation, it is important not to call, within +dw::Textblock::sizeRequestImpl of this textblock, +dw::core::Widget::sizeRequest for the float. Instead, ... (?) + +(Actually, dw::Textblock::sizeRequestImpl calls +dw::core::Widget::sizeRequest for the float, which conflicts with the +rules defined in \ref dw-widget-sizes; but actually should not cause +conflicts. Should be revised.) + +**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::getYWidget (called by the latter), + where these three cases are distinguished; +- dw::OutOfFlowMgr::sizeAllocate, which is called by the containing + block. + +Implementation overview +======================= + +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. + + +*/
\ No newline at end of file diff --git a/doc/dw-widget-sizes.doc b/doc/dw-widget-sizes.doc index 419a4a73..86f46561 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,122 @@ 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. + +<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..47ec5275 100644 --- a/dw/Makefile.am +++ b/dw/Makefile.am @@ -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 4390e3ee..e86116f1 100644 --- a/dw/findtext.cc +++ b/dw/findtext.cc @@ -94,7 +94,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 */ @@ -126,7 +126,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 f7bd7597..ff121250 100644 --- a/dw/layout.cc +++ b/dw/layout.cc @@ -245,6 +245,8 @@ Layout::Layout (Platform *platform) topLevel = NULL; widgetAtPoint = NULL; + queueResizeList = new typed::Vector<Widget> (4, false); + DBG_OBJ_CREATE ("dw::core::Layout"); bgColor = NULL; @@ -277,6 +279,9 @@ Layout::Layout (Platform *platform) selectionState.setLayout(this); + queueResizeCounter = sizeAllocateCounter = sizeRequestCounter = + getExtremesCounter = 0; + layoutImgRenderer = NULL; } @@ -303,6 +308,7 @@ Layout::~Layout () topLevel = NULL; delete w; } + delete queueResizeList; delete platform; delete view; delete anchorsTable; @@ -318,12 +324,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 () @@ -332,6 +344,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; @@ -774,11 +787,38 @@ void Layout::setBgImage (style::StyleImage *bgImage, void Layout::resizeIdle () { + enterQueueResize (); + //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; @@ -786,8 +826,14 @@ void Layout::resizeIdle () if (topLevel) { Requisition requisition; Allocation allocation; - + topLevel->sizeRequest (&requisition); + + // 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; @@ -827,6 +873,10 @@ void Layout::resizeIdle () } updateAnchor (); + + //printf ("Layout::resizeIdle end\n"); + + leaveQueueResize (); } void Layout::setSizeHints () @@ -1162,8 +1212,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..64274714 100644 --- a/dw/layout.hh +++ b/dw/layout.hh @@ -153,6 +153,7 @@ private: Platform *platform; View *view; Widget *topLevel, *widgetAtPoint; + lout::container::typed::Vector<Widget> *queueResizeList; /* The state, which must be projected into the view. */ style::Color *bgColor; @@ -236,6 +237,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 enterQueueResize () { resizeIdleCounter++; } + void leaveQueueResize () { resizeIdleCounter--; } + public: Layout (Platform *platform); ~Layout (); diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc new file mode 100644 index 00000000..49babdab --- /dev/null +++ b/dw/outofflowmgr.cc @@ -0,0 +1,1720 @@ +/* + * 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" + +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::Float::Float (OutOfFlowMgr *oofm, Widget *widget, + Textblock *generatingBlock, int externalIndex) +{ + this->oofm = oofm; + this->widget = 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 (widget); + + if (widget) { + sb->append (" ("); + sb->append (widget->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 (" }"); +} + +int OutOfFlowMgr::Float::compareTo(Comparable *other) +{ + Float *otherFloat = (Float*)other; + + if (oofm->wasAllocated (generatingBlock)) { + assert (oofm->wasAllocated (otherFloat->generatingBlock)); + return yForContainer() - otherFloat->yForContainer(); + } else { + assert (generatingBlock == otherFloat->generatingBlock); + return yReal - otherFloat->yReal; + } +} + +int OutOfFlowMgr::Float::yForTextblock (Textblock *textblock, int y) +{ + if (oofm->wasAllocated (generatingBlock)) { + assert (oofm->wasAllocated (textblock)); + return oofm->getAllocation(generatingBlock)->y + y + - oofm->getAllocation(textblock)->y; + } else { + assert (textblock == generatingBlock); + return y; + } +} + +int OutOfFlowMgr::Float::yForContainer (int y) +{ + assert (oofm->wasAllocated (generatingBlock)); + return y + oofm->getAllocation(generatingBlock)->y - + oofm->getAllocation(oofm->containingBlock)->y; +} + +bool OutOfFlowMgr::Float::covers (Textblock *textblock, int y, int h) +{ + int reqy, fly; // either widget or canvas coordinates + if (oofm->wasAllocated (generatingBlock)) { + assert (oofm->wasAllocated (textblock)); + reqy = oofm->getAllocation(textblock)->y + y; + fly = oofm->getAllocation(generatingBlock)->y + yReal; + } else { + assert (textblock == generatingBlock); + reqy = y; + fly = yReal; + } + + oofm->ensureFloatSize (this); + + //printf ("[%p] COVERS (%p, %d, %d) => %d + %d + %d > %d && %d < %d + %d? " + // "%s.\n", oofm->containingBlock, textblock, y, h, fly, size.ascent, + // size.descent, reqy, fly, reqy, h, + // (fly + size.ascent + size.descent > reqy && fly < reqy + h) ? + // "yes" : "no"); + + return fly + size.ascent + size.descent > reqy && fly < reqy + h; +} + +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; + + //printf ("[%p] comparing (%p, %d) with (%p, %d) ...\n", + // oofm->containingBlock, f1->generatingBlock, f1->externalIndex, + // f2->generatingBlock, f2->externalIndex); + + if (f1->generatingBlock == f2->generatingBlock) { + //printf (" (a) generating blocks equal => %d - %d = %d\n", + // f1->externalIndex, f2->externalIndex, + // f1->externalIndex - f2->externalIndex); + return f1->externalIndex - f2->externalIndex; + } else { + TBInfo *t1 = oofm->getTextblock (f1->generatingBlock), + *t2 = oofm->getTextblock (f2->generatingBlock); + + for (TBInfo *t = t1; t != NULL; t = t->parent) + if (t->parent == t2) { + //printf (" (b) %p is an achestor of %p; direct child is %p (%d)" + // " => %d - %d = %d\n", t2->textblock, t1->textblock, + // t->textblock, t->parentExtIndex, t->parentExtIndex, + // f2->externalIndex, t->parentExtIndex - f2->externalIndex); + return t->parentExtIndex - f2->externalIndex; + } + + for (TBInfo *t = t2; t != NULL; t = t->parent) + if (t->parent == t1) { + //printf (" (c) %p is an achestor of %p; direct child is %p (%d)" + // " => %d - %d = %d\n", t1->textblock, t2->textblock, + // t->textblock, t->parentExtIndex, f1->externalIndex, + // t->parentExtIndex, f1->externalIndex - t->parentExtIndex); + return f1->externalIndex - t->parentExtIndex; + } + + //printf (" (d) other => %d - %d = %d\n", + // t1->index, t2->index, t1->index - t2->index); + return t1->index - t2->index; + } +} + +int OutOfFlowMgr::SortedFloatsVector::findFloatIndex (Textblock *lastGB, + int lastExtIndex) +{ + Float key (oofm, NULL, lastGB, lastExtIndex); + Float::CompareGBAndExtIndex cmp (oofm); + int i = bsearch (&key, false, &cmp); + + // 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 (cmp.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); + //} + + return r; +} + +int OutOfFlowMgr::SortedFloatsVector::find (Textblock *textblock, int y, + int start, int end) +{ + Float key (oofm, NULL, NULL, 0); + key.generatingBlock = textblock; + key.yReal = y; + return bsearch (&key, false, start, end); +} + +int OutOfFlowMgr::SortedFloatsVector::findFirst (Textblock *textblock, + int y, int h, + Textblock *lastGB, + int lastExtIndex) +{ + int last = findFloatIndex (lastGB, lastExtIndex); + assert (last < size()); + int i = find (textblock, y, 0, last); + + //printf ("[%p] FIND (%s, %p, allocated: %s, %d, %p, %d) => last = %d, " + // "result = %d (of %d)\n", oofm->containingBlock, + // type == GB ? "GB" : "CB", textblock, + // oofm->wasAllocated (textblock) ? "true" : "false", y, lastGB, + // lastExtIndex, last, i, size()); + + // 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)) + return i - 1; + else if (i <= last && get(i)->covers (textblock, y, h)) + return i; + else + return -1; +} + +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) +{ + this->textblock = 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) +{ + //printf ("OutOfFlowMgr::OutOfFlowMgr\n"); + + 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) +{ + this->containingBlockAllocation = *containingBlockAllocation; + containingBlockWasAllocated = true; +} + +void OutOfFlowMgr::sizeAllocateEnd () +{ + //printf ("[%p] SIZE_ALLOCATE_END: leftFloatsMark = %d, " + // "rightFloatsMark = %d\n", + // containingBlock, leftFloatsMark, rightFloatsMark); + + // 1. Move floats from GB lists to the one CB list. + moveFromGBToCB (LEFT); + moveFromGBToCB (RIGHT); + + // 2. Floats and absolutely positioned blocks have to be allocated + sizeAllocateFloats (LEFT); + sizeAllocateFloats (RIGHT); + sizeAllocateAbsolutelyPositioned (); + + // 3. Textblocks have already been allocated, but we (i) check + // allocation change of textblocks, and (ii) 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(); + + Allocation *tbAllocation = getAllocation (tb); + int xCB = tbAllocation->x - containingBlockAllocation.x; + int yCB = tbAllocation->y - containingBlockAllocation.y; + int width = tbAllocation->width; + int height = tbAllocation->ascent + tbAllocation->descent; + + // (i) Check allocation change of textblocks. + if ((!tbInfo->wasAllocated || tbInfo->xCB != xCB || tbInfo->yCB != yCB || + tbInfo->width != width || tbInfo->height != height)) { + // Changed: change borders when covered by floats. + int oldPos, newPos; + Widget *oldFloat, *newFloat; + // To calculate the minimum, both allocations, old and new, + // have to be tested. + + // Old allocation: + bool c1 = isTextblockCoveredByFloats (tb, + tbInfo->xCB + + containingBlockAllocation.x, + tbInfo->yCB + + containingBlockAllocation.y, + tbInfo->width, tbInfo->height, + &oldPos, &oldFloat); + // new allocation: + int c2 = isTextblockCoveredByFloats (tb, tbAllocation->x, + tbAllocation->y, width, + height, &newPos, &newFloat); + if (c1 || c2) { + if (!c1) + tb->borderChanged (newPos, newFloat); + else if (!c2) + tb->borderChanged (oldPos, oldFloat); + else { + if (oldPos < newPos) + tb->borderChanged (oldPos, oldFloat); + else + tb->borderChanged (newPos, newFloat); + } + } + } + + // TODO Comment and re-number. + checkChangedFloatSizes (); + + // (ii) store some information for later use. + tbInfo->wasAllocated = true; + tbInfo->xCB = xCB; + tbInfo->yCB = yCB; + tbInfo->width = width; + tbInfo->height = height; + } +} + +bool OutOfFlowMgr::isTextblockCoveredByFloats (Textblock *tb, int tbx, int tby, + int tbWidth, int tbHeight, + int *minFloatPos, + Widget **minFloat) +{ + int leftMinPos, rightMinPos; + Widget *leftMinFloat, *rightMinFloat; + bool c1 = isTextblockCoveredByFloats (leftFloatsCB, tb, tbx, tby, + tbWidth, tbHeight, &leftMinPos, + &leftMinFloat); + bool c2 = isTextblockCoveredByFloats (rightFloatsCB, tb, tbx, tby, + tbWidth, tbHeight, &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; + } + } + } + + return c1 || c2; +} + +bool OutOfFlowMgr::isTextblockCoveredByFloats (SortedFloatsVector *list, + Textblock *tb, int tbx, int tby, + int tbWidth, int tbHeight, + int *minFloatPos, + Widget **minFloat) +{ + bool covered = false; + + for (int i = 0; i < list->size(); i++) { + // TODO binary search + Float *vloat = list->get(i); + int floatPos; + + // TODO Clarify the old condition: tb != v->generatingBlock. Neccessary? + if (tb != vloat->generatingBlock && + isTextblockCoveredByFloat (vloat, tb, tbx, tby, tbWidth, tbHeight, + &floatPos)) { + if (!covered || floatPos < *minFloatPos) { + *minFloatPos = floatPos; + *minFloat = vloat->widget; + } + covered = true; + } + + // All floarts are searched, to find the minimum. TODO: Are + // floats sorted, so this can be shortened? (The first is the + // minimum?) + } + + return covered; +} + +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::checkChangedFloatSizes () +{ + checkChangedFloatSizes (leftFloatsCB); + checkChangedFloatSizes (rightFloatsCB); +} + +void OutOfFlowMgr::checkChangedFloatSizes (SortedFloatsVector *list) +{ + // TODO (i) Comment (ii) linear search? + for (int i = 0; i < list->size(); i++) { + // TODO binary search + Float *vloat = list->get(i); + + if (vloat->sizeChangedSinceLastAllocation && + wasAllocated (vloat->generatingBlock)) { + //printf ("=== start checking textblocks ===\n"); + + for (lout::container::typed::Iterator<TypedPointer <Textblock> > it = + tbInfosByTextblock->iterator (); + it.hasNext (); ) { + Textblock *tb = it.getNext()->getTypedValue(); + if (wasAllocated (tb)) { + Allocation *tba = getAllocation (tb); + int floatPos; + + if (isTextblockCoveredByFloat + (vloat, tb, tba->x - containingBlockAllocation.x, + tba->y - containingBlockAllocation.y, + tba->width, tba->ascent + tba->descent, &floatPos)) { + //printf (" ---> yes: %p (parent: %p)\n", tb, + // tb->getParent()); + tb->borderChanged (floatPos, vloat->widget); + } //else + // printf (" ---> not covered: %p (parent: %p)\n", tb, + // tb->getParent()); + } //else + // printf (" ---> not allocated: %p (parent: %p)\n", tb, + // tb->getParent()); + } + + //printf ("=== end checking textblocks ===\n"); + + vloat->sizeChangedSinceLastAllocation = 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++) { + // TODO Missing: check newly calculated positions, collisions, + // and queue resize, when neccessary. TODO: See step 2? + + Float *vloat = list->get(i); + ensureFloatSize (vloat); + + Allocation *gbAllocation = getAllocation(vloat->generatingBlock); + Allocation *cbAllocation = getAllocation(containingBlock); + // In some cases, the actual (allocated) width is too large; we + // use the "available" width here. + int gbWidth = + min (gbAllocation->width, vloat->generatingBlock->getAvailWidth()); + + Allocation childAllocation; + + switch (side) { + case LEFT: + // Left floats are always aligned on the left side of the + // generator (content, not allocation). + childAllocation.x = gbAllocation->x + + vloat->generatingBlock->getStyle()->boxOffsetX(); + break; + + case RIGHT: + // 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). + childAllocation.x = + max (gbAllocation->x + gbWidth - vloat->size.width + - vloat->generatingBlock->getStyle()->boxRestWidth(), + // Do not exceed CB allocation: + cbAllocation->x); + break; + } + + childAllocation.y = gbAllocation->y + vloat->yReal; + childAllocation.width = vloat->size.width; + childAllocation.ascent = vloat->size.ascent; + childAllocation.descent = vloat->size.descent; + + vloat->widget->sizeAllocate (&childAllocation); + + //printf (" allocate %s float #%d -> (%d, %d), %d x (%d + %d)\n", + // side == LEFT ? "left" : "right", i, childAllocation.x, + // childAllocation.y, childAllocation.width, + // childAllocation.ascent, childAllocation.descent); + } +} + + + +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->widget->intersects (area, &childArea)) + vloat->widget->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->wasAllocated = false; + tbInfo->index = tbInfos->size(); + + tbInfos->put (tbInfo); + tbInfosByTextblock->put (new TypedPointer<Textblock> (textblock), tbInfo); +} + +void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, + int 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) +{ + //printf ("[%p] MARK_SIZE_CHANGE (%d)\n", containingBlock, ref); + + 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; + + // Effects take place in ensureFloatSize. + } else if (isRefAbsolutelyPositioned (ref)) { + int i = getAbsolutelyPositionedIndexFromRef (ref); + absolutelyPositioned->get(i)->dirty = true; + } else + assertNotReached(); +} + + +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->widget->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) +{ + assert (yReq >= 0); + + Float *vloat = findFloatByWidget(widget); + + //printf ("[%p] TELL_FLOAT_POSITION (%p (%s), %d)\n", + // containingBlock, widget, widget->getClassName (), yReq); + //printf (" this float: %s\n", vloat->toString()); + + SortedFloatsVector *listSame, *listOpp; + getFloatsLists (vloat, &listSame, &listOpp); + ensureFloatSize (vloat); + //printf (" ensured size: %s\n", vloat->toString()); + + //printf (" all floats on same side (%s):\n", + // listSame->type == SortedFloatsVector::GB ? "GB" : "CB"); + //for (int i = 0; i < listSame->size(); i++) + // printf (" %d: %p, %s\n", i, listSame->get(i), + // listSame->get(i)->toString()); + + int oldY = vloat->yReal; + + // "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 && + collides (vloat, listSame->get (vloat->index - 1), &yRealNew)) { + vloat->yReal = yRealNew; + //printf (" collides; yReal = %d\n", vloat->yReal); + } + + // 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 (collides (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; + } + } + + // No call neccessary when yReal has not changed. (Notice that + // checking for yReq is wrong: yReq may remain the same, when yReal + // changes, e. g. when previous float has changes its size. + if (vloat->yReal != oldY) + checkCoveragePosChanged (vloat, oldY); +} + +bool OutOfFlowMgr::collides (Float *vloat, Float *other, int *yReal) +{ + ensureFloatSize (other); + int otherH = other->size.ascent + other->size.descent; + + if (wasAllocated (other->generatingBlock)) { + assert (wasAllocated (vloat->generatingBlock)); + + if (getAllocation(vloat->generatingBlock)->y + vloat->yReal < + getAllocation(other->generatingBlock)->y + other->yReal + otherH) { + *yReal = + getAllocation(other->generatingBlock)->y + other->yReal + + otherH - getAllocation(vloat->generatingBlock)->y; + return true; + } + } else { + assert (vloat->generatingBlock == other->generatingBlock); + + if (vloat->yReal < other->yReal + otherH) { + *yReal = other->yReal + otherH; + return true; + } + } + + return false; +} + +void OutOfFlowMgr::checkCoveragePosChanged (Float *vloat, int oldY) +{ + // Only this float has been changed (see tellFloatPosition), so + // only this float has to be tested against all textblocks. + if (wasAllocated (vloat->generatingBlock)) { + // TODO This (and similar code) is not very efficient. + for (lout::container::typed::Iterator<TypedPointer <Textblock> > it = + tbInfosByTextblock->iterator (); it.hasNext (); ) { + TypedPointer <Textblock> *key = it.getNext (); + Textblock *textblock = key->getTypedValue(); + + if (textblock != vloat->generatingBlock && wasAllocated (textblock)) { + Allocation *tba = getAllocation (textblock); + Allocation *gba = getAllocation (vloat->generatingBlock); + int tby1 = tba->y; + int tby2 = tba->y + tba->ascent + tba->descent; + + int flh = + vloat->dirty ? 0 : vloat->size.ascent + vloat->size.descent; + int y1old = gba->y + oldY, y2old = y1old + flh; + int y1new = gba->y + vloat->yReal, y2new = y1new + flh; + + bool covered = + (y2old > tby1 && y1old < tby2) || (y2new > tby1 && y1new < tby2); + + if (covered) { + int yTextblock = gba->y + min (oldY, vloat->yReal) - tba->y; + textblock->borderChanged (yTextblock, vloat->widget); + } + } + } + } +} + +void OutOfFlowMgr::getFloatsLists (Float *vloat, SortedFloatsVector **listSame, + SortedFloatsVector **listOpp) +{ + TBInfo *tbInfo = getTextblock (vloat->generatingBlock); + + switch (vloat->widget->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; + } + 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; + } + break; + + default: + assertNotReached(); + } +} + +void OutOfFlowMgr::getSize (int cbWidth, int cbHeight, + int *oofWidth, int *oofHeight) +{ + // CbWidth and cbHeight *do* contain padding, border, and + // margin. See call in dw::Textblock::sizeRequest. (Notice that + // this has changed from an earlier version.) + + // Also notice that Float::y includes margins etc. + + // TODO Is it correct to add padding, border, and margin to the + // containing block? Check CSS spec. + + //printf ("[%p] GET_SIZE (%d, %d, ...): %d / %d floats...\n", + // containingBlock, cbWidth, cbHeight, + // leftFloatsCB->size(), rightFloatsCB->size()); + + int oofWidthAbsPos, oofHeightAbsPos; + getAbsolutelyPositionedSize (&oofWidthAbsPos, &oofHeightAbsPos); + + int oofWidthtLeft, oofWidthRight, oofHeightLeft, oofHeightRight; + getFloatsSize (leftFloatsCB, LEFT, &oofWidthtLeft, &oofHeightLeft); + getFloatsSize (rightFloatsCB, RIGHT, &oofWidthRight, &oofHeightRight); + + *oofWidth = max (oofWidthtLeft, oofWidthRight, oofWidthAbsPos); + *oofHeight = max (oofHeightLeft, oofHeightRight, oofHeightAbsPos); + + //printf (" => %d x %d => %d x %d (%d / %d)\n", + // cbWidth, cbHeight, *oofWidth, *oofHeight, + // oofHeightLeft, oofHeightRight); +} + +void OutOfFlowMgr::getFloatsSize (SortedFloatsVector *list, Side side, + int *width, int *height) +{ + *width = 0; + *height = containingBlock->getStyle()->boxDiffHeight(); + + // Idea for a faster implementation: find the last float; this + // should be the relevant one, since the list is sorted. + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + ensureFloatSize (vloat); + + // 1. Height: May play a role when the float is too wide. + // Minimum taken, since the relevant case is when the containing + // block is otherwise not wide enough. + int borderDiff = getMinBorderDiff (vloat, side); + *width = max (*width, vloat->size.width + borderDiff); + + // 2. Height: Plays a role for floats hanging over at the bottom + // of the page. + + // Notice that all positions are relative to the generating + // block, but we need them relative to the containing block. + + // Position of generating block, relative 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 yGBinCB; + + if (vloat->generatingBlock == containingBlock) + // Simplest case: the generator is the container. + yGBinCB = 0; + else { + if (wasAllocated (containingBlock)) { + if (wasAllocated (vloat->generatingBlock)) + // Simple case: both containing block and generating + // block are defined. + yGBinCB = getAllocation(vloat->generatingBlock)->y + - containingBlock->getAllocation()->y; + else + // Generating block not yet allocation; the next + // allocation will, when necessary, trigger + // sizeRequest. (TODO: Is this really the case?) + yGBinCB = 0; + } else + // Nothing can be done now, but the next allocation + // will trigger sizeAllocate. (TODO: Is this really the + // case?) + yGBinCB = 0; + } + + *height = + max (*height, + yGBinCB + vloat->yReal + vloat->size.ascent + vloat->size.descent + + containingBlock->getStyle()->boxRestHeight()); + //printf (" float %d: (%d + %d) + (%d + %d + %d) => %d\n", + // i, yGBinCB, vloat->yReal, vloat->size.ascent, + // vloat->size.descent, + // containingBlock->getStyle()->boxRestHeight(), *height); + } +} + +void OutOfFlowMgr::getExtremes (int cbMinWidth, int cbMaxWidth, + 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? + + 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->widget->getExtremes (&extr); + + *oofMinWidth = max (*oofMinWidth, extr.minWidth + minBorderDiff); + *oofMaxWidth = max (*oofMaxWidth, extr.maxWidth + maxBorderDiff); + } +} + +/** + * 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); + //printf ("getLeftBorder (%p, %d, %d) => %d\n", textblock, y, h, 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); + //printf ("getRightBorder (%p, %d, %d) => %d\n", textblock, y, h, b); + return b; +} + +int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h, + Textblock *lastGB, int lastExtIndex) +{ + //printf ("[%p] GET_BORDER (%p (allocated: %s), %s, %d, %d, %p, %d)\n", + // containingBlock, textblock, + // wasAllocated (textblock) ? "true" : "false", + // side == LEFT ? "LEFT" : "RIGHT", y, h, lastGB, lastExtIndex); + + SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); + + //printf (" searching in list:\n"); + //for (int i = 0; i < list->size(); i++) { + // printf (" %d: %s\n", i, list->get(i)->toString()); + // //printf (" (widget at (%d, %d))\n", + // // list->get(i)->widget->getAllocation()->x, + // // list->get(i)->widget->getAllocation()->y); + //} + + int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex); + + //printf (" first = %d\n", first); + + if (first == -1) + // No float. + 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); + //printf (" float %d: %s; covers? %s.\n", + // i, vloat->toString(), covers ? "yes" : "no"); + + if (covers) { + int borderDiff = getBorderDiff (textblock, vloat, side); + int borderIn = side == LEFT ? + vloat->generatingBlock->getStyle()->boxOffsetX() : + vloat->generatingBlock->getStyle()->boxRestWidth(); + border = max (border, vloat->size.width + borderIn + borderDiff); + //printf (" => border = %d\n", border); + } + } + + 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) +{ + return hasFloat (textblock, LEFT, y, h, lastGB, lastExtIndex); +} + +bool OutOfFlowMgr::hasFloatRight (Textblock *textblock, int y, int h, + Textblock *lastGB, int lastExtIndex) +{ + return hasFloat (textblock, RIGHT, y, h, lastGB, lastExtIndex); +} + +bool OutOfFlowMgr::hasFloat (Textblock *textblock, Side side, int y, int h, + Textblock *lastGB, int lastExtIndex) +{ + //printf ("[%p] hasFloat (%p, %s, %d, %d, %p, %d)\n", + // containingBlock, textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, + // lastGB, lastExtIndex); + SortedFloatsVector *list = getFloatsListForTextblock(textblock, side); + return list->findFirst (textblock, y, h, lastGB, lastExtIndex) != -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->widget->getStyle()->width) && + vloat->cbAvailWidth != containingBlock->getAvailWidth ())) { + // TODO Ugly. Soon to be replaced by cleaner code? See also + // comment in Textblock::calcWidgetSize. + if (vloat->widget->usesHints ()) { + if (isAbsLength (vloat->widget->getStyle()->width)) + vloat->widget->setWidth + (absLengthVal (vloat->widget->getStyle()->width)); + else if (isPerLength (vloat->widget->getStyle()->width)) + vloat->widget->setWidth + (multiplyWithPerLength (containingBlock->getAvailWidth(), + vloat->widget->getStyle()->width)); + } + + // 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->widget->getExtremes (&extremes); + + vloat->widget->sizeRequest (&vloat->size); + + // Set width ... + int width = vloat->size.width; + // Consider the available width of the containing block (when set): + if (width > containingBlock->getAvailWidth()) + width = containingBlock->getAvailWidth(); + // Finally, consider extremes (as described above). + if (width < extremes.minWidth) + width = extremes.minWidth; + if (width > extremes.maxWidth) + width = extremes.maxWidth; + + vloat->widget->setWidth (width); + vloat->widget->sizeRequest (&vloat->size); + + //printf (" float %p (%s %p): %d x (%d + %d)\n", + // vloat, vloat->widget->getClassName(), vloat->widget, + // vloat->size.width, vloat->size.ascent, vloat->size.descent); + + vloat->cbAvailWidth = containingBlock->getAvailWidth (); + vloat->dirty = false; + + // "sizeChangedSinceLastAllocation" is reset in sizeAllocateEnd() + } +} + +/** + * Return the difference between generator and calling textblock. Used + * in getBorder() to determine the difference between calling + * textblock and float (which position is defined relative to the + * generator). + */ +int OutOfFlowMgr::getBorderDiff (Textblock *textblock, Float *vloat, Side side) +{ + if (textblock == vloat->generatingBlock) + return 0; + else { + assert (wasAllocated (textblock) && + wasAllocated (vloat->generatingBlock)); + + switch (side) { + case LEFT: + return getAllocation(vloat->generatingBlock)->x + - getAllocation(textblock)->x; + + case RIGHT: + return + getAllocation(textblock)->x + getAllocation(textblock)->width + - (getAllocation(vloat->generatingBlock)->x + + min (getAllocation(vloat->generatingBlock)->width, + vloat->generatingBlock->getAvailWidth())); + + default: + assertNotReached(); + return 0; + } + } +} + +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); + } +} + +// TODO Latest change: Check also Textblock::borderChanged: looks OK, +// but the comment ("... (i) with canvas coordinates ...") looks wrong +// (and looks as having always been wrong). + +// Another issue: does it make sense to call Textblock::borderChanged +// for generators, when the respective widgets have not been called +// yet? + +} // namespace dw diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh new file mode 100644 index 00000000..71599d14 --- /dev/null +++ b/dw/outofflowmgr.hh @@ -0,0 +1,380 @@ +#ifndef __DW_OUTOFFLOWMGR_HH__ +#define __DW_OUTOFFLOWMGR_HH__ + +#include "core.hh" + +namespace dw { + +class Textblock; + +/** + * \brief Represents additional data for containing blocks. + */ +class OutOfFlowMgr +{ +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 below + // for usage. + core::Allocation containingBlockAllocation; + bool containingBlockWasAllocated; + + class Float: public lout::object::Comparable + { + private: + OutOfFlowMgr *oofm; + + public: + class CompareSideSpanningIndex: public lout::object::Comparator + { + 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); + }; + + core::Widget *widget; + 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); + int compareTo(Comparable *other); + + int yForTextblock (Textblock *textblock, int y); + inline int yForTextblock (Textblock *textblock) + { return yForTextblock (textblock, yReal); } + int yForContainer (int y); + inline int yForContainer () { return yForContainer (yReal); } + 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 lout::object::Object + { + public: + bool wasAllocated; + int xCB, yCB; // relative to the containing block + int width, height; + int index; // position within "tbInfos" + Textblock *textblock; // for debugging; may be removed again + + 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 (); + }; + + 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); + bool isTextblockCoveredByFloats (Textblock *tb, int tbx, int tby, + int tbWidth, int tbHeight, int *minFloatPos, + core::Widget **minFloat); + bool isTextblockCoveredByFloats (SortedFloatsVector *list, Textblock *tb, + int tbx, int tby, int tbWidth, int tbHeight, + int *minFloatPos, core::Widget **minFloat); + bool isTextblockCoveredByFloat (Float *vloat, Textblock *tb, + int tbx, int tby, int tbWidth, int tbHeight, + int *floatPos); + void checkChangedFloatSizes (); + void checkChangedFloatSizes (SortedFloatsVector *list); + + 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 collides (Float *vloat, Float *other, int *yReal); + void checkCoveragePosChanged (Float *vloat, int oldY); + + void getFloatsLists (Float *vloat, SortedFloatsVector **listSame, + SortedFloatsVector **listOpp); + + void getFloatsSize (SortedFloatsVector *list, 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); + int getBorderDiff (Textblock *textblock, Float *vloat, Side side); + + 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 cbWidth, int cbHeight, int *oofWidth, int *oofHeight); + void getExtremes (int cbMinWidth, int cbMaxWidth, 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)->widget; + else if (i < leftFloatsAll->size() + rightFloatsAll->size()) + return rightFloatsAll->get(i - leftFloatsAll->size())->widget; + 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 4ab5673a..3f39a39e 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 + @@ -302,6 +323,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 089cf59b..3fe3b7c9 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -295,7 +295,6 @@ enum ListStylePosition { LIST_STYLE_POSITION_INSIDE, LIST_STYLE_POSITION_OUTSIDE }; - enum ListStyleType { LIST_STYLE_TYPE_DISC, LIST_STYLE_TYPE_CIRCLE, @@ -331,6 +330,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, @@ -347,6 +353,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. * @@ -502,6 +521,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 8db9ef2a..eef1b6ce 100644 --- a/dw/table.cc +++ b/dw/table.cc @@ -480,7 +480,7 @@ void Table::reallocChildren (int newNumCols, int newNumRows) void Table::calcCellSizes () { - if (needsResize ()) + if (needsResize () || resizeQueued ()) forceCalcCellSizes (); } @@ -638,7 +638,7 @@ void Table::apportionRowSpan () */ void Table::calcColumnExtremes () { - if (extremesChanged ()) + if (extremesChanged () || extremesQueued ()) forceCalcColumnExtremes (); } @@ -1128,7 +1128,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; } } @@ -1150,8 +1150,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; } @@ -1165,7 +1165,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; } @@ -1177,8 +1177,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; } @@ -1192,7 +1192,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 5a005e3d..edad1524 100644 --- a/dw/tablecell.cc +++ b/dw/tablecell.cc @@ -106,7 +106,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 8623a574..c0a902f4 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,6 +252,7 @@ Textblock::Textblock (bool limitTextWidth) nonTemporaryLines = 0; words = new misc::NotSoSimpleVector <Word> (1); anchors = new misc::SimpleVector <Anchor> (1); + outOfFlowMgr = NULL; //DBG_OBJ_SET_NUM(this, "num_lines", num_lines); @@ -267,6 +270,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 +281,8 @@ Textblock::Textblock (bool limitTextWidth) hlEnd[layer].index = 0; hlEnd[layer].nChar = 0; } + + initNewLine (); } Textblock::~Textblock () @@ -288,8 +295,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 +317,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; @@ -360,9 +371,24 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) this, innerPadding, getStyle()->boxDiffWidth ()); requisition->width += innerPadding + getStyle()->boxDiffWidth (); - requisition->ascent += getStyle()->boxOffsetY (); + requisition->ascent += verticalOffset + getStyle()->boxOffsetY (); requisition->descent += getStyle()->boxRestHeight (); + // 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. + + if (outOfFlowMgr) { + int oofWidth, oofHeight; + outOfFlowMgr->getSize (requisition->width, + requisition->ascent + requisition->descent, + &oofWidth, &oofHeight); + requisition->width = misc::max (requisition->width, oofWidth); + if (oofHeight > requisition->ascent + requisition->descent) + requisition->descent = oofHeight - requisition->ascent; + } + if (requisition->width < availWidth) requisition->width = availWidth; @@ -375,7 +401,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 { @@ -423,6 +449,19 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) extremes->minWidth += diff; extremes->maxWidth += diff; + if (outOfFlowMgr) { + int oofMinWidth, oofMaxWidth; + outOfFlowMgr->getExtremes (extremes->minWidth, extremes->maxWidth, + &oofMinWidth, &oofMaxWidth); + + //printf ("[%p] extremes: %d / %d, corrected: %d / %d\n", + // this, extremes->minWidth, extremes->maxWidth, + // oofMinWidth, oofMaxWidth); + + extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth); + extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth); + } + PRINTF ("[%p] GET_EXTREMES => %d / %d\n", this, extremes->minWidth, extremes->maxWidth); } @@ -436,6 +475,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) showMissingLines (); + if(outOfFlowMgr) + outOfFlowMgr->sizeAllocateStart (allocation); + int lineIndex, wordIndex; Line *line; Word *word; @@ -449,7 +491,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 +501,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 +515,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 +574,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 +610,114 @@ void Textblock::resizeDrawImpl () void Textblock::markSizeChange (int 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); + + // 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); + } +} + +void Textblock::markExtremesChange (int ref) +{ 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 = ref; - else - wrapRefLines = misc::min (wrapRefLines, ref); + 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)); + } + + PRINTF (" ... => %d\n", wrapRefParagraphs); } +} - PRINTF (" ... => %d\n", wrapRefLine); +void Textblock::notifySetAsTopLevel() +{ + PRINTF ("%p becomes toplevel\n", this); + containingBlock = this; + PRINTF ("-> %p is its own containing block\n", this); +} - // 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); +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::markExtremesChange (int ref) +void Textblock::notifySetParent () { - 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); - } + PRINTF ("%p becomes a child of %p\n", this, getParent()); + + // Search for containing Box. + containingBlock = NULL; - PRINTF (" ... => %d\n", wrapRefParagraphs); + 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) @@ -615,7 +731,7 @@ void Textblock::setWidth (int width) // words->size()); availWidth = width; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; redrawY = 0; } @@ -630,7 +746,7 @@ void Textblock::setAscent (int ascent) // words->size()); availAscent = ascent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; } } @@ -644,7 +760,7 @@ void Textblock::setDescent (int descent) // words->size()); availDescent = descent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; } } @@ -753,7 +869,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 +878,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 +977,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 +1012,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 +1381,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 +1392,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 +1439,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 +1476,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 +1502,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 +1559,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 +1601,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 +1620,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area) drawLine (line, view, area); } + + if(outOfFlowMgr) + outOfFlowMgr->draw(view, area); } /** @@ -1935,6 +2109,12 @@ 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); + // 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); } @@ -1943,28 +2123,48 @@ 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); + + 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; + } 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; + } processWord (words->size () - 1); //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref", @@ -2066,8 +2266,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; @@ -2139,7 +2341,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 @@ -2148,23 +2350,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 = @@ -2173,7 +2375,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; } } @@ -2181,6 +2384,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) } /* Otherwise continue to examine parents. */ } + /* Return in any case. */ return; } @@ -2229,7 +2433,6 @@ void Textblock::addLinebreak (core::style::Style *style) processWord (words->size () - 1); } - /** * \brief Search recursively through widget. * @@ -2238,6 +2441,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; @@ -2248,7 +2455,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; @@ -2259,7 +2474,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); @@ -2340,7 +2555,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, @@ -2392,4 +2607,277 @@ 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) +{ + PRINTF ("[%p] BORDER_CHANGED: %d (float %s %p, with generator %p)\n", + this, y, vloat->getClassName(), vloat, vloat->getGenerator()); + + int lineIndex = findLineIndex (y); + PRINTF (" Line index: %d (of %d).\n", 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; + + PRINTF (" Rewrapping from line %d (of %d).\n", + 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); + } + + PRINTF (" Corrected to line %d.\n", wrapLineIndex); + + queueResize (OutOfFlowMgr::createRefNormalFlow (wrapLineIndex), true); + lastWordDrawn = lines->getRef(wrapLineIndex)->firstWord; + } +} + +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) +{ + if (lineNo == 0) + return verticalOffset + getStyle()->boxOffsetY(); + else { + Line *prevLine = lines->getRef (lineNo - 1); + return verticalOffset + + prevLine->top + prevLine->boxAscent + prevLine->boxDescent + + prevLine->breakSpace + getStyle()->boxOffsetY(); + } +} + +int Textblock::heightOfPossiblyMissingLine (int lineNo) +{ + PRINTF ("[%p] HEIGHT_OF_POSSIBLY_MISSING_LINE (%d)\n", this, lineNo); + + 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) { + PRINTF (" Exists and is finished; height = %d + %d = %d\n", + line->boxAscent, line->boxDescent, + line->boxAscent + line->boxDescent); + return line->boxAscent + line->boxDescent; + } else { + PRINTF (" Exist but is not finished.\n"); + return 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? + + PRINTF (" Does not exist.\n"); + return misc::max (1, newLineAscent + newLineDescent); + } else + return 1; +} + } // namespace dw diff --git a/dw/textblock.hh b/dw/textblock.hh index b85937ba..b9255bb9 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -4,6 +4,7 @@ #include <limits.h> #include "core.hh" +#include "outofflowmgr.hh" #include "../lout/misc.hh" // These were used when improved line breaking and hyphenation were @@ -11,6 +12,8 @@ #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> * @@ -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,6 +766,11 @@ 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; } }; } // namespace dw 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..1c1a3c1f 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -232,21 +232,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) @@ -341,9 +327,23 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, PRINTF ("[%p] ADD_LINE (%d, %d) => %d\n", this, firstWord, lastWord, lines->size ()); - Word *lastWordOfLine = words->getRef(lastWord); - // Word::totalWidth includes the hyphen (which is what we want here). - int lineWidth = lastWordOfLine->totalWidth; + //for (int i = firstWord; i <= lastWord; i++) { + // printf (" word %d: ", i); + // printWord (words->getRef (i)); + // printf ("\n"); + //} + + int lineWidth; + if (lastWord >= firstWord) { + 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; + // "lineWidth" is relative to leftOffset, so we may have to add // "line1OffsetEff" (remember: this is, for list items, negative). if (lines->size () == 0) @@ -353,9 +353,6 @@ 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); @@ -379,6 +376,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,6 +398,19 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, for(int i = line->firstWord; i <= line->lastWord; i++) accumulateWordForLine (lineIndex, i); + // 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; + 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", @@ -408,9 +419,10 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, line->contentAscent); PRINTF (" line[%d].contentDescent = %d\n", lines->size () - 1, line->contentDescent); - - PRINTF (" line[%d].maxLineWidth = %d\n", - lines->size () - 1, line->maxLineWidth); + PRINTF (" line[%d].maxLineWidth = %d (lineWidth = %d)\n", + lines->size () - 1, line->maxLineWidth, lineWidth); + PRINTF (" line[%d].offsetCompleteWidget = %d\n", + lines->size () - 1, line->offsetCompleteWidget); mustQueueResize = true; @@ -421,7 +433,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 +443,10 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, xWidget += word->size.width + word->effSpace; } + line->finished = true; + + initNewLine (); + return line; } @@ -510,15 +526,12 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll) PRINTF ("[%p] WORD_WRAP (%d, %s)\n", this, wordIndex, wrapAll ? "true" : "false"); - Word *word; - bool wordListChanged = false; - if (!wrapAll) removeTemporaryLines (); initLine1Offset (wordIndex); - word = words->getRef (wordIndex); + Word *word = words->getRef (wordIndex); word->effSpace = word->origSpace; accumulateWordData (wordIndex); @@ -527,16 +540,55 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll) //printWord (word); //printf ("\n"); + switch (word->content.type) { + case core::Content::WIDGET_OOF_REF: + return wrapWordOofRef (wordIndex, wrapAll); + break; + + default: + return wrapWordInFlow (wordIndex, wrapAll); + break; + } +} + +bool Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) +{ + 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 thereWillBeMoreSpace = + mustBorderBeRegarded (lines->size ()) ? + newLineHasFloatLeft || newLineHasFloatRight : + 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; @@ -552,13 +604,21 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll) // 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. + bool possibleLineBreak = false; - for (int i = firstIndex; !possibleLineBreak && i <= wordIndex - 1; i++) + for (int i = firstIndex; + !(thereWillBeMoreSpace || possibleLineBreak) + && i <= wordIndex - 1; + i++) if (words->getRef(i)->badnessAndPenalty .lineCanBeBroken (penaltyIndex)) possibleLineBreak = true; - if (possibleLineBreak && word->badnessAndPenalty.lineTooTight ()) { + if ((thereWillBeMoreSpace || possibleLineBreak) + && word->badnessAndPenalty.lineTooTight ()) { newLine = true; searchUntil = wordIndex - 1; PRINTF (" NEW LINE: line too tight\n"); @@ -575,65 +635,78 @@ 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); + if (firstIndex > searchUntil) { + // empty line + assert (searchUntil == firstIndex - 1); + addLine (firstIndex, firstIndex - 1, tempNewLine); 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; + 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"); - // 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. + 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; + + // 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; + } + + PRINTF ("[%p] accumulating again from %d to %d\n", + this, 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); - } 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,7 +723,8 @@ 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); @@ -660,6 +734,87 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll) 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) +{ + Word *w = words->getRef (wordIndex); + bool heightIncreased = + containingBlock->outOfFlowMgr && + (w->size.ascent > newLineAscent || w->size.descent >= newLineDescent); + + newLineAscent = misc::max (newLineAscent, w->size.ascent); + newLineDescent = misc::max (newLineDescent, w->size.descent); + + if (heightIncreased) + updateBorders (wordIndex, true, true); +} + +bool Textblock::wrapWordOofRef (int wordIndex, bool wrapAll) +{ + 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); + + 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) +{ + 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); +} + int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex, bool correctAtEnd) { @@ -763,7 +918,6 @@ bool Textblock::isHyphenationCandidate (Word *word) Hyphenator::isHyphenationCandidate (word->content.text); } - /** * Counter part to wordWrap(), but for extremes, not size calculation. */ @@ -886,6 +1040,10 @@ int Textblock::hyphenateWord (int wordIndex) 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); @@ -941,13 +1099,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 +1131,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 +1151,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 +1175,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); @@ -1080,18 +1250,35 @@ void Textblock::accumulateWordData (int wordIndex) int Textblock::calcAvailWidth (int lineIndex) { - int availWidth = - this->availWidth - getStyle()->boxDiffWidth() - innerPadding; + PRINTF ("[%p] CALC_AVAIL_WIDTH (%d of %d) ...\n", + this, lineIndex, lines->size()); + + 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); + + PRINTF ("[%p] CALC_AVAIL_WIDTH (%d of %d) => %d - %d - (%d + %d)" + " = %d\n", this, lineIndex, lines->size(), this->availWidth, + innerPadding, leftBorder, rightBorder, + availWidth); return availWidth; } @@ -1108,7 +1295,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,7 +1367,7 @@ void Textblock::alignLine (int lineIndex) */ void Textblock::rewrap () { - PRINTF ("[%p] REWRAP: wrapRef = %d\n", this, wrapRef); + PRINTF ("[%p] REWRAP: wrapRefLines = %d\n", this, wrapRefLines); if (wrapRefLines == -1) /* page does not have to be rewrapped */ @@ -1191,16 +1378,21 @@ void Textblock::rewrap () lines->setSize (wrapRefLines); nonTemporaryLines = misc::min (nonTemporaryLines, wrapRefLines); + initNewLine (); + int firstWord; - if (lines->size () > 0) - firstWord = lines->getLastRef()->lastWord + 1; - else + if (lines->size () > 0) { + Line *lastLine = lines->getLastRef(); + firstWord = lastLine->lastWord + 1; + } else firstWord = 0; + PRINTF (" starting with word %d\n", firstWord); + for (int i = firstWord; 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) calcWidgetSize (word->content.widget, &word->size); wordWrap (i, false); @@ -1266,6 +1458,49 @@ void Textblock::fillParagraphs () wrapRefParagraphs = -1; } +void Textblock::initNewLine () +{ + // 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); + + PRINTF ("[%p] INIT_NEW_LINE: %d (%s) / %d (%s), at %d (%d), until %d\n", + this, newLineLeftBorder, newLineHasFloatLeft ? "true" : "false", + newLineRightBorder, newLineHasFloatRight ? "true" : "false", + y, h, lastRef); + } else { + newLineHasFloatLeft = newLineHasFloatRight = false; + newLineLeftBorder = newLineRightBorder = 0; + + PRINTF ("[%p] INIT_NEW_LINE: no CB or OOFM\n", this); + } + + newLineAscent = newLineDescent = 0; +} + void Textblock::showMissingLines () { int firstWordToWrap = lines->size () > 0 ? 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 @@ -270,7 +270,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 b09c51e9..83b9956a 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; @@ -148,15 +148,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); } @@ -165,40 +167,64 @@ void Widget::queueDrawArea (int x, int y, int width, int height) */ void Widget::queueResize (int ref, bool extremesChanged) { + assert (!queueResizeEntered ()); + + 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 (); } @@ -208,6 +234,25 @@ void Widget::queueResize (int ref, bool extremesChanged) */ void Widget::sizeRequest (Requisition *requisition) { + assert (!queueResizeEntered ()); + + 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); @@ -219,6 +264,11 @@ 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 (); } /** @@ -226,6 +276,19 @@ void Widget::sizeRequest (Requisition *requisition) */ void Widget::getExtremes (Extremes *extremes) { + assert (!queueResizeEntered ()); + + 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; @@ -235,6 +298,8 @@ void Widget::getExtremes (Extremes *extremes) DBG_OBJ_SET_NUM ("extremes.maxWidth", extremes->maxWidth); } else *extremes = this->extremes; + + leaveGetExtremes (); } /** @@ -243,6 +308,23 @@ void Widget::getExtremes (Extremes *extremes) */ void Widget::sizeAllocate (Allocation *allocation) { + assert (!queueResizeEntered ()); + assert (!sizeRequestEntered ()); + assert (!getExtremesEntered ()); + assert (resizeIdleEntered ()); + + 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 || @@ -283,6 +365,8 @@ void Widget::sizeAllocate (Allocation *allocation) } /*unsetFlags (NEEDS_RESIZE);*/ + + leaveSizeAllocate (); } bool Widget::buttonPress (EventButton *event) @@ -503,6 +587,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. */ @@ -562,7 +665,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, @@ -623,6 +728,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..c9aeb427 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 << 3, + + /** + * \todo Comment this. */ - NEEDS_ALLOCATE = 1 << 1, + 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; @@ -200,6 +231,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 +283,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 +322,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 +361,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/container.cc b/lout/container.cc index de36a6f7..908df4ae 100644 --- a/lout/container.cc +++ b/lout/container.cc @@ -188,9 +188,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,18 +203,20 @@ 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; + + if (start > end) + return mustExist ? -1 : start; - int high = numElements - 1, low = 0; + int low = start, high = end; while (true) { int index = (low + high) / 2; - int c = ((Comparable*) key)->compareTo ((Comparable*)array[index]); + int c = comparator->compare (key, array[index]); if (c == 0) return index; else { @@ -233,6 +236,7 @@ int Vector::bsearch(Object *key, bool mustExist) /* + Comparator::compareFunComparator = comparator; void *result = ::bsearch (&key, array, numElements, sizeof (Object*), Comparable::compareFun); if (result) diff --git a/lout/container.hh b/lout/container.hh index c87eb10c..9180b9e0 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) { 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) { return (T*)((untyped::Vector*)this->base)->get(pos); } inline int size() { 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/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 cff8e05b..2ed5e1b0 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/cssparser.cc b/src/cssparser.cc index b283957a..4ff8f4f7 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/html.cc b/src/html.cc index 1257c102..74b90577 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 98b31d32..2da9d8f7 100644 --- a/src/styleengine.cc +++ b/src/styleengine.cc @@ -606,6 +606,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; @@ -615,6 +621,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; @@ -666,6 +678,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; @@ -678,6 +696,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? |