aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/Makefile.am1
-rw-r--r--doc/dw-floats-01.pngbin0 -> 11196 bytes
-rw-r--r--doc/dw-out-of-flow.doc177
-rw-r--r--doc/dw-widget-sizes.doc186
-rw-r--r--dw/Makefile.am2
-rw-r--r--dw/findtext.cc4
-rw-r--r--dw/iterator.cc84
-rw-r--r--dw/iterator.hh8
-rw-r--r--dw/layout.cc1
-rw-r--r--dw/outofflowmgr.cc900
-rw-r--r--dw/outofflowmgr.hh145
-rw-r--r--dw/style.cc12
-rw-r--r--dw/style.hh17
-rw-r--r--dw/table.cc14
-rw-r--r--dw/tablecell.cc2
-rw-r--r--dw/textblock.cc401
-rw-r--r--dw/textblock.hh66
-rw-r--r--dw/textblock_iterator.cc331
-rw-r--r--dw/textblock_linebreaking.cc427
-rw-r--r--dw/types.cc53
-rw-r--r--dw/types.hh26
-rw-r--r--dw/widget.cc37
-rw-r--r--dw/widget.hh15
-rw-r--r--lout/identity.cc3
-rw-r--r--src/cssparser.cc6
-rw-r--r--src/html.cc4
-rw-r--r--src/styleengine.cc3
-rw-r--r--test/Makefile.am9
-rw-r--r--test/dw_float_test.cc145
-rw-r--r--test/floats-and-absolute.html51
-rw-r--r--test/floats-and-margins.html40
-rw-r--r--test/floats-table.html24
-rw-r--r--test/floats-worm.html42
-rw-r--r--test/floats1.html46
-rw-r--r--test/floats2.html17
-rw-r--r--test/floats3.html16
-rw-r--r--test/floats4.html63
-rw-r--r--test/floats5.html16
38 files changed, 2947 insertions, 447 deletions
diff --git a/doc/Makefile.am b/doc/Makefile.am
index d644393b..9c42b8de 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -27,6 +27,7 @@ EXTRA_DIST = \
dw-textblock-collapsing-spaces-1-2.png \
dw-textblock-collapsing-spaces-2-1.png \
dw-textblock-collapsing-spaces-2-2.png \
+ dw-floats-01.png \
Cache.txt \
Cookies.txt \
Dillo.txt \
diff --git a/doc/dw-floats-01.png b/doc/dw-floats-01.png
new file mode 100644
index 00000000..16eb63a5
--- /dev/null
+++ b/doc/dw-floats-01.png
Binary files differ
diff --git a/doc/dw-out-of-flow.doc b/doc/dw-out-of-flow.doc
new file mode 100644
index 00000000..1cedbdda
--- /dev/null
+++ b/doc/dw-out-of-flow.doc
@@ -0,0 +1,177 @@
+/** \page dw-out-of-flow Handling Elements Out Of Flow
+
+<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
+padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
+Incomplete; some parts have been removed, beause they are not up to
+date.</div>
+
+<div style="border: 2px solid #ffff00; margin-bottom: 0.5em;
+padding: 0.5em 1em; background-color: #ffffe0"><b>Info:</b>
+Should be incorporated into dw::Textblock.</div>
+
+This texts deals with both floats and absolute positions, which have
+in common, that there is a distinction between generating block and
+containing block (we are here using the same notation as in the
+CSS&nbsp;2 specification). Consider this snippet (regarding floats):
+
+
+ <ul>
+ <li>Some text.</li>
+ <li>
+ <div style="float:right; width=50%">Some longer text, so
+ that the effect described in this passage can be
+ demonstrated.
+ </div>
+ Some more and longer text.</li>
+ <li>Final text. Plus some more to demonstrate how text flows
+ around the float on the right side.</li>
+ </ul>
+
+which may be rendered like this
+
+\image html dw-floats-01.png
+
+The float (the DIV section, yellow in the image) is defined
+("generated") within the list item (blue), so, in CSS 2 terms, the
+list item is the generating block of the float. However, as the image
+shows, the float is not contained by the list item, but another block,
+several levels above (not shown here). In terms of ::dw, this means
+that the dw::Textblock representing the float cannot be a child of the
+dw::Textblock representing the generating block, the list item, since
+the allocation of a child widget must be within the allocation of the
+parent widget. Instead, to each dw::Textblock, another dw::Textblock
+is assigned as the containing box.
+
+(Notice also that other text blocks must regard floats to calculate
+their borders, and so their size. In this example, the following list
+item (green) must consider the position of the float. This is
+discussed in detail in the next section.)
+
+
+Implication for size calculations and allocation
+================================================
+
+*See also:* \ref dw-widget-sizes
+
+The size/position model of ::dw consists mainly of the following two
+steps:
+
+1. First, the size of the toplevel widget is calculated. Size
+ calculation typically depends on the sizes of the widgets, which
+ are calculated recursively, but not more.
+2. After this, the toplevel widget is allocated at position (0, 0),
+ with the preiosly calculated size. Each widget must allocate its
+ children, although the condition for the toplevel widget is not
+ necessary, but each widget may be allocated at every size.
+
+Especially for floats, this model becomes a bit difficult, for reasons
+described below. For the solutions, much is centralized at the level
+of the containing block, which delegates most to an instance of
+dw::OutOfFlowMgr (details below).
+
+**The size of a widget depends on the size not only of the children.**
+In the example above, the last list item (green, following the
+generating list item) must know the size of the the float (which is
+not a child or, generally, descendant, but generally the sibling of an
+anchestor) to determine the borders, which is done in
+dw::Textblock::sizeRequestImpl.
+
+In this situation, it is important not to call, within
+dw::Textblock::sizeRequestImpl of this textblock,
+dw::core::Widget::sizeRequest for the float. Instead, ... (?)
+
+(Actually, dw::Textblock::sizeRequestImpl calls
+dw::core::Widget::sizeRequest for the float, which conflicts with the
+rules defined in \ref dw-widget-sizes; but actually should not cause
+conflicts. Should be revised.)
+
+**The size of a widget depends on the allocation of another widget.**
+In the example above, both list items (blue and green) must know the
+position of the float widget, within dw::Textblock::sizeRequestImpl,
+to calculate the borders. The position, however, is stored in the
+allocation, which is typically calculated later.
+
+Here, two cases must be distinguished. The position of a float is
+always relative to its generating block, so for calculating the
+borders for the generating block, the allocation needs not to be
+know. For other textblocks, it needs to be known, so the calculation
+of the borders will ignore floats generated by other textblocks, until
+all widgets are allocated. The latter will call (when neccessary)
+dw::core::Widget::queueResize, so that all border calculations are
+repeated.
+
+For details see:
+
+- dw::OutOfFlowMgr::getLeftBorder, dw::OutOfFlowMgr::getRightBorder,
+ dw::OutOfFlowMgr::getBorder (called by the first two), and
+ especially, dw::OutOfFlowMgr::getYWidget (called by the latter),
+ where these three cases are distinguished;
+- dw::OutOfFlowMgr::sizeAllocate, which is called by the containing
+ block.
+
+Implementation overview
+=======================
+
+Widget level
+------------
+
+The terms _generating block_ and _containing block_ have been raised
+to a higher level, the one of dw::core::Widget, and are here called
+_generating widget_ and _containing widget_. To represent the
+distinction, the type of dw::core::Content has been split into three
+parts:
+
+- If a widget is out of flow, the generating widget keeps a reference
+ with the type dw::core::Content::WIDGET_OOF_REF, while the
+ containing block refers to it as dw::core::Content::WIDGET_OOF_CONT.
+
+- For widgets within flow, dw::core::Content::WIDGET_IN_FLOW is used.
+
+Notice that in the first case, there are two pieces of content
+referring to the same widget.
+
+An application of this distinction is iterators. TODO: more. And still
+missing: DeepIterator may need the generating parent widget in some
+cases.
+
+
+Textblock level
+---------------
+Both dw::Textblock::notifySetAsTopLevel and
+dw::Textblock::notifySetParent set the member
+dw::Textblock::containingBlock appropriately, (according to rules
+which should be defined in this document).
+
+Handling widgets out of flow is partly the task of the new class
+dw::OutOfFlowMgr, which is stored by dw::Textblock::outOfFlowMgr, but
+only for containing blocks. Generating blocks should refer to
+_containingBlock->outOfFlowMgr_. (Perhaps dw::OutOfFlowMgr may become
+independent of dw::Textblock.)
+
+dw::Textblock::addWidget is extended, so that floats and absolutely
+positioned elements can be added. Notice that not this widget, but the
+containing block becomes the parent of the newly added child, if it is
+out of flow. dw::Textblock::addWidget decides this by calling
+dw::OutOfFlowMgr::isOutOfFlow. (See new content types above.)
+
+dw::core::Widget::parentRef has become a new representation. Before,
+it represented the line numer. Now (least signifant bit left):
+
+ +---+---+- - - - - - - - - - -+---+
+ | line number | 0 |
+ +---+---+- - - - - - - - - - -+---+
+
+ +---+---+- - - - - - - - -+---+---+
+ | number of left float | 0 | 1 |
+ +---+---+- - - - - - - - -+---+---+
+
+ +---+---+- - - - - - - - -+---+---+
+ | number of right float | 1 | 1 |
+ +---+---+- - - - - - - - -+---+---+
+
+The latter two must be changed, as soom as absolute positions are
+introduced. Details are hidden by static inline methods of
+dw::OutOfFlowMgr.
+
+
+*/ \ No newline at end of file
diff --git a/doc/dw-widget-sizes.doc b/doc/dw-widget-sizes.doc
index 419a4a73..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.&nbsp;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 18d7cd5a..c31c2706 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 d086721c..d8a59eec 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..015fd2ac
--- /dev/null
+++ b/dw/outofflowmgr.cc
@@ -0,0 +1,900 @@
+#include "outofflowmgr.hh"
+#include "textblock.hh"
+
+#include <limits.h>
+
+using namespace lout::object;
+using namespace lout::container::typed;
+using namespace lout::misc;
+using namespace dw::core;
+using namespace dw::core::style;
+
+namespace dw {
+
+int OutOfFlowMgr::Float::yForContainer (OutOfFlowMgr *oofm, int y)
+{
+ return y - generatingBlock->getAllocation()->y +
+ oofm->containingBlock->getAllocation()->y;
+}
+
+OutOfFlowMgr::OutOfFlowMgr (Textblock *containingBlock)
+{
+ //printf ("OutOfFlowMgr::OutOfFlowMgr\n");
+
+ this->containingBlock = containingBlock;
+ leftFloats = new Vector<Float> (1, true);
+ rightFloats = new Vector<Float> (1, true);
+ tbInfos = new HashTable <TypedPointer <Textblock>, TBInfo> (true, true);
+}
+
+OutOfFlowMgr::~OutOfFlowMgr ()
+{
+ //printf ("OutOfFlowMgr::~OutOfFlowMgr\n");
+
+ delete leftFloats;
+ delete rightFloats;
+ delete tbInfos;
+}
+
+void OutOfFlowMgr::sizeAllocate (Allocation *containingBlockAllocation)
+{
+ // 1. Floats have to be allocated
+ sizeAllocateFloats (leftFloats, false, containingBlockAllocation);
+ sizeAllocateFloats (rightFloats, true, containingBlockAllocation);
+
+ // 2. Textblocks have already been allocated, but we store some
+ // information for later use. TODO: Update this comment!
+ for (lout::container::typed::Iterator<TypedPointer <Textblock> > it =
+ tbInfos->iterator ();
+ it.hasNext (); ) {
+ TypedPointer <Textblock> *key = it.getNext ();
+ TBInfo *tbInfo = tbInfos->get (key);
+ Textblock *tb = key->getTypedValue();
+
+ int xCB = tb->getAllocation()->x - containingBlockAllocation->x;
+ int yCB = tb->getAllocation()->y - containingBlockAllocation->y;
+ int width = tb->getAllocation()->width;
+ int height = tb->getAllocation()->ascent + tb->getAllocation()->descent;
+
+ if ((!tbInfo->wasAllocated || tbInfo->xCB != xCB || tbInfo->yCB != yCB ||
+ tbInfo->width != width || tbInfo->height != height)) {
+ int oldPos, newPos;
+ // To calculate the minimum, both allocations, old and new,
+ // have to be tested.
+
+ // Old allocation:
+ bool c1 = isTextblockCoveredByFloats (containingBlockAllocation, tb,
+ tbInfo->xCB
+ + containingBlockAllocation->x,
+ tbInfo->yCB
+ + containingBlockAllocation->y,
+ tbInfo->width, tbInfo->height,
+ &oldPos);
+ // new allocation:
+ int c2 = isTextblockCoveredByFloats (containingBlockAllocation, tb,
+ tb->getAllocation()->x,
+ tb->getAllocation()->y,
+ width, height, &newPos);
+ if (c1 || c2)
+ tb->borderChanged (min (oldPos, newPos));
+ }
+
+ tbInfo->wasAllocated = true;
+ tbInfo->xCB = xCB;
+ tbInfo->yCB = yCB;
+ tbInfo->width = width;
+ tbInfo->height = height;
+ }
+}
+
+bool OutOfFlowMgr::isTextblockCoveredByFloats (Allocation
+ *containingBlockAllocation,
+ Textblock *tb, int tbx, int tby,
+ int tbWidth, int tbHeight,
+ int *floatPos)
+{
+ int leftPos, rightPos;
+ bool c1 = isTextblockCoveredByFloats (leftFloats, containingBlockAllocation,
+ tb, tbx, tby, tbWidth, tbHeight,
+ &leftPos);
+ bool c2 = isTextblockCoveredByFloats (rightFloats, containingBlockAllocation,
+ tb, tbx, tby, tbWidth, tbHeight,
+ &rightPos);
+ *floatPos = min (leftPos, rightPos);
+ return c1 || c2;
+}
+
+bool OutOfFlowMgr::isTextblockCoveredByFloats (Vector<Float> *list,
+ Allocation
+ *containingBlockAllocation,
+ Textblock *tb, int tbx, int tby,
+ int tbWidth, int tbHeight,
+ int *floatPos)
+{
+ *floatPos = INT_MAX;
+ bool covered = false;
+
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+
+ // This method is called within OOFM::sizeAllocate, which is
+ // called in Textblock::sizeAllocateImpl for the containing
+ // block, so that the only textblock which is not necessary
+ // allocates is the containing block.
+ assert (vloat->generatingBlock->wasAllocated() ||
+ vloat->generatingBlock == containingBlock);
+
+ // In this case we have to refer to the allocation passed to
+ // Textblock::sizeAllocateImpl.
+ Allocation *generatingBlockAllocation =
+ vloat->generatingBlock == containingBlock ?
+ containingBlockAllocation : vloat->generatingBlock->getAllocation();
+
+ // Idea: the distinction could be removed, and the code so made
+ // simpler, by moving this second part of OOFM::sizeAllocate
+ // into an idle function. This would also make the question
+ // obsolete whether it is allowed to call queueResize within
+ // sizeAllocate. Unfortunately, idle funtions currently only
+ // refer to layouts, not to widgets.
+
+ if (tb != vloat->generatingBlock && vloat->positioned) {
+ int flh = vloat->dirty ? 0 : vloat->size.ascent + vloat->size.descent;
+ int y1 = generatingBlockAllocation->y + vloat->yReal;
+ int y2 = y1 + flh;
+
+ // TODO: Also regard horizontal dimension (same for tellPositionOrNot).
+ if (y2 > tby && y1 < tby + tbWidth) {
+ covered = true;
+ *floatPos = min (*floatPos, y1 - tby);
+ }
+ }
+
+ // All floarts are searched, to find the minimum. TODO: Are
+ // floats sorted, so this can be shortene? (The first is the
+ // minimum?)
+ }
+
+ return covered;
+}
+
+void OutOfFlowMgr::sizeAllocateFloats (Vector<Float> *list, bool right,
+ Allocation *containingBlockAllocation)
+{
+ for (int i = 0; i < list->size(); i++) {
+ // TODO Missing: check newly calculated positions, collisions,
+ // and queue resize, when neccessary. TODO: See step 2?
+
+ Float *vloat = list->get(i);
+ assert (vloat->positioned);
+ ensureFloatSize (vloat);
+
+ Allocation childAllocation;
+ if (right)
+ childAllocation.x =
+ vloat->generatingBlock->getAllocation()->x
+ + min (vloat->generatingBlock->getAllocation()->width,
+ vloat->generatingBlock->getAvailWidth())
+ - vloat->size.width
+ - vloat->generatingBlock->getStyle()->boxRestWidth();
+ else
+ childAllocation.x = vloat->generatingBlock->getAllocation()->x
+ + vloat->generatingBlock->getStyle()->boxOffsetX();
+
+ childAllocation.y =
+ vloat->generatingBlock->getAllocation()->y + vloat->yReal;
+ childAllocation.width = vloat->size.width;
+ childAllocation.ascent = vloat->size.ascent;
+ childAllocation.descent = vloat->size.descent;
+
+ vloat->widget->sizeAllocate (&childAllocation);
+
+ //printf ("allocate %s #%d -> (%d, %d), %d x (%d + %d)\n",
+ // right ? "right" : "left", 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->positioned);
+
+ core::Rectangle childArea;
+ if (vloat->widget->intersects (area, &childArea))
+ vloat->widget->draw (view, &childArea);
+ }
+}
+
+void OutOfFlowMgr::queueResize(int ref)
+{
+ // TODO Is there something to do?
+}
+
+bool OutOfFlowMgr::isWidgetOutOfFlow (core::Widget *widget)
+{
+ // Will be extended for absolute positions.
+ return widget->getStyle()->vloat != FLOAT_NONE;
+}
+
+void OutOfFlowMgr::addWidget (Widget *widget, Textblock *generatingBlock)
+{
+ if (widget->getStyle()->vloat != FLOAT_NONE) {
+ Float *vloat = new Float ();
+ vloat->widget = widget;
+ vloat->generatingBlock = generatingBlock;
+ vloat->dirty = true;
+ vloat->yReq = vloat->positioned;
+
+ 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->positioned)
+ vloat->generatingBlock->borderChanged (vloat->yReal);
+ } else
+ // later: absolute positions
+ assertNotReached();
+}
+
+
+void OutOfFlowMgr::markExtremesChange (int ref)
+{
+ // Nothing to do here.
+}
+
+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;
+}
+
+
+/**
+ * This method is called by Textblock::rewrap at the beginning, to
+ * avoid wrong positions.
+ */
+void OutOfFlowMgr::tellNoPosition (Widget *widget)
+{
+ tellPositionOrNot (widget, 0, false);
+}
+
+
+void OutOfFlowMgr::tellPosition (Widget *widget, int y)
+{
+ assert (y >= 0);
+ tellPositionOrNot (widget, y, true);
+}
+
+void OutOfFlowMgr::tellPositionOrNot (Widget *widget, int y, bool positioned)
+{
+ Float *vloat = findFloatByWidget(widget);
+ if ((!positioned && !vloat->positioned) ||
+ (positioned && vloat->positioned && y == vloat->yReq))
+ // Nothing happened.
+ return;
+
+ if (positioned)
+ ensureFloatSize (vloat);
+ int oldY = vloat->yReal;
+ bool oldPositioned = vloat->positioned;
+
+ // "yReal" may change due to collisions (see below).
+ vloat->yReq = vloat->yReal = y;
+ vloat->positioned = positioned;
+
+ if (positioned) {
+ Vector<Float> *listSame = getFloatList (widget);
+ Vector<Float> *listOpp = getOppositeFloatList (widget);
+ bool collides;
+
+ // Collisions. TODO Can this be simplified?
+ do {
+ collides = false;
+
+ // Test collisions on the same side.
+ for (int i = 0; i < listSame->size(); i++) {
+ Float *v = listSame->get(i);
+ int yWidget;
+ if (v != vloat &&
+ getYWidget (vloat->generatingBlock, v, &yWidget)) {
+ ensureFloatSize (v);
+ if (v->positioned != -1 && vloat->yReal >= yWidget &&
+ vloat->yReal < yWidget + v->size.ascent + v->size.descent) {
+ collides = true;
+ vloat->yReal = yWidget + 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.
+ int yWidget;
+ if (getYWidget (vloat->generatingBlock, v, &yWidget)) {
+ ensureFloatSize (v);
+ if (v->positioned != -1 && vloat->yReal >= yWidget &&
+ vloat->yReal < yWidget + v->size.ascent + v->size.descent) {
+ // For the other side, horizontal dimensions have
+ // to be considered, too.
+ bool collidesH;
+ if (vloat->generatingBlock == v->generatingBlock)
+ collidesH = vloat->size.width + v->size.width +
+ vloat->generatingBlock->getStyle()->boxDiffWidth()
+ > vloat->generatingBlock->getAvailWidth();
+ else {
+ // Here (different generating blocks) it can be
+ // assumed that the allocations are defined
+ // (because getYWidget() would have returned
+ // false, otherwise).
+ Float *left, *right;
+ if (listSame == leftFloats) {
+ left = vloat;
+ right = v;
+ } else {
+ left = v;
+ right = vloat;
+ }
+
+ // right border of the left float (canvas coordinates)
+ int rightOfLeft =
+ left->generatingBlock->getAllocation()->x
+ + left->generatingBlock->getStyle()->boxOffsetX()
+ + left->size.width;
+ // left border of the right float (canvas coordinates)
+ int leftOfRight =
+ right->generatingBlock->getAllocation()->x
+ + min (right->generatingBlock->getAllocation()->width,
+ right->generatingBlock->getAvailWidth())
+ - right->generatingBlock->getStyle()->boxRestWidth()
+ - right->size.width;
+
+ collidesH = rightOfLeft > leftOfRight;
+ }
+
+ if (collidesH) {
+ collides = true;
+ vloat->yReal = yWidget + v->size.ascent + v->size.descent;
+ break;
+ }
+ }
+ }
+ }
+ } while (collides);
+
+ // It is assumed that there are no floats below this float
+ // within this generator. For this reason, no other floats have
+ // to be adjusted.
+ }
+
+ // Only this float has been changed (see above), so only this float
+ // has to be tested against all textblocks.
+ if (vloat->generatingBlock->wasAllocated () &&
+ // A change from "no position" to "no position" is uninteresting.
+ !(oldPositioned && !vloat->positioned)) {
+ int yChange;
+
+ if (!vloat->positioned) {
+ // position -> no position
+ assert (oldPositioned);
+ yChange = oldY;
+ } else if (!oldPositioned) {
+ // no position -> position
+ assert (vloat->positioned);
+ yChange = vloat->yReal;
+ } else {
+ // position -> position
+ assert (oldPositioned && vloat->positioned);
+ yChange = min (oldY, vloat->yReal);
+ }
+
+ // TODO This (and similar code) is not very efficient.
+ for (lout::container::typed::Iterator<TypedPointer <Textblock> > it =
+ tbInfos->iterator ();
+ it.hasNext (); ) {
+ TypedPointer <Textblock> *key = it.getNext ();
+ Textblock *textblock = key->getTypedValue();
+
+ if (textblock->wasAllocated () &&
+ // If not positioned, there will be soon a rewrap (see
+ // Textblock::rewrap), or, if positioned , the generating
+ // block takes care of the possible change (see
+ // Textblock::wrapWidgetOofRef), respectively; so, only
+ // the other textblocks must be told about this.
+ textblock != vloat->generatingBlock) {
+ int tby1 = textblock->getAllocation()->y;
+ int tby2 = tby1 + textblock->getAllocation()->ascent
+ + textblock->getAllocation()->descent;
+ int flh =
+ vloat->dirty ? 0 : vloat->size.ascent + vloat->size.descent;
+ bool covered = false;
+
+ if (oldPositioned) {
+ int y1 = vloat->generatingBlock->getAllocation()->y + oldY;
+ int y2 = y1 + flh;
+ covered = y2 > tby1 && y1 < tby2;
+ }
+
+ if (!covered && vloat->positioned) {
+ int y1 =
+ vloat->generatingBlock->getAllocation()->y + vloat->yReal;
+ int y2 = y1 + flh;
+ covered = y2 > tby1 && y1 < tby2;
+ }
+
+ if (covered) {
+ int yTextblock =
+ vloat->generatingBlock->getAllocation()->y + yChange
+ - textblock->getAllocation()->y;
+ textblock->borderChanged (yTextblock);
+ }
+ }
+ }
+ }
+}
+
+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 = getFloatsSize (leftFloats);
+ int oofHeightRight = getFloatsSize (rightFloats);
+ *oofHeight = max (oofHeightLeft, oofHeightRight);
+}
+
+int OutOfFlowMgr::getFloatsSize (Vector<Float> *list)
+{
+ int height = containingBlock->getStyle()->boxDiffHeight();
+
+ // Idea for a faster implementation: find the last float; this
+ // should be the relevant one.
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ if (vloat->positioned) {
+ ensureFloatSize (vloat);
+
+ // Notice that all positions are relative to the generating
+ // block, but we need them relative to the containing block.
+
+ // Position of generating block, relative to containing
+ // block. Greater or equal than 0, so dealing with 0 when it
+ // cannot yet be calculated is safe. (No distiction whether
+ // it is defined or not is necessary.)
+ int yGBinCB;
+
+ if (vloat->generatingBlock == containingBlock)
+ // Simplest case: the generator is the container.
+ yGBinCB = 0;
+ else {
+ if (containingBlock->wasAllocated()) {
+ if (vloat->generatingBlock->wasAllocated())
+ // Simple case: both containing block and generating
+ // block are defined.
+ yGBinCB = vloat->generatingBlock->getAllocation()->y
+ - containingBlock->getAllocation()->y;
+ else
+ // Generating block not yet allocation; the next
+ // allocation will, when necessary, trigger
+ // sizeRequest. (TODO: Is this really the case?)
+ yGBinCB = 0;
+ } else
+ // Nothing can be done now, but the next allocation
+ // will trigger sizeAllocate. (TODO: Is this really the
+ // case?)
+ yGBinCB = 0;
+ }
+
+ height =
+ max (height,
+ yGBinCB + vloat->yReal
+ + vloat->size.ascent + vloat->size.descent
+ + containingBlock->getStyle()->boxRestHeight());
+ }
+ }
+
+ return height;
+}
+
+void OutOfFlowMgr::getExtremes (int cbMinWidth, int cbMaxWidth,
+ int *oofMinWidth, int *oofMaxWidth)
+{
+ *oofMinWidth = *oofMaxWidth = 0;
+ accumExtremes (leftFloats, oofMinWidth, oofMaxWidth);
+ accumExtremes (rightFloats, oofMinWidth, oofMaxWidth);
+}
+
+void OutOfFlowMgr::accumExtremes (Vector<Float> *list, int *oofMinWidth,
+ int *oofMaxWidth)
+{
+ // Idea for a faster implementation: use incremental resizing?
+
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+
+ // Difference between generating block and to containing block,
+ // sum on both sides. Greater or equal than 0, so dealing with 0
+ // when it cannot yet be calculated is safe. (No distiction
+ // whether it is defined or not is necessary.)
+ int borderDiff;
+
+ if (vloat->generatingBlock == containingBlock)
+ // Simplest case: the generator is the container.
+ borderDiff = 0;
+ else {
+ if (containingBlock->wasAllocated()) {
+ if (vloat->generatingBlock->wasAllocated())
+ // Simple case: both containing block and generating
+ // block are defined.
+ borderDiff = containingBlock->getAllocation()->width -
+ vloat->generatingBlock->getAllocation()->width;
+ else
+ // Generating block not yet allocation; the next
+ // allocation will, when necessary, trigger
+ // getExtremes. (TODO: Is this really the case?)
+ borderDiff = 0;
+ } else
+ // Nothing can be done now, but the next allocation will
+ // trigger getExtremes. (TODO: Is this really the case?)
+ borderDiff = 0;
+ }
+
+ Extremes extr;
+ vloat->widget->getExtremes (&extr);
+
+ *oofMinWidth = max (*oofMinWidth, extr.minWidth + borderDiff);
+ *oofMaxWidth = max (*oofMaxWidth, extr.maxWidth + borderDiff);
+ }
+}
+
+void OutOfFlowMgr::registerCaller (Textblock *textblock)
+{
+ TypedPointer<Textblock> key (textblock);
+ TBInfo *tbInfo = tbInfos->get (&key);
+ if (tbInfo == NULL) {
+ tbInfo = new TBInfo;
+ tbInfo->wasAllocated = false;
+ tbInfos->put (new TypedPointer<Textblock> (textblock), tbInfo);
+ }
+}
+
+/**
+ * Get the left border for the vertical position of *y*, for a height
+ * of *h", based on floats; relative to the allocation of the calling
+ * textblock.
+ *
+ * The border includes marging/border/padding of the calling textblock
+ * but is 0 if there is no float, so a caller should also consider
+ * other borders.
+ */
+int OutOfFlowMgr::getLeftBorder (Textblock *textblock, int y, int h)
+{
+ return getBorder (textblock, leftFloats, false, 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 (Textblock *textblock, int y, int h)
+{
+ return getBorder (textblock, rightFloats, true, y, h);
+}
+
+int OutOfFlowMgr::getBorder (Textblock *textblock, Vector<Float> *list,
+ bool right, int y, int h)
+{
+ registerCaller (textblock);
+
+ 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);
+
+ int yWidget;
+ if (getYWidget (textblock, vloat, &yWidget)
+ && y + h > yWidget
+ && y < yWidget + vloat->size.ascent + vloat->size.descent) {
+ int borderDiff = getBorderDiff (textblock, vloat, right);
+ int borderIn = right ?
+ vloat->generatingBlock->getStyle()->boxRestWidth() :
+ vloat->generatingBlock->getStyle()->boxOffsetX();
+
+ //printf (" borderDiff = %d, borderIn = %d\n", borderDiff, borderIn);
+ border = max (border, vloat->size.width + borderIn + borderDiff);
+ }
+
+ // 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.
+
+ // 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.
+ }
+
+ //printf ("[%p] %s border (%d, %d) = %d\n",
+ // textblock, right ? "right" : "left", y, h, border);
+
+ return border;
+}
+
+bool OutOfFlowMgr::hasFloatLeft (Textblock *textblock, int y, int h)
+{
+ return hasFloat (textblock, leftFloats, false, y, h);
+}
+
+bool OutOfFlowMgr::hasFloatRight (Textblock *textblock, int y, int h)
+{
+ return hasFloat (textblock, rightFloats, true, y, h);
+}
+
+bool OutOfFlowMgr::hasFloat (Textblock *textblock, Vector<Float> *list,
+ bool right, int y, int h)
+{
+ // Compare to getBorder(). Actually much copy and paste.
+
+ registerCaller (textblock);
+
+ // To be a bit more efficient, one could use linear search.
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ ensureFloatSize (vloat);
+
+ int yWidget;
+ if (getYWidget (textblock, vloat, &yWidget)
+ && y + h > yWidget
+ && y < yWidget + vloat->size.ascent + vloat->size.descent) {
+ // As opposed to getBorder, finding the first float is
+ // sufficient.
+
+ //printf ("[%p] float on %s side (%d, %d): (%d, %d)\n",
+ // textblock, right ? "right" : "left", y, h,
+ // yWidget, vloat->size.ascent + vloat->size.descent);
+ return true;
+ }
+ }
+
+ //printf ("[%p] no float on %s side (%d, %d)\n",
+ // textblock, right ? "right" : "left", y, h);
+
+ return false;
+}
+
+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
+ (containingBlock->getAvailWidth()
+ * 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 (width > containingBlock->getAvailWidth())
+ width = containingBlock->getAvailWidth();
+ // Finally, consider extremes (as described above).
+ if (width < extremes.minWidth)
+ width = extremes.minWidth;
+ if (width > extremes.maxWidth)
+ width = extremes.maxWidth;
+
+ vloat->widget->setWidth (width);
+ vloat->widget->sizeRequest (&vloat->size);
+
+ //printf (" Float at %d: %d x (%d + %d)\n",
+ // vloat->y, vloat->width, vloat->ascent, vloat->descent);
+
+ vloat->dirty = false;
+ }
+}
+
+/**
+ * Returns true, when position can be determined; in this case, the
+ * position is tored in *yWidget.
+ */
+bool OutOfFlowMgr::getYWidget (Textblock *textblock, Float *vloat, int *yWidget)
+{
+ if (!vloat->positioned)
+ return false;
+ else if (textblock == vloat->generatingBlock) {
+ *yWidget = vloat->yReal;
+ return true;
+ } else {
+ if (textblock->wasAllocated() && vloat->generatingBlock->wasAllocated()) {
+ *yWidget = vloat->yReal + vloat->generatingBlock->getAllocation()->y
+ - textblock->getAllocation()->y;
+ return true;
+ } else
+ return false;
+ }
+}
+
+/**
+ * Return the between generator and calling textblock. (TODO Exact
+ * definition. See getBorder(), where it is used.)
+ *
+ * Assumes that the position can be determined (getYWidget() returns true).
+ */
+int OutOfFlowMgr::getBorderDiff (Textblock *textblock, Float *vloat, bool right)
+{
+ assert (vloat->positioned);
+
+ if (textblock == vloat->generatingBlock)
+ return 0;
+ else {
+ assert (textblock->wasAllocated() &&
+ vloat->generatingBlock->wasAllocated());
+
+ if (right)
+ return
+ textblock->getAllocation()->x + textblock->getAllocation()->width
+ - (vloat->generatingBlock->getAllocation()->x +
+ min (vloat->generatingBlock->getAllocation()->width,
+ vloat->generatingBlock->getAvailWidth()));
+ else
+ return vloat->generatingBlock->getAllocation()->x
+ - textblock->getAllocation()->x;
+ }
+}
+
+// TODO Latest change: Check also Textblock::borderChanged: looks OK,
+// but the comment ("... (i) with canvas coordinates ...") looks wrong
+// (and looks as having always been wrong).
+
+// Another issue: does it make sense to call Textblock::borderChanged
+// for generators, when the respective widgets have not been called
+// yet?
+
+} // namespace dw
diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh
new file mode 100644
index 00000000..faf2b3e0
--- /dev/null
+++ b/dw/outofflowmgr.hh
@@ -0,0 +1,145 @@
+#ifndef __DW_OUTOFFLOWMGR_HH__
+#define __DW_OUTOFFLOWMGR_HH__
+
+#include "core.hh"
+
+namespace dw {
+
+class Textblock;
+
+/**
+ * \brief Represents additional data for containing blocks.
+ */
+class OutOfFlowMgr
+{
+private:
+ Textblock *containingBlock;
+
+ class Float: public lout::object::Object
+ {
+ public:
+ core::Widget *widget;
+ Textblock *generatingBlock;
+ bool positioned;
+ int yReq, yReal; // relative to generator, not container
+ core::Requisition size;
+ bool dirty;
+
+ int yForContainer (OutOfFlowMgr *oofm, int y);
+
+ inline int yForContainer (OutOfFlowMgr *oofm) {
+ return yForContainer (oofm, yReal);
+ }
+ };
+
+ class TBInfo: public lout::object::Object
+ {
+ public:
+ bool wasAllocated;
+ int xCB, yCB; // relative to the containing block
+ int width, height;
+ };
+
+ //lout::container::typed::HashTable<lout::object::TypedPointer
+ // <dw::core::Widget>, Float> *floatsByWidget;
+ lout::container::typed::Vector<Float> *leftFloats, *rightFloats;
+ lout::container::typed::HashTable<lout::object::TypedPointer <Textblock>,
+ TBInfo> *tbInfos;
+
+ Float *findFloatByWidget (core::Widget *widget);
+ lout::container::typed::Vector<Float> *getFloatList (core::Widget *widget);
+ lout::container::typed::Vector<Float> *getOppositeFloatList (core::Widget
+ *widget);
+
+ void sizeAllocateFloats (lout::container::typed::Vector<Float> *list,
+ bool right,
+ core::Allocation *containingBlockAllocation);
+ bool isTextblockCoveredByFloats (core::Allocation *containingBlockAllocation,
+ Textblock *tb, int tbx, int tby,
+ int tbWidth, int tbHeight, int *floatPos);
+ bool isTextblockCoveredByFloats (lout::container::typed::Vector<Float> *list,
+ core::Allocation *containingBlockAllocation,
+ Textblock *tb, int tbx, int tby,
+ int tbWidth, int tbHeight, int *floatPos);
+
+ 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);
+ void tellPositionOrNot (core::Widget *widget, int y, bool positioned);
+ int getFloatsSize (lout::container::typed::Vector<Float> *list);
+ void accumExtremes (lout::container::typed::Vector<Float> *list,
+ int *oofMinWidth, int *oofMaxWidth);
+ void registerCaller (Textblock *textblock);
+ int getBorder (Textblock *textblock,
+ lout::container::typed::Vector<Float> *list, bool right,
+ int y, int h);
+ bool hasFloat (Textblock *textblock,
+ lout::container::typed::Vector<Float> *list, bool right,
+ int y, int h);
+
+ void ensureFloatSize (Float *vloat);
+ bool getYWidget (Textblock *textblock, Float *vloat, int *yWidget);
+ int getBorderDiff (Textblock *textblock, Float *vloat, bool right);
+
+
+ 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 (Textblock *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, Textblock *generatingBlock);
+
+ void tellNoPosition (core::Widget *widget);
+ void tellPosition (core::Widget *widget, int y);
+
+ void getSize (int cbWidth, int cbHeight, int *oofWidth, int *oofHeight);
+ void getExtremes (int cbMinWidth, int cbMaxWidth, int *oofMinWidth,
+ int *oofMaxWidth);
+
+ int getLeftBorder (Textblock *textblock, int y, int h);
+ int getRightBorder (Textblock *textblock, int y, int h);
+
+ bool hasFloatLeft (Textblock *textblock, int y, int h);
+ bool hasFloatRight (Textblock *textblock, 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); }
+
+ // 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 defc4259..5587f469 100644
--- a/dw/table.cc
+++ b/dw/table.cc
@@ -1104,7 +1104,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;
}
}
@@ -1126,8 +1126,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;
}
@@ -1141,7 +1141,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;
}
@@ -1153,8 +1153,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;
}
@@ -1168,7 +1168,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 9edea207..03f15f63 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);
@@ -181,8 +183,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 +201,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 +258,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 +285,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 {
@@ -312,6 +333,19 @@ void Textblock::getExtremesImpl (core::Extremes *extremes)
extremes->minWidth += diff;
extremes->maxWidth += diff;
+ if (outOfFlowMgr) {
+ int oofMinWidth, oofMaxWidth;
+ outOfFlowMgr->getExtremes (extremes->minWidth, extremes->maxWidth,
+ &oofMinWidth, &oofMaxWidth);
+
+ //printf ("[%p] extremes: %d / %d, corrected: %d / %d\n",
+ // this, extremes->minWidth, extremes->maxWidth,
+ // oofMinWidth, oofMaxWidth);
+
+ extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth);
+ extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth);
+ }
+
PRINTF ("[%p] GET_EXTREMES => %d / %d\n",
this, extremes->minWidth, extremes->maxWidth);
}
@@ -338,7 +372,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 +382,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 +454,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 +487,114 @@ void Textblock::resizeDrawImpl ()
void Textblock::markSizeChange (int ref)
{
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markSizeChange (ref);
+ } else {
+ PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n",
+ this, ref, wrapRefLines);
+
+ /* By the way: ref == -1 may have two different causes: (i) flush()
+ calls "queueResize (-1, true)", when no rewrapping is necessary;
+ and (ii) a word may have parentRef == -1 , when it is not yet
+ added to a line. In the latter case, nothing has to be done
+ now, but addLine(...) will do everything necessary. */
+ if (ref != -1) {
+ if (wrapRefLines == -1)
+ wrapRefLines = OutOfFlowMgr::getLineNoFromRef (ref);
+ else
+ wrapRefLines = misc::min (wrapRefLines,
+ OutOfFlowMgr::getLineNoFromRef (ref));
+ }
+
+ PRINTF (" ... => %d\n", wrapRefLine);
+
+ // It seems that sometimes (even without floats) the lines
+ // structure is changed, so that wrapRefLines may refers to a
+ // line which does not exist anymore. Should be examined
+ // again. Until then, setting wrapRefLines to the same value is
+ // a workaround.
+ markExtremesChange (ref);
+ }
+}
+
+void Textblock::markExtremesChange (int ref)
+{
PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", this, ref, wrapRefLines);
- /* By the way: ref == -1 may have two different causes: (i) flush()
- calls "queueResize (-1, true)", when no rewrapping is necessary;
- and (ii) a word may have parentRef == -1 , when it is not yet
- added to a line. In the latter case, nothing has to be done
- now, but addLine(...) will do everything necessary. */
- if (ref != -1) {
- if (wrapRefLines == -1)
- wrapRefLines = ref;
- else
- wrapRefLines = misc::min (wrapRefLines, ref);
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markExtremesChange (ref);
+ } else {
+ PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n",
+ this, ref, wrapRefParagraphs);
+
+ /* By the way: ref == -1 may have two different causes: (i) flush()
+ calls "queueResize (-1, true)", when no rewrapping is necessary;
+ and (ii) a word may have parentRef == -1 , when it is not yet
+ added to a line. In the latter case, nothing has to be done
+ now, but addLine(...) will do everything necessary. */
+ if (ref != -1) {
+ if (wrapRefParagraphs == -1)
+ wrapRefParagraphs = OutOfFlowMgr::getLineNoFromRef (ref);
+ else
+ wrapRefParagraphs =
+ misc::min (wrapRefParagraphs,
+ OutOfFlowMgr::getLineNoFromRef (ref));
+ }
+
+ PRINTF (" ... => %d\n", wrapRefParagraphs);
}
+}
- PRINTF (" ... => %d\n", wrapRefLine);
+void Textblock::notifySetAsTopLevel()
+{
+ PRINTF ("%p becomes toplevel\n", this);
+ containingBlock = this;
+ PRINTF ("-> %p is its own containing block\n", this);
+}
- // It seems that sometimes the lines structure is changed, so that
- // wrapRefLines may refers to a line which does not exist
- // anymore. Should be examined again. Until then, setting
- // wrapRefLines to the same value is a workaround.
- markExtremesChange (ref);
+bool Textblock::isContainingBlock (Widget *widget)
+{
+ return
+ // Of course, only textblocks are considered as containing
+ // blocks.
+ widget->instanceOf (Textblock::CLASS_ID) &&
+ // The second condition: that this block is "out of flow", in a
+ // wider sense.
+ (// The toplevel widget is "out of flow", since there is no
+ // parent, and so no context.
+ widget->getParent() == NULL ||
+ // A similar reasoning applies to a widget with another parent
+ // than a textblock (typical example: a table cell (this is
+ // also a text block) within a table widget).
+ !widget->getParent()->instanceOf (Textblock::CLASS_ID) ||
+ // Finally, "out of flow" in a narrower sense: floats and
+ // absolute positions.
+ OutOfFlowMgr::isWidgetOutOfFlow (widget));
}
-void Textblock::markExtremesChange (int ref)
+void Textblock::notifySetParent ()
{
- PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n",
- this, ref, wrapRefParagraphs);
-
- /* By the way: ref == -1 may have two different causes: (i) flush()
- calls "queueResize (-1, true)", when no rewrapping is necessary;
- and (ii) a word may have parentRef == -1 , when it is not yet
- added to a line. In the latter case, nothing has to be done
- now, but addLine(...) will do everything necessary. */
- if (ref != -1) {
- if (wrapRefParagraphs == -1)
- wrapRefParagraphs = ref;
- else
- wrapRefParagraphs = misc::min (wrapRefParagraphs, ref);
- }
+ PRINTF ("%p becomes a child of %p\n", this, getParent());
- PRINTF (" ... => %d\n", wrapRefParagraphs);
+ // Search for containing Box.
+ containingBlock = NULL;
+
+ for (Widget *widget = this; widget != NULL && containingBlock == NULL;
+ widget = widget->getParent())
+ if (isContainingBlock (widget)) {
+ containingBlock = (Textblock*)widget;
+
+ if (containingBlock == this) {
+ PRINTF ("-> %p is its own containing block\n", this);
+ } else {
+ PRINTF ("-> %p becomes containing block of %p\n",
+ containingBlock, this);
+ }
+ }
+
+ assert (containingBlock != NULL);
}
void Textblock::setWidth (int width)
@@ -504,7 +608,7 @@ void Textblock::setWidth (int width)
// words->size());
availWidth = width;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
redrawY = 0;
}
@@ -519,7 +623,7 @@ void Textblock::setAscent (int ascent)
// words->size());
availAscent = ascent;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
}
}
@@ -533,7 +637,7 @@ void Textblock::setDescent (int descent)
// words->size());
availDescent = descent;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
}
}
@@ -650,11 +754,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;
@@ -749,8 +853,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;
@@ -1133,7 +1238,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;
@@ -1144,10 +1249,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;
@@ -1291,7 +1396,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;
@@ -1337,6 +1442,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area)
drawLine (line, view, area);
}
+
+ if(outOfFlowMgr)
+ outOfFlowMgr->draw(view, area);
}
/**
@@ -1750,6 +1858,12 @@ void Textblock::addText0 (const char *text, size_t len, short flags,
word->content.type = core::Content::TEXT;
word->content.text = layout->textZone->strndup(text, len);
+ // The following debug message may be useful to identify the
+ // different textblocks.
+
+ //if (words->size() == 1)
+ // printf ("[%p] first word: '%s'\n", this, text);
+
processWord (words->size () - 1);
}
@@ -1758,28 +1872,42 @@ 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);
+
+ 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",
@@ -1934,7 +2062,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
@@ -1943,23 +2071,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 =
@@ -1968,7 +2096,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;
}
}
@@ -1976,6 +2105,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
}
/* Otherwise continue to examine parents. */
}
+
/* Return in any case. */
return;
}
@@ -2024,7 +2154,6 @@ void Textblock::addLinebreak (core::style::Style *style)
processWord (words->size () - 1);
}
-
/**
* \brief Search recursively through widget.
*
@@ -2033,6 +2162,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;
@@ -2043,6 +2176,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 ()) {
@@ -2054,7 +2195,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);
@@ -2135,7 +2276,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,
@@ -2187,4 +2328,112 @@ void Textblock::queueDrawRange (int index1, int index2)
}
}
+void Textblock::borderChanged (int y)
+{
+ PRINTF ("[%p] Border has changed: %d\n", this, y);
+
+ int lineIndex = findLineIndex (y);
+ // 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), true);
+ }
+}
+
+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)
+{
+ PRINTF ("[%p] HEIGHT_OF_POSSIBLY_MISSING_LINE (%d)\n", this, lineNo);
+
+ if (lineNo < lines->size()) {
+ // An existing line.
+ Line *line = lines->getRef (lineNo);
+ PRINTF (" exists; height = %d + %d = %d\n", line->boxAscent,
+ line->boxDescent, line->boxAscent + line->boxDescent);
+ 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;
+
+ PRINTF (" does not exist; accumulating words from %d to %d\n",
+ firstWord, words->size());
+
+ for (int i = firstWord; i < words->size(); i++) {
+ Word *word = words->getRef (i);
+ h = misc::max (h, word->size.ascent + word->size.descent);
+ //printf (" word %d: ", i);
+ //printWordShort (word);
+ //printf (" => %d + %d = %d\n", word->size.ascent, word->size.descent,
+ // word->size.ascent + word->size.descent);
+ }
+ PRINTF (" => %d\n", h);
+ return h;
+ } else
+ return 1;
+}
+
} // namespace dw
diff --git a/dw/textblock.hh b/dw/textblock.hh
index 90185cd2..04a7fa68 100644
--- a/dw/textblock.hh
+++ b/dw/textblock.hh
@@ -4,6 +4,7 @@
#include <limits.h>
#include "core.hh"
+#include "outofflowmgr.hh"
#include "../lout/misc.hh"
// These were used when improved line breaking and hyphenation were
@@ -11,6 +12,8 @@
#define PRINTF(fmt, ...)
#define PUTCHAR(ch)
+//#define DEBUG
+
namespace dw {
/**
@@ -18,10 +21,11 @@ namespace dw {
* of paragraphs.
*
* <div style="border: 2px solid #ffff00; margin-top: 0.5em;
- * margin-bottom: 0.5em; padding: 0.5em 1em;
- * background-color: #ffffe0"><b>Info:</b> The recent changes (line
- * breaking and hyphenation) have not yet been incorporated into this
- * documentation. See \ref dw-line-breaking.</div>
+ * margin-bottom: 0.5em; padding: 0.5em 1em; background-color:
+ * #ffffe0"><b>Info:</b> The recent changes (line breaking and
+ * hyphenation on one hand, floats on the other hand) have not yet
+ * been incorporated into this documentation. See \ref
+ * dw-line-breaking and \ref dw-out-of-flow.</div>
*
* <h3>Signals</h3>
*
@@ -236,6 +240,9 @@ private:
static const char *hyphenDrawChar;
+ Textblock *containingBlock;
+ OutOfFlowMgr *outOfFlowMgr;
+
protected:
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. */
@@ -367,13 +374,14 @@ protected:
class TextblockIterator: public core::Iterator
{
private:
+ bool oofm;
int index;
public:
TextblockIterator (Textblock *textblock, core::Content::Type mask,
bool atEnd);
TextblockIterator (Textblock *textblock, core::Content::Type mask,
- int index);
+ bool oofm, int index);
lout::object::Object *clone();
int compareTo(lout::object::Comparable *other);
@@ -383,6 +391,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;
@@ -432,7 +441,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;
@@ -490,25 +502,17 @@ protected:
core::Requisition *size, bool isStart, bool isEnd);
/**
- * \brief Returns the x offset (the indentation plus any offset needed for
- * centering or right justification) for the line.
- *
- * The offset returned is relative to the page *content* (i.e. without
- * border etc.).
+ * Of nested text blocks, only the most inner one must regard the
+ * borders of floats.
*/
- inline int lineXOffsetContents (Line *line)
+ inline bool mustBorderBeRegarded (Line *line)
{
- return innerPadding + line->leftOffset +
- (line == lines->getFirstRef() ? line1OffsetEff : 0);
+ return getTextblockForLine (line) == NULL;
}
- /**
- * \brief Like lineXOffset, but relative to the allocation (i.e.
- * including border etc.).
- */
- inline int lineXOffsetWidget (Line *line)
+ inline bool mustBorderBeRegarded (int lineNo)
{
- return lineXOffsetContents (line) + getStyle()->boxOffsetX ();
+ return getTextblockForLine (lineNo) == NULL;
}
inline int lineYOffsetWidgetAllocation (Line *line,
@@ -559,6 +563,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);
@@ -566,6 +576,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 ();
@@ -585,6 +601,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);
@@ -604,6 +622,7 @@ protected:
core::style::Style *style,
int numBreaks, int *breakPos,
core::Requisition *wordSize);
+ static bool isContainingBlock (Widget *widget);
public:
static int CLASS_ID;
@@ -638,6 +657,11 @@ public:
void changeLinkColor (int link, int newColor);
void changeWordStyle (int from, int to, core::style::Style *style,
bool includeFirstSpace, bool includeLastSpace);
+
+ void borderChanged (int y);
+ inline int getAvailWidth () { return availWidth; }
+ inline int getAvailAscent () { return availAscent; }
+ inline int getAvailDescent () { return availDescent; }
};
} // namespace dw
diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc
index 22f43fb6..daf82c50 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(object::Comparable *other)
{
- return index - ((TextblockIterator*)other)->index;
+ TextblockIterator *otherTI = (TextblockIterator*)other;
+
+ if (oofm && !otherTI->oofm)
+ return +1;
+ else if (!oofm && otherTI->oofm)
+ return -1;
+ else
+ return index - otherTI->index;
}
bool Textblock::TextblockIterator::next ()
@@ -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,132 +147,188 @@ bool Textblock::TextblockIterator::prev ()
if (content.type == core::Content::START)
return false;
+ short type;
+
do {
index--;
- if (index < 0) {
- content.type = core::Content::START;
- return false;
+
+ if (oofm) {
+ // Iterating over OOFM.
+ if (index >= 0)
+ // Still widgets left.
+ type = core::Content::WIDGET_OOF_CONT;
+ else {
+ // Beginning of OOFM list reached. Continue with words.
+ oofm = false;
+ index = textblock->words->size() - 1;
+ if (index < 0) {
+ // There are no words. (Actually, this case should not
+ // happen: When there are OOF widgets, ther must be OOF
+ // references, or widgets in flow, which contain
+ // references.
+ content.type = core::Content::END;
+ return false;
+ }
+ type = textblock->words->getRef(index)->content.type;
+ }
+ } else {
+ // Iterating over words list.
+ if (index < 0) {
+ // Beginning of words list reached.
+ content.type = core::Content::START;
+ return false;
+ }
+ type = textblock->words->getRef(index)->content.type;
}
- } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
+ } while ((type & getMask()) == 0);
+
+ if (oofm) {
+ content.type = core::Content::WIDGET_OOF_CONT;
+ content.type = false;
+ content.widget = textblock->outOfFlowMgr->getWidget (index);
+ } else
+ content = textblock->words->getRef(index)->content;
- content = textblock->words->getRef(index)->content;
return true;
}
void Textblock::TextblockIterator::highlight (int start, int end,
core::HighlightLayer layer)
{
- Textblock *textblock = (Textblock*)getWidget();
- int index1 = index, index2 = index;
-
- int oldStartIndex = textblock->hlStart[layer].index;
- int oldStartChar = textblock->hlStart[layer].nChar;
- int oldEndIndex = textblock->hlEnd[layer].index;
- int oldEndChar = textblock->hlEnd[layer].nChar;
-
- if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
- /* nothing is highlighted */
- textblock->hlStart[layer].index = index;
- textblock->hlEnd[layer].index = index;
- }
-
- if (textblock->hlStart[layer].index >= index) {
- index2 = textblock->hlStart[layer].index;
- textblock->hlStart[layer].index = index;
- textblock->hlStart[layer].nChar = start;
- }
-
- if (textblock->hlEnd[layer].index <= index) {
- index2 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index;
- textblock->hlEnd[layer].nChar = end;
+ if (!oofm) {
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ int oldStartIndex = textblock->hlStart[layer].index;
+ int oldStartChar = textblock->hlStart[layer].nChar;
+ int oldEndIndex = textblock->hlEnd[layer].index;
+ int oldEndChar = textblock->hlEnd[layer].nChar;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
+ /* nothing is highlighted */
+ textblock->hlStart[layer].index = index;
+ textblock->hlEnd[layer].index = index;
+ }
+
+ if (textblock->hlStart[layer].index >= index) {
+ index2 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index;
+ textblock->hlStart[layer].nChar = start;
+ }
+
+ if (textblock->hlEnd[layer].index <= index) {
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index;
+ textblock->hlEnd[layer].nChar = end;
+ }
+
+ if (oldStartIndex != textblock->hlStart[layer].index ||
+ oldStartChar != textblock->hlStart[layer].nChar ||
+ oldEndIndex != textblock->hlEnd[layer].index ||
+ oldEndChar != textblock->hlEnd[layer].nChar)
+ textblock->queueDrawRange (index1, index2);
}
- if (oldStartIndex != textblock->hlStart[layer].index ||
- oldStartChar != textblock->hlStart[layer].nChar ||
- oldEndIndex != textblock->hlEnd[layer].index ||
- oldEndChar != textblock->hlEnd[layer].nChar)
- textblock->queueDrawRange (index1, index2);
+ // TODO What about OOF widgets?
}
void Textblock::TextblockIterator::unhighlight (int direction,
core::HighlightLayer layer)
{
- Textblock *textblock = (Textblock*)getWidget();
- int index1 = index, index2 = index;
-
- if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
- return;
-
- int oldStartIndex = textblock->hlStart[layer].index;
- int oldStartChar = textblock->hlStart[layer].nChar;
- int oldEndIndex = textblock->hlEnd[layer].index;
- int oldEndChar = textblock->hlEnd[layer].nChar;
-
- if (direction == 0) {
- index1 = textblock->hlStart[layer].index;
- index2 = textblock->hlEnd[layer].index;
- textblock->hlStart[layer].index = 1;
- textblock->hlEnd[layer].index = 0;
- } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
- index1 = textblock->hlStart[layer].index;
- textblock->hlStart[layer].index = index + 1;
- textblock->hlStart[layer].nChar = 0;
- } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
- index1 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index - 1;
- textblock->hlEnd[layer].nChar = INT_MAX;
+ if (!oofm) {
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
+ return;
+
+ int oldStartIndex = textblock->hlStart[layer].index;
+ int oldStartChar = textblock->hlStart[layer].nChar;
+ int oldEndIndex = textblock->hlEnd[layer].index;
+ int oldEndChar = textblock->hlEnd[layer].nChar;
+
+ if (direction == 0) {
+ index1 = textblock->hlStart[layer].index;
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlStart[layer].index = 1;
+ textblock->hlEnd[layer].index = 0;
+ } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
+ index1 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index + 1;
+ textblock->hlStart[layer].nChar = 0;
+ } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
+ index1 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index - 1;
+ textblock->hlEnd[layer].nChar = INT_MAX;
+ }
+
+ if (oldStartIndex != textblock->hlStart[layer].index ||
+ oldStartChar != textblock->hlStart[layer].nChar ||
+ oldEndIndex != textblock->hlEnd[layer].index ||
+ oldEndChar != textblock->hlEnd[layer].nChar)
+ textblock->queueDrawRange (index1, index2);
}
- if (oldStartIndex != textblock->hlStart[layer].index ||
- oldStartChar != textblock->hlStart[layer].nChar ||
- oldEndIndex != textblock->hlEnd[layer].index ||
- oldEndChar != textblock->hlEnd[layer].nChar)
- textblock->queueDrawRange (index1, index2);
+ // TODO What about OOF widgets?
}
void Textblock::TextblockIterator::getAllocation (int start, int end,
core::Allocation *allocation)
{
Textblock *textblock = (Textblock*)getWidget();
- int lineIndex = textblock->findLineOfWord (index);
- Line *line = textblock->lines->getRef (lineIndex);
- Word *word = textblock->words->getRef (index);
-
- allocation->x =
- textblock->allocation.x + textblock->lineXOffsetWidget (line);
- for (int i = line->firstWord; i < index; i++) {
- Word *w = textblock->words->getRef(i);
- allocation->x += w->size.width + w->effSpace;
- }
- if (start > 0 && word->content.type == core::Content::TEXT) {
- allocation->x += textblock->textWidth (word->content.text, 0, start,
- word->style,
- word->flags & Word::WORD_START,
- (word->flags & Word::WORD_END)
- && word->content.text[start] == 0);
- }
- allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
- word->size.ascent;
-
- allocation->width = word->size.width;
- if (word->content.type == core::Content::TEXT) {
- int wordEnd = strlen(word->content.text);
-
- if (start > 0 || end < wordEnd) {
- end = misc::min(end, wordEnd); /* end could be INT_MAX */
- allocation->width =
- textblock->textWidth (word->content.text, start, end - start,
- word->style,
- (word->flags & Word::WORD_START)
- && start == 0,
- (word->flags & Word::WORD_END)
- && word->content.text[end] == 0);
+ if (oofm) {
+ // TODO Consider start and end?
+ *allocation =
+ *(textblock->outOfFlowMgr->getWidget(index)->getAllocation());
+ } else {
+ int lineIndex = textblock->findLineOfWord (index);
+ Line *line = textblock->lines->getRef (lineIndex);
+ Word *word = textblock->words->getRef (index);
+
+ allocation->x =
+ textblock->allocation.x + line->offsetCompleteWidget;
+
+ for (int i = line->firstWord; i < index; i++) {
+ Word *w = textblock->words->getRef(i);
+ allocation->x += w->size.width + w->effSpace;
+ }
+ if (start > 0 && word->content.type == core::Content::TEXT) {
+ allocation->x += textblock->textWidth (word->content.text, 0, start,
+ word->style,
+ word->flags & Word::WORD_START,
+ (word->flags & Word::WORD_END)
+ && word->content.text[start]
+ == 0);
+ }
+ allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
+ word->size.ascent;
+
+ allocation->width = word->size.width;
+ if (word->content.type == core::Content::TEXT) {
+ int wordEnd = strlen(word->content.text);
+
+ if (start > 0 || end < wordEnd) {
+ end = misc::min(end, wordEnd); /* end could be INT_MAX */
+ allocation->width =
+ textblock->textWidth (word->content.text, start, end - start,
+ word->style,
+ (word->flags & Word::WORD_START)
+ && start == 0,
+ (word->flags & Word::WORD_END)
+ && word->content.text[end] == 0);
+ }
}
+ allocation->ascent = word->size.ascent;
+ allocation->descent = word->size.descent;
}
- allocation->ascent = word->size.ascent;
- allocation->descent = word->size.descent;
+}
+
+void Textblock::TextblockIterator::print ()
+{
+ Iterator::print ();
+ printf (", oofm = %s, index = %d", oofm ? "true" : "false", index);
+
}
} // namespace dw
diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc
index 2375c988..9cbe6814 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)
@@ -317,6 +303,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;
@@ -376,6 +368,26 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
for(int i = line->firstWord; i <= line->lastWord; i++)
accumulateWordForLine (lineIndex, i);
+ // Especially empty lines (possible when there are floats) have
+ // zero height, which may cause endless loops. For this reasons,
+ // the height should be positive.
+ line->boxAscent = misc::max (line->boxAscent, 1);
+
+ // Calculate offsetCompleteWidget, which includes also floats.
+ int leftBorder;
+ if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (line)) {
+ int y = line->top + getStyle()->boxOffsetY();
+ int h = line->boxAscent + line->boxDescent;
+ leftBorder = containingBlock->outOfFlowMgr->getLeftBorder (this, y, h);
+ } else
+ leftBorder = 0;
+
+ line->offsetCompleteWidget =
+ misc::max (leftBorder,
+ getStyle()->boxOffsetX() + innerPadding
+ + (lineIndex == 0 ? line1OffsetEff : 0))
+ + line->leftOffset;
+
PRINTF (" line[%d].top = %d\n", lines->size () - 1, line->top);
PRINTF (" line[%d].boxAscent = %d\n", lines->size () - 1, line->boxAscent);
PRINTF (" line[%d].boxDescent = %d\n",
@@ -384,9 +396,10 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
line->contentAscent);
PRINTF (" line[%d].contentDescent = %d\n",
lines->size () - 1, line->contentDescent);
-
PRINTF (" line[%d].maxLineWidth = %d\n",
lines->size () - 1, line->maxLineWidth);
+ PRINTF (" line[%d].offsetCompleteWidget = %d\n",
+ lines->size () - 1, line->offsetCompleteWidget);
mustQueueResize = true;
@@ -454,14 +467,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);
@@ -470,16 +481,56 @@ 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 ||
+ !mustBorderBeRegarded (lines->size ())) {
+ thereWillBeMoreSpace = false;
+ PRINTF (" thereWillBeMoreSpace = false (no OOFM or ...)\n");
+ } else {
+ int y =
+ topOfPossiblyMissingLine (lines->size ());
+ int h = heightOfPossiblyMissingLine (lines->size ());
+
+ // A previous version checked only the borders, not directly,
+ // whether there are floats. The distinction is rather
+ // disputable. (More on this later.)
+
+ thereWillBeMoreSpace =
+ containingBlock->outOfFlowMgr->hasFloatLeft (this, y, h) ||
+ containingBlock->outOfFlowMgr->hasFloatRight (this, y, h);
+
+ PRINTF (" thereWillBeMoreSpace = %s (y = %d, h = %d)\n",
+ thereWillBeMoreSpace ? "true" : "false", y, h);
+ }
+
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;
@@ -495,13 +546,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");
@@ -518,118 +577,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
@@ -646,7 +655,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);
@@ -654,6 +664,118 @@ 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,
+ 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.
*/
@@ -830,13 +952,20 @@ int Textblock::hyphenateWord (int wordIndex)
}
}
- accumulateWordData (wordIndex + i);
-
//printf ("[%p] %d: hyphenated word part: ", this, wordIndex + i);
//printWordWithFlags (w);
//printf ("\n");
}
+ // AccumulateWordData() will calculate the width, which depends
+ // on the borders (possibly limited by floats), which depends on
+ // the widgeds so far. For this reason, it is important to first
+ // make all words consistent before calling
+ // accumulateWordData(); therefore the second loop.
+
+ for (int i = 0; i < numBreaks + 1; i++)
+ accumulateWordData (wordIndex + i);
+
PRINTF (" finished\n");
//delete origword->content.text; TODO: Via textZone?
@@ -855,8 +984,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);
@@ -871,7 +1004,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 =
@@ -895,7 +1028,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);
@@ -961,18 +1095,37 @@ 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, rightBorder;
+ if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (lineIndex)) {
+ int y = topOfPossiblyMissingLine (lineIndex);
+ int h = heightOfPossiblyMissingLine (lineIndex);
+ leftBorder = containingBlock->outOfFlowMgr->getLeftBorder (this, y, h);
+ rightBorder = containingBlock->outOfFlowMgr->getRightBorder (this, y, h);
+ } else
+ leftBorder = rightBorder = 0;
+
+ leftBorder = misc::max (leftBorder, getStyle()->boxOffsetX());
+ rightBorder = misc::max (rightBorder, getStyle()->boxRestWidth());
+
+ availWidth -= (leftBorder + rightBorder);
+
+ PRINTF ("[%p] CALC_AVAIL_WIDTH (%d of %d) => %d - %d - (%d + %d)"
+ " = %d\n", this, lineIndex, lines->size(), this->availWidth,
+ innerPadding, leftBorder, rightBorder,
+ availWidth);
return availWidth;
}
@@ -989,7 +1142,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 {
@@ -1080,8 +1233,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 9c6fe380..12430f01 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);
}
@@ -168,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);
@@ -179,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);
}
/**
@@ -509,7 +519,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,
@@ -553,6 +565,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 6a834acb..db7b65b9 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 \
@@ -51,6 +52,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-table.html b/test/floats-table.html
new file mode 100644
index 00000000..77d77563
--- /dev/null
+++ b/test/floats-table.html
@@ -0,0 +1,24 @@
+<p>Demonstrating how to include floats into witdth extremes.
+
+<table>
+ <tr>
+ <td style="border: 1px dashed black">
+ <div style="float:left; border: 1px dashed black">Somelongwordwhichmustnotbebrokensotakingmuchspaceinatablecolumn</div>
+ Some short text.
+ <td style="border: 1px dashed black">
+ Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+ accusantium doloremque laudantium, totam rem aperiam eaque ipsa,
+ quae ab illo inventore veritatis et quasi architecto beatae
+ vitae dicta sunt, explicabo. nemo enim ipsam voluptatem, quia
+ voluptas sit, aspernatur aut odit aut fugit, sed quia
+ consequuntur magni dolores eos, qui ratione voluptatem sequi
+ nesciunt, neque porro quisquam est, qui dolorem ipsum, quia
+ dolor sit, amet, consectetur, adipisci velit, sed quia non
+ numquam eius modi tempora incidunt, ut labore et dolore magnam
+ aliquam quaerat voluptatem. ut enim ad minima veniam, quis
+ nostrum exercitationem ullam corporis suscipit laboriosam, nisi
+ ut aliquid ex ea commodi consequatur? quis autem vel eum iure
+ reprehenderit, qui in ea voluptate velit esse, quam nihil
+ molestiae consequatur, vel illum, qui dolorem eum fugiat, quo
+ voluptas nulla pariatur?
+</table>
diff --git a/test/floats-worm.html b/test/floats-worm.html
new file mode 100644
index 00000000..9e050933
--- /dev/null
+++ b/test/floats-worm.html
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+ <head>
+ <title>Floats and iterators: search for "worm" and pay attention
+ to the order.</title>
+ <style type="text/css">
+ .float1, .float2 {
+ padding: 0.5em;
+ border: 1px dashed #404040;
+ width: 30%;
+ }
+ .float1 {
+ float: left;
+ margin: 0.5em 0.5em 0.5em 0;
+ }
+ .float2 {
+ float: right;
+ margin: 0.5em 0 0.5em 0.5em;
+ }
+
+ </style>
+ </head>
+ <body>
+ <div class="float1">1: apple apple worm apple apple</div>
+ <p>2: apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple worm apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple</p>
+ <div class="float2">3: apple apple worm apple apple</div>
+ <p>4: apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple worm apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple</p>
+ <div class="float1">5: apple apple worm apple apple</div>
+ <body>
+</html>
+
+
+
diff --git a/test/floats1.html b/test/floats1.html
new file mode 100644
index 00000000..eae30c4a
--- /dev/null
+++ b/test/floats1.html
@@ -0,0 +1,46 @@
+<div>First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First
+paragraph. <div style="float: left; border: 1px dashed black">Left
+float. Left float. Left float. Left float. Left float. Left
+float. Left float. Left float. Left float. Left float. Left
+float. Left float. Left float. Left float. Left float. Left
+float. Left float. Left float. Left float. Left float.</div> First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph.</div>
+<div><div>Second paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph.<div style="float:
+right; border: 1px dashed black">Right float. Right float. Right
+float. Right float. Right float. Right float.</div> Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph.</div></div>
diff --git a/test/floats2.html b/test/floats2.html
new file mode 100644
index 00000000..b7978706
--- /dev/null
+++ b/test/floats2.html
@@ -0,0 +1,17 @@
+Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae
+ab illo inventore veritatis et quasi architecto beatae vitae dicta
+sunt, explicabo.
+<div style="float:left; border: 1px dashed black">Some text in a
+float.<br /><img src="http://www.dillo.org/dw/html/not-so-simple-container.png" /></div>
+nemo enim ipsam voluptatem, quia voluptas sit,
+aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,
+qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit,
+sed quia non numquam eius modi tempora incidunt, ut labore et dolore
+magnam aliquam quaerat voluptatem. ut enim ad minima veniam, quis
+nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut
+aliquid ex ea commodi consequatur? quis autem vel eum iure
+reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae
+consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla
+pariatur?
diff --git a/test/floats3.html b/test/floats3.html
new file mode 100644
index 00000000..4d57e453
--- /dev/null
+++ b/test/floats3.html
@@ -0,0 +1,16 @@
+<p>Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae
+ab illo inventore veritatis et quasi architecto beatae vitae dicta
+sunt, explicabo.</p>
+<div style="float:left; border: 1px dashed black">Some text in a
+float.<br /><img src="http://www.dillo.org/dw/html/not-so-simple-container.png" /></div>
+<p>nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit
+aut fugit, sed quia consequuntur magni dolores eos, qui ratione
+voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem
+ipsum, quia dolor sit, amet, consectetur, adipisci velit, sed quia non
+numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam
+quaerat voluptatem. ut enim ad minima veniam, quis nostrum
+exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex
+ea commodi consequatur? quis autem vel eum iure reprehenderit, qui in
+ea voluptate velit esse, quam nihil molestiae consequatur, vel illum,
+qui dolorem eum fugiat, quo voluptas nulla pariatur?</p>
diff --git a/test/floats4.html b/test/floats4.html
new file mode 100644
index 00000000..965ed68d
--- /dev/null
+++ b/test/floats4.html
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+ <head>
+ <title>Floats 4</title>
+ <style type="text/css">
+ .border {
+ background-color: #e0e0ff;
+ padding: 1cm;
+ }
+ .float1, .float2 {
+ margin: 1cm;
+ padding: 1cm;
+ border: 1px dashed red;
+ background-color: #f0fff0;
+ float: right;
+ }
+ .float1 {
+ float: left;
+ }
+ .float2 {
+ float: right;
+ }
+
+ .wide {
+ margin: 1cm 0;
+ padding: 1cm;
+ border: 1px dashed red;
+ background-color: #ffffd0;
+ width: 40cm;
+ }
+ </style>
+ </head>
+ <body class="border">
+ <div class="float2">Some text in a float.</div>
+
+ <p>Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+ accusantium doloremque laudantium, totam rem aperiam eaque ipsa,
+ quae ab illo inventore veritatis et quasi architecto beatae
+ vitae dicta sunt, explicabo. nemo enim ipsam voluptatem, quia
+ voluptas sit, aspernatur aut odit aut fugit, sed quia
+ consequuntur magni dolores eos, qui ratione voluptatem sequi
+ nesciunt, neque porro quisquam est, qui dolorem ipsum, quia
+ dolor sit, amet, consectetur, adipisci velit, sed quia non
+ numquam eius modi tempora incidunt, ut labore et dolore magnam
+ aliquam quaerat voluptatem. ut enim ad minima veniam, quis
+ nostrum exercitationem ullam corporis suscipit laboriosam, nisi
+ ut aliquid ex ea commodi consequatur? quis autem vel eum iure
+ reprehenderit, qui in ea voluptate velit esse, quam nihil
+ molestiae consequatur, vel illum, qui dolorem eum fugiat, quo
+ voluptas nulla pariatur?</p>
+
+ <table class="wide"><tbody><tr><td>Ἐν ἀρχῇ ἦν ὁ Λόγος, καὶ ὁ Λόγος
+ ἦν πρὸς τὸν Θεόν, καὶ Θεὸς ἦν ὁ Λόγος. Οὗτος ἦν ἐν ἀρχῇ πρὸς τὸν
+ Θεόν. πάντα δι' αὐτοῦ ἐγένετο, καὶ χωρὶς αὐτοῦ ἐγένετο οὐδὲ ἕν ὃ
+ γέγονεν. ἐν αὐτῷ ζωὴ ἦν, καὶ ἡ ζωὴ ἦν τὸ φῶς τῶν ἀνθρώπων. καὶ
+ τὸ φῶς ἐν τῇ σκοτίᾳ φαίνει, καὶ ἡ σκοτία αὐτὸ οὐ
+ κατέλαβεν.</tbody></tr></td></table>
+ <body>
+</html>
+
+
+
diff --git a/test/floats5.html b/test/floats5.html
new file mode 100644
index 00000000..c8c6564a
--- /dev/null
+++ b/test/floats5.html
@@ -0,0 +1,16 @@
+Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae
+ab illo inventore veritatis et quasi architecto beatae vitae dicta
+sunt, explicabo.
+<div style="float:left; border: 1px dashed black"><img src="http://www.dillo.org/Icons/ProgramIcon16.png" /></div>
+nemo enim ipsam voluptatem, quia voluptas sit,
+aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,
+qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit,
+sed quia non numquam eius modi tempora incidunt, ut labore et dolore
+magnam aliquam quaerat voluptatem. ut enim ad minima veniam, quis
+nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut
+aliquid ex ea commodi consequatur? quis autem vel eum iure
+reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae
+consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla
+pariatur?