diff options
35 files changed, 2817 insertions, 454 deletions
diff --git a/doc/dw-out-of-flow.doc b/doc/dw-out-of-flow.doc new file mode 100644 index 00000000..b6f65a28 --- /dev/null +++ b/doc/dw-out-of-flow.doc @@ -0,0 +1,409 @@ +/** \page dw-out-of-flow Handling Elements Out Of Flow + +<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.</li> + </ul> + +which may be rendered like this + + - - - - - - - - - - - - - - - - - - - - - - - - - . + | * Some text. + * Some more and - - - - - - - - - - - -.| + | longer text. |Some longer text, so that + * Final text. the effect described || + ` - - - - - - - - - - - |above in this passage can ' + be demonstrated. | + ` - - - - - - - - - - - - + +The float (the DIV section) is defined ("generated") within the list +item, 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. + +Definition of "containing block" +================================ + +The following definition follows the definition in the CSS 2 +specification. For a given dw::Textblock, the containing block is +defined as the one dw::Textblock following these rules: + +- For the toplevel widget, the containing block is itself (see + dw::Textblock::notifySetAsTopLevel). + +- Otherwise, ... TODO: Has been changed; see + dw::Textblock::notifySetParent. + +TODO: + +1. Compare this to + <http://www.w3.org/TR/CSS2/visudet.html#containing-block-details>. +2. Handle fixed positions: in this case, the viewport is the + containing box, but the viewport is not represented by a widget. +3. Clarify: the element out of flow must not be a dw::Textblock. + + +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 the rules +defined above. + +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. + + +Interaction between generating box and containing box +===================================================== + +<div style="border: 2px solid #ff0000; margin-bottom: 0.5em; padding: +0.5em 1em; background-color: #ffefe0"><b>Warning:</b> This section +documents the current state, which is not free of errors.</div> + +A new widget out of flow is added to the generating block +--------------------------------------------------------- +With the respective style attributes set, this is delegated to the +dw::OutOfFlowMgr of the containing block: + + ,-------------. ,-------------. ,-------------. ,-------------. + | float | | OOFM | | cont. block | | gen. block | + `-------------' `-------------' `-------------' `-------------' + | | | | + ------------------------------------------------------------>| + | | | addWidget | + | | | (with an | + | | | appr. style) | + | | | | + | |<----------------------------------| + | | addWidget | | + +As with widgets in flow, dw::Textblock::wordWrap is called by/for the +generating block: + + | | | |--. + | | | | |wordWrap + | | | |<-' + +First, the generating block will tell the dw::OutOfFlowMgr about the +float position: + + | |<----------------------------------| + | | tellPosition | | + | | [1] | | + | | | | + | |---------------->| | + | | borderChanged | | + | | | | + | | |---------------->| + | | | borderChanged | + | | | | + | | | |--. + | | | | |queueResize + | | | |<-' + +After this, the line with must be calculated, with some implications: + + | | | |--. + | | | | |calcAvail\ + | | | | |Width + | | | |<-' + | | | | + | | | |--. + | | | | |lineLeft\ + | | | | |Border [1] + | | | |<-' + | | | | + | |<----------------------------------| + | | getLeftBorder | | + | | | | + | |--. | | + | | |ensureFloat\ | | + | | |Size | | + | |<-' | | + | | | | + |<----------------| | | + | sizeRequest | | | + | (perhaps) | | | + +"getRightBorder" in an analogue way. + +[1] Here, the values of diffXToContainingBlock, + restWidthToContainingBlock, and diffYToContainingBlock are + used. TODO: These may not be set correctly. + +The size of a float changes its size +------------------------------------ +First, the standard part: + + ,-------------. ,-------------. ,-------------. ,-------------. + | float | | OOFM | | cont. block | | gen. block | + `-------------' `-------------' `-------------' `-------------' + | | | | + |--. | | | + | |queueResize | | | + |<-' | | | + | | | | + |---------------------------------->| | + | | markSizeChange | | + +Since dw::core::Widget::parentRef indicates this child as out of flow, +this is delegated to the dw::OutOfFlowMgr: + + | |<----------------| | + | | markSizeChange | | + | | | | + | |---------------->| | + | | borderChanged | | + | | | | + | | |---------------->| + | | | borderChanged | + | | | | + | | | |--. + | | | | |queueResize + | | | |<-' + +Notice that the size of the float is not yet calculated, instead a +size request is queued (dw::OutOfFlowMgr::Float::dirty is set), which +will result in a new calculation of the size of the containing block. + +Also, queueResize is called recursively here: before the call for the +float is finished, it is called for the generating block. (See \ref +dw-widget-sizes for details.) + +Calculating the size +-------------------- +As usually, it starts at the top of the widget tree; in this case, the +containing block is regarded: + + ,-------------. ,-------------. ,-------------. ,-------------. + | Float | | OOFM | | cont. block | | gen. block | + `-------------' `-------------' `-------------' `-------------' + | | | | + ------------------------------------------>| | + | | sizeRequest | | + | | | | + | | |--. | + | | | sizeRequestImpl | + | | |<-' | + +During rewrapping, the size of the child is requested. We omit some +steps, since nothing has changed, except that + +- dw::Textblock::diffXToContainingBlock, +- dw::Textblock::restWidthToContainingBlock, and +- dw::Textblock::diffYToContainingBlock + +are set for the child, before this call: + + | | |---------------->| + | | | sizeRequest | + | | | | + | | | |--. + | | | | sizeRequest\ + | | | | Impl + | | | |<-' + +It may be that the child, the generating block, has to rewrap the +lines: + + | | | |--. + | | | | rewrap + | | | |<-' + | | | | + | |<----------------------------------| + | | tellNoPosition | | + | | | | +The rest is then similar to above: + + | | | |--. + | | | | wordWrap + | | | |<-' + | | | | + | |<----------------------------------| + | | tellPosition | | + | | [1] | | + | | | | + | |---------------->| | + | | borderChanged | | + | | | | + | | |---------------->| + | | | borderChanged | + | | | | + | | | |--. + | | | | |queueResize + | | | |<-' + | | | | + | | | |--. + | | | | |calcAvail\ + | | | | |Width + | | | |<-' + | | | | + | | | |--. + | | | | |lineLeft\ + | | | | |Border [1] + | | | |<-' + | | | | + | |<----------------------------------| + | | getLeftBorder | | + | | | | + | |--. | | + | | |ensureFloat\ | | + | | |Size | | + | |<-' | | + | | | | + |<----------------| | | + | sizeRequest | | | + | (perhaps) | | | + +Notice the nested sizeRequest (containing block -- generating block -- +float), but also the call of dw::core::Widget::queueResize within +dw::core::Widget::sizeRequestImpl, which is allowed, see +\ref dw-widget-sizes. + +[1] Here, the values of diffXToContainingBlock, + restWidthToContainingBlock, and diffYToContainingBlock are used. + + +Bugs and Things Needing Improvement +=================================== + +High Priority +------------- +**Misplaced/missing floats, wrong widths etc.:** Best is to provide +test pages with these errors, which are as simple as possible. + +**Extremes:** Extremes will be implemented by +dw::OutOfFlowMgr::getExtremes, which must be considered by +dw::Textblock::getExtremesImpl, like dw::OutOfFlowMgr::getSize is used +by dw::Textblock::sizeRequestImpl. The last two points (lines with no +contents/collisions of floats) lead to a very simple of +dw::OutOfFlowMgr::getExtremes: the result is simply the maximum of the +floats extremes. (This is at least correct for the minimal width.) + +**Rewrapping** (via dw::Textblock::borderChanged) sometimes does not +imply redrawing. Reproduce? + +(Plus some more; list must be updated.) + +Medium Priority +--------------- +**Position of a text block:** Setting, +dw::Textblock::diffXToContainingBlock, +dw::Textblock::restWidthToContainingBlock, and +dw::Textblock::diffYToContainingBlock in the current way is ugly, +should be replaced by something like size hints. + +**Determining the containing block:** In +dw::Textblock::notifySetAsTopLevel and dw::Textblock::notifySetParent: +should other widgets be able to be generating/containing block? + +**Determining whether a widget cares about floats:** Both variants of +_mustBorderBeRegarded_, and all variants of _getTextblockForLine_: +Perhaps, there should be only one widget of this type per line. + +**Implementing the attribute "clear":** ... + +**Exact positions of floats:** See dw::OutOfFlowMgr::calcBorderDiff. + +Low Priority +------------- +**Absolute positions:** Will be incorporated into the +dw::OutOfFlowMgr. + +**Fixed elements:** Some notes about what has do be done for fixed +elements: (i) In the current design, there is only one containing +block for a given generating block. However, a given generating block +may contain an element with absolute position (or, a float), + +Not a Bug (Kind of) +------------------- + +*Floats too far left?* Sometimes, floats seem too far on the +left side, as in test/floats-and-absolute.html, and also in +Wikipedia. This is only due to the fact that dillo does not yet +support absolute positions, and so absolutely positioned elements are +not regarded as containing block. (Or related to *Exact positions of +floats* above?) + +**Update:** Floats refer to the border of the *generating* box, not of +the *containing* box, so this is not an issue at all; furthermore, +this has been fixed. + +*/
\ No newline at end of file diff --git a/doc/dw-widget-sizes.doc b/doc/dw-widget-sizes.doc index 419a4a73..a4675c8a 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,82 @@ 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 +===================================== + +<div style="border: 2px solid #ff0000; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #ffefe0"><b>Warning:</b> +This section still needs some work.</div> + +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 + dw::core::Widget::sizeRequestImpl (called by + dw::core::Widget::sizeRequest), dw::core::Widget::markSizeChange, + and dw::core::Widget::markExtremesChange (the latter two are + called by dw::core::Widget::queueResize). +2. On the other hand, to make sure that the calls, which are allowed, + are handled correctly, especially in implementations of + dw::core::Widget::sizeRequestImpl, + dw::core::Widget::markSizeChange, and + dw::core::Widget::markExtremesChange. + +The rules in detail: + +- Within dw::core::Widget::sizeRequest / + dw::core::Widget::sizeRequestImpl: + 1. dw::core::Widget::sizeRequest must only be called for + children. Otherwise, an endless recursion would easily occur. + 2. dw::core::Widget::queueResize can be called. See below for + details. + +- Within queueResize / dw::core::Widget::markSizeChange / + dw::core::Widget::markExtremesChange. + 1. dw::core::Widget::sizeRequest must _not_ be called. As a example, + look at dw::Image::setBuffer: Here, dw::core::Widget::queueResize + is called at the beginning, while dw::Image::buffer, which is + needed by dw::Image::sizeRequest, is set later. If the + implementation of dw::core::Widget::markSizeChange or + dw::core::Widget::markExtremesChange of a parent widget calls + dw::core::Widget::sizeRequest for the image widget, a wrong value + for the buffer would be used. (Of course, it is possible to first + set dw::Image::buffer, and the call + dw::core::Widget::queueResize, but this does not change the fact + that these considerations should not be necessary on this level.) + 2. dw::core::Widget::queueResize can be called with some + limitations. It has to be ensured that no endless recursion is + caused, e. g. by calling dw::core::Widget::queueResize for a + child. (But this is not the only case.) See below for details. + +The facts that dw::core::Widget::queueResize can be called within +other methods, leads to following guidelines: + +- dw::core::Widget::queueResize simply goes up in the hierarchy of + widgeds, sets some flags, and calls dw::core::Widget::markSizeChange + and dw::core::Widget::markExtremesChange. +- dw::core::Widget::markSizeChange and + dw::core::Widget::markExtremesChange should be kept extremely + simple. (TODO: Details) + */ diff --git a/dw/Makefile.am b/dw/Makefile.am index e108da60..aa5c88c3 100644 --- a/dw/Makefile.am +++ b/dw/Makefile.am @@ -65,6 +65,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 05896ebd..19485078 100644 --- a/dw/findtext.cc +++ b/dw/findtext.cc @@ -91,7 +91,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 */ @@ -123,7 +123,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 5f46cbdb..ccfc068b 100644 --- a/dw/iterator.cc +++ b/dw/iterator.cc @@ -186,6 +186,15 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end, } } + +void Iterator::print () +{ + printf ("in the %s %p, mask ", widget->getClassName(), widget); + Content::printMask (mask); + printf (", pointing at "); + Content::print (&content); +} + // ------------------- // EmptyIterator // ------------------- @@ -343,7 +352,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 +365,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 +399,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 +425,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 +450,19 @@ 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(); +} + /** * \brief Create a new deep iterator from an existing dw::core::Iterator. * @@ -456,6 +479,14 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask, */ DeepIterator::DeepIterator (Iterator *it) { + // 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 +498,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,21 +525,34 @@ 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; 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; } + } stack.put (it, thisLevel); @@ -577,7 +621,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 +654,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 +686,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 c5cfd72b..a48356ef 100644 --- a/dw/iterator.hh +++ b/dw/iterator.hh @@ -85,6 +85,8 @@ public: static void scrollTo (Iterator *it1, Iterator *it2, int start, int end, HPosition hpos, VPosition vpos); + + virtual void print (); }; @@ -167,6 +169,10 @@ private: bool hasContents; inline DeepIterator () { } + static Widget *getRespectiveParent (Widget *widget, Content::Type mask); + inline Widget *getRespectiveParent (Widget *widget) { + return getRespectiveParent (widget, mask); + } public: DeepIterator(Iterator *it); @@ -230,7 +236,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 a974378f..209decc8 100644 --- a/dw/layout.cc +++ b/dw/layout.cc @@ -248,6 +248,7 @@ void Layout::addWidget (Widget *widget) topLevel = widget; widget->layout = this; + widget->notifySetAsTopLevel(); findtextState.setWidget (widget); diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc new file mode 100644 index 00000000..ed531c79 --- /dev/null +++ b/dw/outofflowmgr.cc @@ -0,0 +1,495 @@ +#include "outofflowmgr.hh" + +//#include <math.h> // testing + +using namespace lout::container::typed; +using namespace lout::misc; +using namespace dw::core; +using namespace dw::core::style; + +namespace dw { + +OutOfFlowMgr::OutOfFlowMgr (ContainingBlock *containingBlock) +{ + //printf ("OutOfFlowMgr::OutOfFlowMgr\n"); + + this->containingBlock = containingBlock; + availWidth = availAscent = availDescent = -1; + leftFloats = new Vector<Float> (1, true); + rightFloats = new Vector<Float> (1, true); +} + +OutOfFlowMgr::~OutOfFlowMgr () +{ + //printf ("OutOfFlowMgr::~OutOfFlowMgr\n"); + + delete leftFloats; + delete rightFloats; +} + +void OutOfFlowMgr::sizeAllocate (Allocation *containingBlockAllocation) +{ + // TODO Much copy and paste. + + for (int i = 0; i < leftFloats->size(); i++) { + Float *vloat = leftFloats->get(i); + assert (vloat->y != -1); + ensureFloatSize (vloat); + + Allocation childAllocation; + childAllocation.x = containingBlockAllocation->x + vloat->borderWidth; + childAllocation.y = containingBlockAllocation->y + vloat->y; + childAllocation.width = vloat->size.width; + childAllocation.ascent = vloat->size.ascent; + childAllocation.descent = vloat->size.descent; + + vloat->widget->sizeAllocate (&childAllocation); + + //printf ("allocate left #%d -> (%d, %d), %d x (%d + %d)\n", + // i, childAllocation.x, childAllocation.y, childAllocation.width, + // childAllocation.ascent, childAllocation.descent); + } + + int width = + availWidth != -1 ? availWidth : containingBlockAllocation->width; + + for (int i = 0; i < rightFloats->size(); i++) { + Float *vloat = rightFloats->get(i); + assert (vloat->y != -1); + ensureFloatSize (vloat); + + Allocation childAllocation; + childAllocation.x = containingBlockAllocation->x + width + - (vloat->size.width + vloat->borderWidth); + childAllocation.y = containingBlockAllocation->y + vloat->y; + childAllocation.width = vloat->size.width; + childAllocation.ascent = vloat->size.ascent; + childAllocation.descent = vloat->size.descent; + + vloat->widget->sizeAllocate (&childAllocation); + + //printf ("allocate right #%d -> (%d, %d), %d x (%d + %d)\n", + // i, childAllocation.x, childAllocation.y, childAllocation.width, + // childAllocation.ascent, childAllocation.descent); + } +} + + +void OutOfFlowMgr::draw (View *view, Rectangle *area) +{ + draw (leftFloats, view, area); + draw (rightFloats, view, area); +} + +void OutOfFlowMgr::draw (Vector<Float> *list, View *view, Rectangle *area) +{ + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + assert (vloat->y != -1); + + core::Rectangle childArea; + if (vloat->widget->intersects (area, &childArea)) + vloat->widget->draw (view, &childArea); + } +} + +void OutOfFlowMgr::queueResize(int ref) +{ +} + +bool OutOfFlowMgr::isWidgetOutOfFlow (core::Widget *widget) +{ + // Will be extended for absolute positions. + return widget->getStyle()->vloat != FLOAT_NONE; +} + +void OutOfFlowMgr::addWidget (Widget *widget, Widget *generatingBlock) +{ + if (widget->getStyle()->vloat != FLOAT_NONE) { + Float *vloat = new Float (); + vloat->widget = widget; + vloat->generatingBlock = generatingBlock; + vloat->dirty = true; + vloat->y = -1; + + switch (widget->getStyle()->vloat) { + case FLOAT_LEFT: + leftFloats->put (vloat); + widget->parentRef = createRefLeftFloat (leftFloats->size() - 1); + break; + + case FLOAT_RIGHT: + rightFloats->put (vloat); + widget->parentRef = createRefRightFloat (rightFloats->size() - 1); + break; + + default: + assertNotReached(); + } + } else + // Will continue here for absolute positions. + assertNotReached(); +} + +OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget) +{ + Vector<Float> *list = getFloatList (widget); + + for(int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + if(vloat->widget == widget) + return vloat; + } + + assertNotReached(); + return NULL; +} + +Vector<OutOfFlowMgr::Float> *OutOfFlowMgr::getFloatList (Widget *widget) +{ + switch (widget->getStyle()->vloat) { + case FLOAT_LEFT: + return leftFloats; + + case FLOAT_RIGHT: + return rightFloats; + + default: + assertNotReached(); + return NULL; + } +} + +Vector<OutOfFlowMgr::Float> *OutOfFlowMgr::getOppositeFloatList (Widget *widget) +{ + switch (widget->getStyle()->vloat) { + case FLOAT_LEFT: + return rightFloats; + + case FLOAT_RIGHT: + return leftFloats; + + default: + assertNotReached(); + return NULL; + } +} + +void OutOfFlowMgr::markSizeChange (int ref) +{ + //printf ("[%p] MARK_SIZE_CHANGE (%d)\n", containingBlock, ref); + + if (isRefFloat (ref)) { + Float *vloat; + + if (isRefLeftFloat (ref)) + vloat = leftFloats->get (getFloatIndexFromRef (ref)); + else if (isRefRightFloat (ref)) + vloat = rightFloats->get (getFloatIndexFromRef (ref)); + else { + assertNotReached(); + vloat = NULL; // compiler happiness + } + + vloat->dirty = true; + // In some cases, the vertical position is not yet defined. Nothing + // necessary then. + if (vloat->y != -1) + containingBlock->borderChanged (vloat->y); + } + else + // later: absolute positions + assertNotReached(); +} + + +void OutOfFlowMgr::markExtremesChange (int ref) +{ +} + +Widget *OutOfFlowMgr::getWidgetAtPoint (int x, int y, int level) +{ + Widget *childAtPoint = getWidgetAtPoint (leftFloats, x, y, level); + if (childAtPoint == NULL) + childAtPoint = getWidgetAtPoint (rightFloats, x, y, level); + return childAtPoint; +} + +Widget *OutOfFlowMgr::getWidgetAtPoint (Vector<Float> *list, + int x, int y, int level) +{ + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + Widget *childAtPoint = vloat->widget->getWidgetAtPoint (x, y, level + 1); + if (childAtPoint) + return childAtPoint; + } + + return NULL; +} + + +void OutOfFlowMgr::tellNoPosition (Widget *widget) +{ + Float *vloat = findFloatByWidget(widget); + int oldY = vloat->y; + vloat->y = -1; + + if (oldY != -1) + containingBlock->borderChanged (oldY); +} + + +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. + + *oofWidth = cbWidth; /* This (or "<=" instead of "=") should be + the case for floats. */ + + int oofHeightLeft = containingBlock->asWidget()->getStyle()->boxDiffWidth(); + int oofHeightRight = containingBlock->asWidget()->getStyle()->boxDiffWidth(); + + for (int i = 0; i < leftFloats->size(); i++) { + Float *vloat = leftFloats->get(i); + assert (vloat->y != -1); + ensureFloatSize (vloat); + oofHeightLeft = + max (oofHeightLeft, + vloat->y + vloat->size.ascent + vloat->size.descent + + containingBlock->asWidget()->getStyle()->boxRestHeight()); + } + + for (int i = 0; i < rightFloats->size(); i++) { + Float *vloat = rightFloats->get(i); + assert (vloat->y != -1); + ensureFloatSize (vloat); + oofHeightRight = + max (oofHeightRight, + vloat->y + vloat->size.ascent + vloat->size.descent + + containingBlock->asWidget()->getStyle()->boxRestHeight()); + } + + *oofHeight = max (oofHeightLeft, oofHeightRight); +} + + +void OutOfFlowMgr::tellPosition (Widget *widget, int y) +{ + assert (y >= 0); + + Float *vloat = findFloatByWidget(widget); + ensureFloatSize (vloat); + + //printf ("[%p] tellPosition (%p, %d): %d ...\n", + // containingBlock, widget, y, vloat->y); + + int oldY = vloat->y; + + int realY = y; + Vector<Float> *listSame = getFloatList (widget); + Vector<Float> *listOpp = getOppositeFloatList (widget); + bool collides; + + do { + // Test collisions on the same side. + collides = false; + + for (int i = 0; i < listSame->size(); i++) { + Float *v = listSame->get(i); + if (v != vloat) { + ensureFloatSize (v); + if (v->y != -1 && realY >= v->y && + realY < v->y + v->size.ascent + v->size.descent) { + collides = true; + realY = v->y + v->size.ascent + v->size.descent; + break; + } + } + } + + // Test collisions on the other side. + for (int i = 0; i < listOpp->size(); i++) { + Float *v = listOpp->get(i); + // Note: Since v is on the opposite side, the condition + // v != vloat (used above) is always true. + ensureFloatSize (v); + if (v->y != -1 && realY >= v->y && + realY < v->y + v->size.ascent + v->size.descent && + // For the other size, horizontal dimensions have to be + // considered, too. + v->size.width + v->borderWidth + + vloat->size.width + vloat->borderWidth > availWidth) { + collides = true; + realY = v->y + v->size.ascent + v->size.descent; + break; + } + } + } while (collides); + + vloat->y = realY; + + //printf (" => %d\n", vloat->y); + + if (oldY == -1) + containingBlock->borderChanged (vloat->y); + else if (vloat->y != oldY) + containingBlock->borderChanged (min (oldY, vloat->y)); +} + +/** + * Get the left border for the vertical position of *y*, for a height + * of *h", based on floats. + * + * The border includes marging/border/padding of the containging + * block, but is 0 if there is no float, so a caller should also + * consider other borders. + */ +int OutOfFlowMgr::getLeftBorder (int y, int h) +{ + return getBorder (leftFloats, y, h); +} + +/** + * 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 (int y, int h) +{ + return getBorder (rightFloats, y, h); +} + +int OutOfFlowMgr::getBorder (Vector<Float> *list, int y, int h) +{ + int border = 0; + + // To be a bit more efficient, one could use linear search to find + // the first affected float. + for(int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + ensureFloatSize (vloat); + + if(vloat->y != -1 && y + h >= vloat->y && + y < vloat->y + vloat->size.ascent + vloat->size.descent) { + // 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. + border = max (border, vloat->size.width + vloat->borderWidth); + } + // To be a bit more efficient, the loop could be stopped when + // (i) at least one float has been found, and (ii) the next float is + // below y + h. + } + + return border; +} + +void OutOfFlowMgr::ensureFloatSize (Float *vloat) +{ + if (vloat->dirty) { + // 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 + (availWidth * perLengthVal (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 (availWidth != -1 && width > availWidth) + width = availWidth; + // 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); + + vloat->borderWidth = calcBorderDiff (vloat); + + //printf (" Float at %d: %d x (%d + %d)\n", + // vloat->y, vloat->width, vloat->ascent, vloat->descent); + + vloat->dirty = false; + } +} + +/** + * Calculate the border diff, i. e., for left floats, the difference + * between the left side of the allocation of the containing block and + * the left side of the allocation of the float, and likewise for + * right floats. Both values are positive. + * + * From how I have understood the CSS specification, the generating + * block should be included. (In the case I am wrong: a change of this + * method should be sufficient.) + */ +int OutOfFlowMgr::calcBorderDiff (Float *vloat) +{ + switch (vloat->widget->getStyle()->vloat) { + case FLOAT_LEFT: + { + int d = containingBlock->asWidget()->getStyle()->boxOffsetX(); + for (Widget *w = vloat->generatingBlock; + w != containingBlock->asWidget(); w = w->getParent()) + d += w->getStyle()->boxOffsetX(); + return d; + } + + case FLOAT_RIGHT: + { + int d = containingBlock->asWidget()->getStyle()->boxRestWidth(); + for (Widget *w = vloat->generatingBlock; + w != containingBlock->asWidget(); w = w->getParent()) + d += w->getStyle()->boxRestWidth(); + return d; + } + + default: + // Only used for floats. + assertNotReached(); + return 0; // compiler happiness + } +} + +} // namespace dw diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh new file mode 100644 index 00000000..13fc0877 --- /dev/null +++ b/dw/outofflowmgr.hh @@ -0,0 +1,115 @@ +#ifndef __DW_OUTOFFLOWMGR_HH__ +#define __DW_OUTOFFLOWMGR_HH__ + +#include "core.hh" + +namespace dw { + +/** + * \brief Represents additional data for containing blocks. + */ +class OutOfFlowMgr +{ +public: + class ContainingBlock + { + public: + virtual void borderChanged (int y) = 0; + virtual core::Widget *asWidget () = 0; + }; + +private: + ContainingBlock *containingBlock; + int availWidth, availAscent, availDescent; + + class Float: public lout::object::Object + { + public: + core::Widget *widget, *generatingBlock; + // width includes border of the containing block + int y, borderWidth; + core::Requisition size; + bool dirty; + }; + + //lout::container::typed::HashTable<lout::object::TypedPointer + // <dw::core::Widget>, Float> *floatsByWidget; + lout::container::typed::Vector<Float> *leftFloats, *rightFloats; + + Float *findFloatByWidget (core::Widget *widget); + lout::container::typed::Vector<Float> *getFloatList (core::Widget *widget); + lout::container::typed::Vector<Float> *getOppositeFloatList (core::Widget + *widget); + void ensureFloatSize (Float *vloat); + int calcBorderDiff (Float *vloat); + + void draw (lout::container::typed::Vector<Float> *list, + core::View *view, core::Rectangle *area); + core::Widget *getWidgetAtPoint (lout::container::typed::Vector<Float> *list, + int x, int y, int level); + int getBorder (lout::container::typed::Vector<Float> *list, int y, int h); + + inline static bool isRefFloat (int ref) + { return ref != -1 && (ref & 1) == 1; } + inline static bool isRefLeftFloat (int ref) + { return ref != -1 && (ref & 3) == 1; } + inline static bool isRefRightFloat (int ref) + { return ref != -1 && (ref & 3) == 3; } + + inline static int createRefLeftFloat (int index) + { return (index << 2) | 1; } + inline static int createRefRightFloat (int index) + { return (index << 2) | 3; } + + inline static int getFloatIndexFromRef (int ref) + { return ref == -1 ? ref : (ref >> 2); } + +public: + OutOfFlowMgr (ContainingBlock *containingBlock); + ~OutOfFlowMgr (); + + void sizeAllocate(core::Allocation *containingBlockAllocation); + void draw (core::View *view, core::Rectangle *area); + void queueResize(int ref); + + void markSizeChange (int ref); + void markExtremesChange (int ref); + core::Widget *getWidgetAtPoint (int x, int y, int level); + + static bool isWidgetOutOfFlow (core::Widget *widget); + void addWidget (core::Widget *widget, core::Widget *generatingBlock); + + void tellNoPosition (core::Widget *widget); + void tellPosition (core::Widget *widget, int y); + + void setWidth (int width) { availWidth = width; } + void setAscent (int ascent) { availAscent = ascent; } + void setDescent (int descent) { availDescent = descent; } + + void getSize (int cbWidth, int cbHeight, int *oofWidth, int *oofHeight); + + int getLeftBorder (int y, int h); + + int getRightBorder (int y, int h); + + 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); } + + //inline static bool isRefOutOfFlow (int ref) { return false; } + //inline static int createRefNormalFlow (int lineNo) { return lineNo; } + //inline static int getLineNoFromRef (int ref) { return ref; } + + // for iterators + inline int getNumWidgets () { + return leftFloats->size() + rightFloats->size(); } + inline core::Widget *getWidget (int i) { + return i < leftFloats->size() ? leftFloats->get(i)->widget : + rightFloats->get(i - leftFloats->size())->widget; } +}; + +} // namespace dw + +#endif // __DW_OUTOFFLOWMGR_HH__ diff --git a/dw/style.cc b/dw/style.cc index 6c0abda2..2f64e468 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> @@ -49,6 +47,8 @@ void StyleAttrs::initValues () valign = VALIGN_BASELINE; backgroundColor = NULL; width = height = lineHeight = LENGTH_AUTO; + vloat = FLOAT_NONE; + clear = CLEAR_NONE; textIndent = 0; margin.setVal (0); borderWidth.setVal (0); @@ -75,6 +75,8 @@ void StyleAttrs::resetValues () valign = VALIGN_BASELINE; textAlignChar = '.'; + vloat = FLOAT_NONE; /** \todo Correct? Check specification. */ + clear = CLEAR_NONE; /** \todo Correct? Check specification. */ backgroundColor = NULL; width = LENGTH_AUTO; height = LENGTH_AUTO; @@ -120,6 +122,8 @@ bool StyleAttrs::equals (object::Object *other) { valign == otherAttrs->valign && textAlignChar == otherAttrs->textAlignChar && textTransform == otherAttrs->textTransform && + vloat == otherAttrs->vloat && + clear == otherAttrs->clear && hBorderSpacing == otherAttrs->hBorderSpacing && vBorderSpacing == otherAttrs->vBorderSpacing && wordSpacing == otherAttrs->wordSpacing && @@ -160,6 +164,8 @@ int StyleAttrs::hashValue () { valign + textAlignChar + textTransform + + vloat + + clear + hBorderSpacing + vBorderSpacing + wordSpacing + @@ -252,6 +258,8 @@ void Style::copyAttrs (StyleAttrs *attrs) valign = attrs->valign; textAlignChar = attrs->textAlignChar; textTransform = attrs->textTransform; + vloat = attrs->vloat; + clear = attrs->clear; hBorderSpacing = attrs->hBorderSpacing; vBorderSpacing = attrs->vBorderSpacing; wordSpacing = attrs->wordSpacing; diff --git a/dw/style.hh b/dw/style.hh index 7c00ac1f..41784c79 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -281,7 +281,6 @@ enum ListStylePosition { LIST_STYLE_POSITION_INSIDE, LIST_STYLE_POSITION_OUTSIDE }; - enum ListStyleType { LIST_STYLE_TYPE_DISC, LIST_STYLE_TYPE_CIRCLE, @@ -333,6 +332,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. * @@ -450,6 +462,9 @@ public: VAlignType valign; char textAlignChar; /* In future, strings will be supported. */ TextTransform textTransform; + + FloatType vloat; /* "float" is a keyword. */ + ClearType clear; int hBorderSpacing, vBorderSpacing, wordSpacing; Length width, height, lineHeight, textIndent; diff --git a/dw/table.cc b/dw/table.cc index c21b7a09..c4108e47 100644 --- a/dw/table.cc +++ b/dw/table.cc @@ -1103,7 +1103,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; } } @@ -1125,8 +1125,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; } @@ -1140,7 +1140,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; } @@ -1152,8 +1152,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; } @@ -1167,7 +1167,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/tablecell.cc b/dw/tablecell.cc index 90dc310d..d1a8e3e1 100644 --- a/dw/tablecell.cc +++ b/dw/tablecell.cc @@ -102,7 +102,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 068a7c0d..fc298562 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -24,13 +24,14 @@ #include "../lout/unicode.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; @@ -144,6 +145,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); @@ -161,6 +163,9 @@ Textblock::Textblock (bool limitTextWidth) availAscent = 100; availDescent = 0; + diffXToContainingBlock = restWidthToContainingBlock = + diffYToContainingBlock = -1; + this->limitTextWidth = limitTextWidth; for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { @@ -181,8 +186,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? */ word->style->unref (); word->spaceStyle->unref (); } @@ -198,6 +204,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; @@ -252,6 +261,21 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) requisition->ascent += 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; @@ -264,7 +288,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 { @@ -338,7 +362,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++) { @@ -348,7 +372,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: @@ -420,6 +444,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) } } + if(outOfFlowMgr) + outOfFlowMgr->sizeAllocate(allocation); + for (int i = 0; i < anchors->size(); i++) { Anchor *anchor = anchors->getRef(i); int y; @@ -450,47 +477,118 @@ 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; + diffXToContainingBlock = restWidthToContainingBlock = + diffYToContainingBlock = 0; + 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) { + diffXToContainingBlock = restWidthToContainingBlock = + diffYToContainingBlock = 0; + 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) @@ -504,10 +602,13 @@ void Textblock::setWidth (int width) // words->size()); availWidth = width; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; redrawY = 0; } + + if (outOfFlowMgr) + outOfFlowMgr->setWidth (width); } void Textblock::setAscent (int ascent) @@ -519,9 +620,12 @@ void Textblock::setAscent (int ascent) // words->size()); availAscent = ascent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; } + + if (outOfFlowMgr) + outOfFlowMgr->setAscent (ascent); } void Textblock::setDescent (int descent) @@ -533,9 +637,12 @@ void Textblock::setDescent (int descent) // words->size()); availDescent = descent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; } + + if (outOfFlowMgr) + outOfFlowMgr->setDescent (descent); } bool Textblock::buttonPressImpl (core::EventButton *event) @@ -650,11 +757,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; @@ -734,8 +841,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; @@ -767,6 +875,32 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size) availAscent = this->availAscent - getStyle()->boxDiffHeight (); availDescent = this->availDescent; + // The following should be changed, made more general, and so on. + // + // With floats, the implementation of Textblock::sizeRequestImpl + // becomes special: Textblock::sizeRequestImpl will rewrap lines, + // for which the line widths are needed, which depend on floats; + // however, if a text block is not its containing block, the floats + // positions depend on the position text block within the text + // container. + // + // This is something new, and should be implemented in a more + // general way, perhaps resembling size hints. + + // Tell a child textblock its position within the containing block. + + if (widget->instanceOf (Textblock::CLASS_ID)) { + Textblock *tb = (Textblock*)widget; + if (tb != tb->containingBlock) { + tb->diffXToContainingBlock = + diffXToContainingBlock + getStyle()->boxOffsetX(); + tb->restWidthToContainingBlock = + restWidthToContainingBlock + getStyle()->boxRestWidth();; + tb->diffYToContainingBlock = + diffYToContainingBlock + topOfPossiblyMissingLine (lines->size ()); + } + } + if (widget->usesHints ()) { widget->setWidth (availWidth); widget->setAscent (availAscent); @@ -1101,7 +1235,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; @@ -1112,10 +1246,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; @@ -1259,7 +1393,7 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) 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; @@ -1305,6 +1439,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area) drawLine (line, view, area); } + + if(outOfFlowMgr) + outOfFlowMgr->draw(view, area); } /** @@ -1704,28 +1841,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 ...\n", + widget->getClassName(), widget, this); + + if (OutOfFlowMgr::isWidgetOutOfFlow (widget)) { + PRINTF (" -> out of flow.\n"); + + if (containingBlock->outOfFlowMgr == NULL) { + containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock); + containingBlock->outOfFlowMgr->setWidth (containingBlock->availWidth); + containingBlock->outOfFlowMgr->setAscent + (containingBlock->availAscent); + containingBlock->outOfFlowMgr->setDescent + (containingBlock->availDescent); + } + + widget->setParent (containingBlock); + widget->setGenerator (this); + containingBlock->outOfFlowMgr->addWidget (widget, this); + Word *word = addWord (0, 0, 0, false, style); + word->content.type = core::Content::WIDGET_OOF_REF; + word->content.widget = widget; + word->style = style; + } else { + PRINTF (" -> within flow.\n"); - word->content.type = core::Content::WIDGET; - word->content.widget = widget; + widget->setParent (this); - //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1, - // word->content.widget); + 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", @@ -1865,7 +2022,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 @@ -1874,23 +2031,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 = @@ -1899,7 +2056,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; } } @@ -1907,6 +2065,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) } /* Otherwise continue to examine parents. */ } + /* Return in any case. */ return; } @@ -1955,7 +2114,6 @@ void Textblock::addLinebreak (core::style::Style *style) processWord (words->size () - 1); } - /** * \brief Search recursively through widget. * @@ -1964,6 +2122,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; @@ -1974,6 +2136,14 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) return NULL; } + // 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 = findLineIndex (y - allocation.y); if (lineIndex < 0 || lineIndex >= lines->size ()) { @@ -1985,7 +2155,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); @@ -2066,7 +2236,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, @@ -2118,4 +2288,137 @@ void Textblock::queueDrawRange (int index1, int index2) } } +void Textblock::borderChanged (int y) +{ + PRINTF ("[%p] Border has changed: %d\n", this, y); + borderChanged (y, true); + PRINTF ("[%p] Done.\n", this); +} + +void Textblock::borderChanged (int yWidget, bool extremesChanges) +{ + PRINTF ("[%p] Border has changed: %d (extremes: %s)\n", + this, yWidget, extremesChanges ? "true" : "false"); + + // Notice that this method is, unlike the other "borderChanged", + // called (i) with canvas coordinates, not widget coordinates, and + // (ii) for all nested textblocks, not only the containing block. + + // findLineIndex expects widget coordinates + int lineIndex = findLineIndex (yWidget); + // Nothing to do at all, when lineIndex >= lines->size (), + // i. e. the change is below the bottom od this widget. + if (lineIndex < lines->size ()) { + int wrapLineIndex; + if (lineIndex < 0) + // Rewrap all. + wrapLineIndex = 0; + else + wrapLineIndex = lineIndex; + + PRINTF ("[%p] Rewrapping from line %d.\n", this, wrapLineIndex); + queueResize (OutOfFlowMgr::createRefNormalFlow (wrapLineIndex), + extremesChanges); + + // lines->size () + 1 here, to get a possibly "missing" line. + // TODO: May there me more than one missing line? Should perhaps + // reworked again. + + // We iterate over the lines to get the top of the line, as + // vertical position of the widget (provided that this is the + // only widget in the line). The allocation cannot be used here, + // since it cannot be assumed that the widget has been + // allocated. + for (int lineNo = wrapLineIndex; lineNo < lines->size () + 1; lineNo++) { + Textblock *childBlock = getTextblockForLine (lineNo); + if (childBlock) + // extremes only change for the containing block, so we pass + // extremesChanges = false for all other widgets. + childBlock->borderChanged (yWidget + - topOfPossiblyMissingLine (lineNo), + false); + } + } +} + +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 ()) { + for (int wordIndex = firstWord; wordIndex <= lastWord; + wordIndex++) { + Word *word = words->getRef (wordIndex); + + if (word->content.type == core::Content::WIDGET_IN_FLOW && + word->content.widget->instanceOf (Textblock::CLASS_ID)) { + //printf ("[%p] (line %d of %d (from %d to %d), word %d) ", + // this, lineNo, lines->size (), firstWord, lastWord, + // wordIndex); + //printWordShort (word); + //printf ("\n"); + + return (Textblock*)word->content.widget; + } + } + } + + return NULL; +} + +/** + * Includes margin, border, and padding. + */ +int Textblock::topOfPossiblyMissingLine (int lineNo) +{ + if (lineNo == 0) + return getStyle()->boxOffsetY(); + else { + Line *prevLine = lines->getRef (lineNo - 1); + return prevLine->top + prevLine->boxAscent + prevLine->boxDescent + + prevLine->breakSpace + getStyle()->boxOffsetY(); + } +} + +int Textblock::heightOfPossiblyMissingLine (int lineNo) +{ + if (lineNo < lines->size()) { + // An existing line. + Line *line = lines->getRef (lineNo); + return line->boxAscent + line->boxDescent; + } else if (lineNo == lines->size()) { + // The line to be constructed: some words exist, but not the + // line. Accumulate the word heights. TODO Could be faster by + // accumulating them when words are added. + + // Furthermore, this is in some cases incomplete: see + // doc/dw-out-of-flow.doc. + + int h = 1; + int firstWord = lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0; + for (int i = firstWord; i < words->size(); i++) { + Word *word = words->getRef (i); + h = misc::max (h, word->size.ascent + word->size.descent); + } + return h; + } else + return 1; +} + +core::Widget *Textblock::asWidget () +{ + return this; +} + } // namespace dw diff --git a/dw/textblock.hh b/dw/textblock.hh index d5b64e42..e5423c98 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> * @@ -146,7 +150,7 @@ namespace dw { * necessary, or otherwise the line from which a rewrap is necessary. * */ -class Textblock: public core::Widget +class Textblock: public core::Widget, public OutOfFlowMgr::ContainingBlock { private: /** @@ -236,6 +240,9 @@ private: static const char *hyphenDrawChar; + Textblock *containingBlock; + OutOfFlowMgr *outOfFlowMgr; + protected: struct Paragraph { @@ -275,7 +282,7 @@ protected: /* "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; + breakSpace, leftOffset, offsetCompleteWidget; /* This is similar to descent, but includes the bottom margins of the * widgets within this line. */ @@ -359,13 +366,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::misc::Comparable *other); @@ -375,6 +383,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; @@ -424,7 +433,10 @@ protected: /* These values are set by set_... */ int availWidth, availAscent, availDescent; - int wrapRefLines, wrapRefParagraphs; /* [0 based] */ + int wrapRefLines, wrapRefParagraphs; /* 0-based. Important: Both + are the line numbers, not + the value stored in + parentRef. */ lout::misc::SimpleVector <Line> *lines; lout::misc::SimpleVector <Paragraph> *paragraphs; @@ -480,25 +492,68 @@ protected: core::Requisition *size); /** - * \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 getTextblockForLine (lineNo) == NULL; + } + + void borderChanged (int yWidget, bool extremesChanges); + + int diffXToContainingBlock, restWidthToContainingBlock, + diffYToContainingBlock; + + inline int lineLeftBorder (int lineNo) + { + assert (diffXToContainingBlock != -1); + assert (diffYToContainingBlock != -1); + + // Note that the line must not exist yet (but unless it is not + // the first line, the previous line, lineNo - 1, must). + int resultFromOOFM; + if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (lineNo)) + resultFromOOFM = + containingBlock->outOfFlowMgr->getLeftBorder + (topOfPossiblyMissingLine (lineNo) + diffYToContainingBlock, + heightOfPossiblyMissingLine (lineNo)) + - diffXToContainingBlock; + else + resultFromOOFM = 0; + + // TODO: line->leftOffset is not regarded, which is correct, depending + // on where this method is called. Document; perhaps rename this method. + // (Update: was renamed.) + return innerPadding + + (lineNo == 0 ? line1OffsetEff : 0) + + lout::misc::max (getStyle()->boxOffsetX(), resultFromOOFM); + } + + inline int lineRightBorder (int lineNo) { - return lineXOffsetContents (line) + getStyle()->boxOffsetX (); + assert (restWidthToContainingBlock != -1); + assert (diffYToContainingBlock != -1); + + // Similar to lineLeftBorder(). + + int resultFromOOFM; + // TODO sizeRequest? + if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (lineNo)) + resultFromOOFM = + containingBlock->outOfFlowMgr->getRightBorder + (topOfPossiblyMissingLine (lineNo) + diffYToContainingBlock, + heightOfPossiblyMissingLine (lineNo)) + - restWidthToContainingBlock; + else + resultFromOOFM = 0; + + return lout::misc::max (getStyle()->boxRestWidth(), resultFromOOFM); } inline int lineYOffsetWidgetAllocation (Line *line, @@ -549,6 +604,12 @@ 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); + int topOfPossiblyMissingLine (int lineNo); + int heightOfPossiblyMissingLine (int lineNo); + bool sendSelectionEvent (core::SelectionState::EventType eventType, core::MousePositionEvent *event); @@ -556,6 +617,12 @@ protected: int *maxOfMinWidth, int *sumOfMaxWidth); void processWord (int wordIndex); virtual void wordWrap (int wordIndex, bool wrapAll); + void wrapWidgetOofRef (int wordIndex); + int searchMinBap (int firstWord, int lastWordm, int penaltyIndex, + bool correctAtEnd); + int considerHyphenation (int breakPos); + bool isHyphenationCandidate (Word *word); + void handleWordExtremes (int wordIndex); void correctLastWordExtremes (); @@ -575,6 +642,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); @@ -594,6 +663,7 @@ protected: core::style::Style *style, int numBreaks, int *breakPos, core::Requisition *wordSize); + static bool isContainingBlock (Widget *widget); public: static int CLASS_ID; @@ -639,6 +709,10 @@ public: void changeLinkColor (int link, int newColor); void changeWordStyle (int from, int to, core::style::Style *style, bool includeFirstSpace, bool includeLastSpace); + + // From OutOfFlowMgr::ContainingBlock: + void borderChanged (int y); + core::Widget *asWidget (); }; } // namespace dw diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc index 245df374..285921b2 100644 --- a/dw/textblock_iterator.cc +++ b/dw/textblock_iterator.cc @@ -33,20 +33,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; @@ -54,12 +68,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(misc::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 () @@ -69,15 +91,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; } @@ -88,125 +147,180 @@ 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); - } - 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); + 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); + } + 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); + } } + 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 35273565..e35744dd 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -210,21 +210,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) @@ -315,6 +301,12 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, PRINTF ("[%p] ADD_LINE (%d, %d) => %d\n", this, firstWord, lastWord, lines->size ()); + //for (int i = firstWord; i <= lastWord; i++) { + // printf (" word %d: ", i); + // printWord (words->getRef (i)); + // printf ("\n"); + //} + Word *lastWordOfLine = words->getRef(lastWord); // Word::totalWidth includes the hyphen (which is what we want here). int lineWidth = lastWordOfLine->totalWidth; @@ -374,6 +366,25 @@ 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 resultFromOOFM; + if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (line)) + resultFromOOFM = + containingBlock->outOfFlowMgr->getLeftBorder + (line->top + getStyle()->boxOffsetY() + diffYToContainingBlock, + line->boxAscent + line->boxDescent) + - diffXToContainingBlock; + else + resultFromOOFM = 0; + line->offsetCompleteWidget = + innerPadding + line->leftOffset + (lineIndex == 0 ? line1OffsetEff : 0) + + misc::max (getStyle()->boxOffsetX(), resultFromOOFM); + 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", @@ -452,14 +463,12 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) PRINTF ("[%p] WORD_WRAP (%d, %s)\n", this, wordIndex, wrapAll ? "true" : "false"); - Word *word; - if (!wrapAll) removeTemporaryLines (); initLine1Offset (wordIndex); - word = words->getRef (wordIndex); + Word *word = words->getRef (wordIndex); word->effSpace = word->origSpace; accumulateWordData (wordIndex); @@ -468,16 +477,59 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) //printWord (word); //printf ("\n"); + if (word->content.type == core::Content::WIDGET_OOF_REF) + wrapWidgetOofRef (wordIndex); + int penaltyIndex = calcPenaltyIndexForNewLine (); bool newLine; do { + // This variable 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; + if (containingBlock->outOfFlowMgr == NULL) { + thereWillBeMoreSpace = false; + PRINTF (" thereWillBeMoreSpace = false (no OOFM)\n"); + } else { + assert (diffXToContainingBlock != -1); + assert (restWidthToContainingBlock != -1); + assert (diffYToContainingBlock != -1); + + int y = + topOfPossiblyMissingLine (lines->size ()) + diffYToContainingBlock; + int h = heightOfPossiblyMissingLine (lines->size ()); + int l = + containingBlock->outOfFlowMgr->getLeftBorder (y, h) + - diffXToContainingBlock; + int r = + containingBlock->outOfFlowMgr->getRightBorder (y, h) + - restWidthToContainingBlock; + + thereWillBeMoreSpace = l > 0 || r > 0; + + PRINTF (" thereWillBeMoreSpace = %s (y = %d, l = %d, r = %d)\n", + thereWillBeMoreSpace ? "true" : "false", y, l, r); + } + 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; @@ -493,13 +545,21 @@ void 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"); @@ -516,118 +576,68 @@ void 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 { - PRINTF (" searching from %d to %d\n", firstIndex, searchUntil); - - int breakPos = -1; - for (int i = firstIndex; i <= searchUntil; i++) { - Word *w = words->getRef(i); - - //printf (" %d (of %d): ", i, words->size ()); - //printWord (w); - //printf ("\n"); - - if (breakPos == -1 || - w->badnessAndPenalty.compareTo - (penaltyIndex, - &words->getRef(breakPos)->badnessAndPenalty) <= 0) - // "<=" instead of "<" in the next lines tends to result in - // more words per line -- theoretically. Practically, the - // case "==" will never occur. - breakPos = i; - } - - PRINTF (" breakPos = %d\n", breakPos); - - if (wrapAll && searchUntil == words->size () - 1) { - // Since no break and no space is added, the last word - // will have a penalty of inf. Actually, it should be - // less, since it is the last word. However, since more - // words may follow, the penalty is not changesd, but - // here, the search is corrected (maybe only - // temporary). - - // (Notice that it was once (temporally) set to -inf, - // not 0, but this will make e.g. test/table-1.html not - // work.) - Word *lastWord = words->getRef (searchUntil); - BadnessAndPenalty correctedBap = lastWord->badnessAndPenalty; - correctedBap.setPenalty (0); - if (correctedBap.compareTo - (penaltyIndex, - &words->getRef(breakPos)->badnessAndPenalty) <= 0) { - breakPos = searchUntil; - PRINTF (" corrected: breakPos = %d\n", breakPos); - } - } - - int hyphenatedWord = -1; - Word *word1 = words->getRef(breakPos); - PRINTF ("[%p] line (broken at word %d): ", this, breakPos); - //word1->badnessAndPenalty.print (); - PRINTF ("\n"); - - if (word1->badnessAndPenalty.lineTight () && - (word1->flags & Word::CAN_BE_HYPHENATED) && - word1->style->x_lang[0] && - word1->content.type == core::Content::TEXT && - Hyphenator::isHyphenationCandidate (word1->content.text)) - hyphenatedWord = breakPos; - - if (word1->badnessAndPenalty.lineLoose () && - breakPos + 1 < words->size ()) { - Word *word2 = words->getRef(breakPos + 1); - if ((word2->flags & Word::CAN_BE_HYPHENATED) && - word2->style->x_lang[0] && - word2->content.type == core::Content::TEXT && - Hyphenator::isHyphenationCandidate (word2->content.text)) - hyphenatedWord = breakPos + 1; - } - - PRINTF ("[%p] breakPos = %d, hyphenatedWord = %d\n", - this, breakPos, hyphenatedWord); - - 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 (breakPos); + + PRINTF ("[%p] breakPos = %d, hyphenatedWord = %d\n", + this, breakPos, hyphenatedWord); - // update word pointer as hyphenateWord() can trigger a - // reorganization of the words structure - word = words->getRef (wordIndex); + if(hyphenatedWord == -1) { + addLine (firstIndex, breakPos, tempNewLine); + PRINTF ("[%p] new line %d (%s), from %d to %d\n", + this, lines->size() - 1, + tempNewLine ? "temporally" : "permanently", + firstIndex, breakPos); + lineAdded = true; + penaltyIndex = calcPenaltyIndexForNewLine (); + } else { + // TODO hyphenateWord() should return whether + // something has changed at all. So that a second + // run, with !word->canBeHyphenated, is unnecessary. + // TODO Update: for this, searchUntil == 0 should be + // checked. + 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); + } + + 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 @@ -644,7 +654,8 @@ void 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); @@ -652,6 +663,121 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) } } +void Textblock::wrapWidgetOofRef (int wordIndex) +{ + int top; + if (lines->size() == 0) + top = 0; + else { + Line *prevLine = lines->getLastRef (); + top = prevLine->top + prevLine->boxAscent + + prevLine->boxDescent + prevLine->breakSpace; + } + + containingBlock->outOfFlowMgr->tellPosition + (words->getRef(wordIndex)->content.widget, + // "diffYToContainingBlock" is already defined here; it is + // set by the parent, or, when this is the containing block, + // even earlier. + diffYToContainingBlock + top + getStyle()->boxOffsetY()); + + // TODO: compare old/new values of calcAvailWidth(...); + int firstIndex = + lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1; + assert (firstIndex <= wordIndex); + for (int i = firstIndex; i <= wordIndex; i++) + accumulateWordData (i); +} + +int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex, + bool correctAtEnd) +{ + PRINTF (" searching from %d to %d\n", firstWord, lastWord); + + int pos = -1; + + for (int i = firstWord; i <= lastWord; i++) { + Word *w = words->getRef(i); + + //printf (" %d (of %d): ", i, words->size ()); + //printWord (w); + //printf ("\n"); + + if (pos == -1 || + w->badnessAndPenalty.compareTo (penaltyIndex, + &words->getRef(pos) + ->badnessAndPenalty) <= 0) + // "<=" instead of "<" in the next lines tends to result in + // more words per line -- theoretically. Practically, the + // case "==" will never occur. + pos = i; + } + + PRINTF (" found at %d\n", pos); + + if (correctAtEnd && lastWord == words->size () - 1) { + // Since no break and no space is added, the last word will have + // a penalty of inf. Actually, it should be less, since it is + // the last word. However, since more words may follow, the + // penalty is not changed, but here, the search is corrected + // (maybe only temporary). + + // (Notice that it was once (temporally) set to -inf, not 0, but + // this will make e.g. test/table-1.html not work.) + Word *w = words->getRef (lastWord); + BadnessAndPenalty correctedBap = w->badnessAndPenalty; + correctedBap.setPenalty (0); + if (correctedBap.compareTo(penaltyIndex, + &words->getRef(pos)->badnessAndPenalty) <= 0) { + pos = lastWord; + PRINTF (" corrected => %d\n", pos); + } + } + + return pos; +} + + +/** + * Suggest a word to hyphenate, when breaking at breakPos is + * planned. Return a word index or -1, when hyphenation makes no + * sense. + */ +int Textblock::considerHyphenation (int breakPos) +{ + int hyphenatedWord = -1; + + Word *word1 = words->getRef(breakPos); + PRINTF ("[%p] line (broken at word %d): ", this, breakPos); + //word1->badnessAndPenalty.print (); + PRINTF ("\n"); + + // A tight line: maybe, after hyphenation, some parts of the last + // word of this line can be put into the next line. + if (word1->badnessAndPenalty.lineTight () && + isHyphenationCandidate (word1)) + hyphenatedWord = breakPos; + + // A loose line: maybe, after hyphenation, some parts of the first + // word of the next line can be put into this line. + if (word1->badnessAndPenalty.lineLoose () && + breakPos + 1 < words->size ()) { + Word *word2 = words->getRef(breakPos + 1); + if (isHyphenationCandidate (word2)) + hyphenatedWord = breakPos + 1; + } + + return hyphenatedWord; +} + +bool Textblock::isHyphenationCandidate (Word *word) +{ + return (word->flags & Word::CAN_BE_HYPHENATED) && + word->style->x_lang[0] && + word->content.type == core::Content::TEXT && + Hyphenator::isHyphenationCandidate (word->content.text); +} + /** * Counter part to wordWrap(), but for extremes, not size calculation. */ @@ -817,9 +943,16 @@ int Textblock::hyphenateWord (int wordIndex) PRINTF (" [%d] + nothing\n", wordIndex + i); } } + } + + // 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"); @@ -839,8 +972,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); @@ -855,7 +992,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 = @@ -879,7 +1016,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); @@ -945,18 +1083,27 @@ void Textblock::accumulateWordData (int wordIndex) int Textblock::calcAvailWidth (int lineIndex) { - int availWidth = - this->availWidth - getStyle()->boxDiffWidth() - innerPadding; + 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); + // TODO if the result is too small, but only in some cases + // (e. g. because of floats), the caller should skip a + // line. General distinction? + int leftBorder = lineLeftBorder (lineIndex); + int rightBorder = lineRightBorder (lineIndex); + 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; } @@ -973,7 +1120,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 { @@ -1064,8 +1211,14 @@ void Textblock::rewrap () 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_OOF_REF) + containingBlock->outOfFlowMgr->tellNoPosition (word->content.widget); + } + + for (int i = firstWord; i < words->size (); i++) { + Word *word = words->getRef (i); + + if (word->content.type == core::Content::WIDGET_IN_FLOW) calcWidgetSize (word->content.widget, &word->size); wordWrap (i, false); diff --git a/dw/types.cc b/dw/types.cc index 86836bc1..a2f0737e 100644 --- a/dw/types.cc +++ b/dw/types.cc @@ -268,5 +268,58 @@ 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::print (Content *content) +{ + switch(content->type) { + case START: + printf ("<start>"); + break; + case END: + printf ("<end>"); + break; + case TEXT: + printf ("\"%s\"", content->text); + break; + case WIDGET_IN_FLOW: + printf ("<widget in flow: %p (%s)>", + content->widget, content->widget->getClassName()); + break; + case WIDGET_OOF_REF: + printf ("<widget oof ref: %p (%s)>", + content->widget, content->widget->getClassName()); + break; + case WIDGET_OOF_CONT: + printf ("<widge oof cont: %p (%s)>", + content->widget, content->widget->getClassName()); + break; + case BREAK: + printf ("<break>"); + break; + default: + printf ("<%d?>", content->type); + break; + } +} + +void Content::printMask (Type mask) +{ + printf ("%s:%s:%s:%s:%s:%s:%s", + (mask & START) ? "st" : "--", + (mask & END) ? "en" : "--", + (mask & TEXT) ? "tx" : "--", + (mask & WIDGET_IN_FLOW) ? "wf" : "--", + (mask & WIDGET_OOF_REF) ? "Wr" : "--", + (mask & WIDGET_OOF_CONT) ? "Wc" : "--", + (mask & BREAK) ? "br" : "--"); +} + } // namespace core } // namespace dw diff --git a/dw/types.hh b/dw/types.hh index f04fc138..0e664895 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,10 @@ struct Content Widget *widget; int breakSpace; }; + + static Content::Type maskForSelection (bool followReferences); + static void print (Content *content); + static void printMask (Type mask); }; } // namespace core diff --git a/dw/widget.cc b/dw/widget.cc index 1d9f96e4..8f5bc5d5 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -39,7 +39,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; @@ -108,6 +108,8 @@ void Widget::setParent (Widget *parent) if (!buttonSensitiveSet) buttonSensitive = parent->buttonSensitive; + notifySetParent(); + //DBG_OBJ_ASSOC (widget, parent); //printf ("The %s %p becomes a child of the %s %p\n", // getClassName(), this, parent->getClassName(), parent); @@ -116,7 +118,8 @@ void Widget::setParent (Widget *parent) 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); } @@ -127,10 +130,8 @@ void Widget::queueResize (int ref, bool extremesChanged) { Widget *widget2, *child; - //DEBUG_MSG (DEBUG_SIZE, - // "a %stop-level %s with parent_ref = %d has changed its size\n", - // widget->parent ? "non-" : "", - // gtk_type_name (GTK_OBJECT_TYPE (widget)), widget->parent_ref); + //printf("The %stop-level %s %p with parentRef = %d has changed its size.\n", + // parent ? "non-" : "", getClassName(), this, parentRef); setFlags (NEEDS_RESIZE); setFlags (NEEDS_ALLOCATE); @@ -148,12 +149,10 @@ void Widget::queueResize (int ref, bool extremesChanged) widget2->markSizeChange (child->parentRef); widget2->setFlags (NEEDS_ALLOCATE); - //DEBUG_MSG (DEBUG_ALLOC, - // "setting DW_NEEDS_ALLOCATE for a %stop-level %s " - // "with parent_ref = %d\n", - // widget2->parent ? "non-" : "", - // gtk_type_name (GTK_OBJECT_TYPE (widget2)), - // widget2->parent_ref); + //printf (" Setting DW_NEEDS_RESIZE and NEEDS_ALLOCATE for the " + // "%stop-level %s %p with parentRef = %d\n", + // widget2->parent ? "non-" : "", widget2->getClassName(), widget2, + // widget2->parentRef); if (extremesChanged) { widget2->setFlags (EXTREMES_CHANGED); @@ -172,6 +171,10 @@ void Widget::queueResize (int ref, bool extremesChanged) */ void Widget::sizeRequest (Requisition *requisition) { + //printf ("The %stop-level %s %p with parentRef = %d: needsResize: %s\n", + // parent ? "non-" : "", getClassName(), this, parentRef, + // needsResize () ? "true" : "false"); + if (needsResize ()) { /** \todo Check requisition == &(this->requisition) and do what? */ sizeRequestImpl (requisition); @@ -183,6 +186,9 @@ void Widget::sizeRequest (Requisition *requisition) DBG_OBJ_SET_NUM (this, "requisition->descent", requisition->descent); } else *requisition = this->requisition; + + //printf (" ==> Result: %d x (%d + %d)\n", + // requisition->width, requisition->ascent, requisition->descent); } /** @@ -507,11 +513,9 @@ Widget *Widget::getWidgetAtPoint (int x, int y, int level) Iterator *it; Widget *childAtPoint; - //_MSG ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n", - // 3 * level, "", gtk_type_name (GTK_OBJECT_TYPE (widget)), widget, - // allocation.x, allocation.y, - // allocation.width, allocation.ascent, - // allocation.descent); + //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); if (x >= allocation.x && y >= allocation.y && @@ -524,7 +528,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, @@ -568,6 +574,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 6942221b..58306c92 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -79,6 +79,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; @@ -178,6 +186,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); @@ -243,6 +254,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; } @@ -282,6 +295,8 @@ public: int getLevel (); 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/identity.cc b/lout/identity.cc index 7c650b24..ebe95ef0 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> @@ -78,6 +76,7 @@ void IdentifiableObject::registerName (const char *className, int *classId) } this->classId = klass->id; + *classId = klass->id; currentlyConstructedClass = klass; } diff --git a/src/cssparser.cc b/src/cssparser.cc index eda45472..5096fb96 100644 --- a/src/cssparser.cc +++ b/src/cssparser.cc @@ -76,6 +76,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 @@ -183,7 +187,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}, diff --git a/src/html.cc b/src/html.cc index 41ccf95f..7819edb7 100644 --- a/src/html.cc +++ b/src/html.cc @@ -353,9 +353,11 @@ bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag, int tagsize) static void Html_add_textblock(DilloHtml *html, int space) { Textblock *textblock = new Textblock (prefs.limit_text_width); + Style *style = html->styleEngine->style (); HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ()); - HT2TB(html)->addWidget (textblock, html->styleEngine->style ()); + HT2TB(html)->addWidget (textblock, style); // Works also for floats etc. + HT2TB(html)->addParbreak (space, html->styleEngine->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 23294ed1..5407408f 100644 --- a/src/styleengine.cc +++ b/src/styleengine.cc @@ -504,6 +504,9 @@ 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_LINE_HEIGHT: if (p->type == CSS_TYPE_ENUM) { //only valid enum value is "normal" attrs->lineHeight = dw::core::style::LENGTH_AUTO; diff --git a/test/Makefile.am b/test/Makefile.am index 9f38e918..53e3d4ed 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-images-simple \ @@ -50,6 +51,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/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-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? |