aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore1
-rw-r--r--ChangeLog2
-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.doc226
-rw-r--r--dw/Makefile.am2
-rw-r--r--dw/findtext.cc4
-rw-r--r--dw/iterator.cc146
-rw-r--r--dw/iterator.hh15
-rw-r--r--dw/layout.cc65
-rw-r--r--dw/layout.hh11
-rw-r--r--dw/outofflowmgr.cc1720
-rw-r--r--dw/outofflowmgr.hh380
-rw-r--r--dw/ruler.cc13
-rw-r--r--dw/ruler.hh4
-rw-r--r--dw/style.cc32
-rw-r--r--dw/style.hh27
-rw-r--r--dw/table.cc18
-rw-r--r--dw/table.hh2
-rw-r--r--dw/tablecell.cc2
-rw-r--r--dw/textblock.cc674
-rw-r--r--dw/textblock.hh126
-rw-r--r--dw/textblock_iterator.cc331
-rw-r--r--dw/textblock_linebreaking.cc425
-rw-r--r--dw/types.cc85
-rw-r--r--dw/types.hh29
-rw-r--r--dw/ui.cc2
-rw-r--r--dw/widget.cc164
-rw-r--r--dw/widget.hh78
-rw-r--r--lout/container.cc18
-rw-r--r--lout/container.hh37
-rw-r--r--lout/identity.cc23
-rw-r--r--lout/identity.hh4
-rw-r--r--lout/misc.hh5
-rw-r--r--lout/object.cc35
-rw-r--r--lout/object.hh40
-rw-r--r--src/cssparser.cc24
-rw-r--r--src/html.cc3
-rw-r--r--src/styleengine.cc21
-rw-r--r--test/Makefile.am9
-rw-r--r--test/containers.cc34
-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
52 files changed, 4972 insertions, 503 deletions
diff --git a/.hgignore b/.hgignore
index 9ab76864..759077b4 100644
--- a/.hgignore
+++ b/.hgignore
@@ -27,6 +27,7 @@
^test/dw-border-test$
^test/dw-example$
^test/dw-find-test$
+^test/dw-float-test$
^test/dw-image-background$
^test/dw-images-scaled$
^test/dw-images-scaled2$
diff --git a/ChangeLog b/ChangeLog
index 21d8887a..98287844 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,4 @@
-=============================================================================
+b=============================================================================
Dillo project
=============================================================================
diff --git a/doc/Makefile.am b/doc/Makefile.am
index dad95629..591ede4c 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -26,6 +26,7 @@ EXTRA_DIST = \
dw-textblock-collapsing-spaces-1-2.png \
dw-textblock-collapsing-spaces-2-1.png \
dw-textblock-collapsing-spaces-2-2.png \
+ dw-floats-01.png \
Cache.txt \
Cookies.txt \
Dillo.txt \
diff --git a/doc/dw-floats-01.png b/doc/dw-floats-01.png
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..86f46561 100644
--- a/doc/dw-widget-sizes.doc
+++ b/doc/dw-widget-sizes.doc
@@ -1,22 +1,20 @@
/** \page dw-widget-sizes Sizes of Dillo Widgets
-<h2>Allocation</h2>
+Allocation
+==========
Each widget has an \em allocation at a given time, this includes
-<ul>
-<li> the position (\em x, \em y) relative to the upper left corner of the
- canvas, and
-<li> the size (\em width, \em ascent, \em descent).
-</ul>
+- the position (\em x, \em y) relative to the upper left corner of the
+ canvas, and
+- the size (\em width, \em ascent, \em descent).
The \em canvas is the whole area available for the widgets, in most
-cases, only a part is seen in a viewport. The allocation of the toplevel widget is exactly the allocation of the canvas, i.e.
+cases, only a part is seen in a viewport. The allocation of the
+toplevel widget is exactly the allocation of the canvas, i.e.
-<ul>
-<li> the position of the toplevel widget is always (0, 0), and
-<li> the canvas size is defined by the size of the toplevel widget.
-</ul>
+- the position of the toplevel widget is always (0, 0), and
+- the canvas size is defined by the size of the toplevel widget.
The size of a widget is not simply defined by the width and the
height, instead, widgets may have a base line, and so are vertically
@@ -31,13 +29,11 @@ defined by the limits of the C++ type \em int.
In the example in the image, the widget has the following allocation:
-<ul>
-<li>\em x = 50
-<li>\em y = 50
-<li>\em width = 150
-<li>\em ascent = 150
-<li>\em descent = 100
-</ul>
+- \em x = 50
+- \em y = 50
+- \em width = 150
+- \em ascent = 150
+- \em descent = 100
The current allocation of a widget is hold in
dw::core::Widget::allocation. It can be set from outside by
@@ -54,7 +50,9 @@ appropriate child allocations. dw::core::Widget::allocation should not
be changed here, this is already done in
dw::core::Widget::sizeAllocate.
-<h2>Requisitions</h2>
+
+Requisitions
+============
A widget may prefer a given size for the allocation. This size, the
\em requisition, should be returned by the method
@@ -62,14 +60,12 @@ dw::core::Widget::sizeRequestImpl. In the simplest case, this is
independent of the context, e.g. for an
image. dw::Image::sizeRequestImpl returns the following size:
-<ul>
-<li> If no buffer has yet been assigned (see dw::Image for more details),
- the size necessary for the alternative text is returned. If no
- alternative text has been set, zero is returned.
+- If no buffer has yet been assigned (see dw::Image for more details),
+ the size necessary for the alternative text is returned. If no
+ alternative text has been set, zero is returned.
-<li> If a buffer has been assigned (by dw::Image::setBuffer), the root
- size is returned (i.e. the original size of the image to display).
-</ul>
+- If a buffer has been assigned (by dw::Image::setBuffer), the root
+ size is returned (i.e. the original size of the image to display).
This is a bit simplified, dw::Image::sizeRequestImpl should also deal
with margins, borders and paddings, see dw::core::style.
@@ -87,7 +83,9 @@ widget), may, but also may not consider the requisition. Instead, a
widget must deal with any allocation. (For example, dw::Image scales
the image buffer when allocated at another size.)
-<h2>Size Hints</h2>
+
+Size Hints
+==========
Some widgets do not have an inherent size, but depend on the context,
e.g. the viewport size. These widgets should adhere to <i>size hints</i>,
@@ -95,11 +93,9 @@ i.e. implement the methods dw::core::Widget::setWidth,
dw::core::Widget::setAscent and dw::core::Widget::setDescent. The values
passed to the callees are
-<ul>
-<li> the viewport size (ascent is the heigt here, while descent is 0) for
- the toplevel widget, and
-<li> determined by the parent for its child widgets.
-</ul>
+- the viewport size (ascent is the heigt here, while descent is 0) for
+ the toplevel widget, and
+- determined by the parent for its child widgets.
Generally, the values should define the available space for the
widget.
@@ -109,42 +105,41 @@ dw::core::Widget::queueResize, when apropriate.
\todo There should be a definition of "available space".
-<h2>Width Extremes</h2>
+
+Width Extremes
+==============
dw::Table uses width extremes for fast calculation of column
widths. The structure dw::core::Extremes represents the minimal and
maximal width of a widget, as defined by:
-<ul>
-<li> the minimal width is the smallest width, at which a widget can still
- display contents, and
-<li> the maximal width is the largest width, above which increasing the width
- does not make any sense.
-</ul>
+- the minimal width is the smallest width, at which a widget can still
+ display contents, and
+- the maximal width is the largest width, above which increasing the
+ width- does not make any sense.
Especially the latter is vaguely defined, here are some examples:
-<ul>
-<li> For those widgets, which do not depend on size hints, the minimal and
- the maximal width is the inherent width (the one returned by
- dw::core::Widget::sizeRequest).
+- For those widgets, which do not depend on size hints, the minimal
+ and the maximal width is the inherent width (the one returned by
+ dw::core::Widget::sizeRequest).
-<li> For a textblock, the minimal width is the width of the widest
- (unbreakable) word, the maximal width is the width of the total
- paragraph (stretching a paragraph further would only waste space).
- Actually, the implementation of dw::Textblock::getExtremesImpl is
- a bit more complex.
+- For a textblock, the minimal width is the width of the widest
+ (unbreakable) word, the maximal width is the width of the total
+ paragraph (stretching a paragraph further would only waste space).
+ Actually, the implementation of dw::Textblock::getExtremesImpl is a
+ bit more complex.
-<li> dw::Table is an example, where the width extremes are calculated
- from the width extremes of the children.
-</ul>
+- dw::Table is an example, where the width extremes are calculated
+ from the width extremes of the children.
Handling width extremes is similar to handling requisitions, a widget
must implement dw::core::Widget::getExtremesImpl, but a caller will
use dw::core::Widget::getExtremes.
-<h2>Resizing</h2>
+Resizing
+========
When the widget changes its size (requisition), it should call
dw::core::Widget::queueResize. The next call of
@@ -160,27 +155,122 @@ done before. In this case, a widget must exactly know the reasons, why
a call of dw::core::Widget::sizeRequestImpl is necessary. To make use
of this, a widget must implement the following:
-<ol>
-<li> There is a member dw::core::Widget::parentRef, which is
- totally under control of the parent widget (and so sometimes not
- used at all). It is necessary to define how parentRef is used
- by a specific parent widget, and it has to be set to the correct
- value whenever necessary.
-
-<li> The widget must implement dw::core::Widget::markSizeChange and
+1. There is a member dw::core::Widget::parentRef, which is totally
+ under control of the parent widget (and so sometimes not used at
+ all). It is necessary to define how parentRef is used by a specific
+ parent widget, and it has to be set to the correct value whenever
+ necessary.
+2. The widget must implement dw::core::Widget::markSizeChange and
dw::core::Widget::markExtremesChange, these methods are called in
two cases:
-
- <ol>
- <li> directly after dw::core::Widget::queueResize, with the argument
- ref was passed to dw::core::Widget::queueResize, and
- <li> if a child widget has called dw::core::Widget::queueResize,
- with the value of the parent_ref member of this child.
- </ol>
-</ol>
+ 1. directly after dw::core::Widget::queueResize, with the
+ argument ref was passed to dw::core::Widget::queueResize,
+ and
+ 2. if a child widget has called dw::core::Widget::queueResize,
+ with the value of the parent_ref member of this child.
This way, a widget can exactly keep track on size changes, and so
implement resizing in a faster way. A good example on how to use this
is dw::Textblock.
+
+Rules for Methods Related to Resizing
+=====================================
+
+Which method can be called, when the call of another method is not
+finished? These rules are important in two circumstances:
+
+1. To know which method can be called, and, especially, which methods
+ *must not* be called, within the implementation of
+ *sizeRequestImpl* (called by *sizeRequest*), *markSizeChange*, and
+ *markExtremesChange* (the latter two are called by *queueResize*).
+2. On the other hand, to make sure that the calls, which are allowed,
+ are handled correctly, especially in implementations of
+ *sizeRequestImpl*, *markSizeChange*, *markExtremesChange*
+
+Generally, the rules defined below are, in case of doubt, rather
+strict; when changing the rules, loosening is simpler than to tighten
+them, since this will make it neccessary to review old code for calls
+previously allowed but now forbidden.
+
+Short recap:
+
+- *QueueResize* directly calls *markSizeChange* and
+ *markExtremesChanges*, and queues an idle function for the actual
+ resizing (dw::core::Layout::resizeIdle). (The idle function is
+ called some time after *queueResize* is finished.)
+- The resize idle function first calls *sizeRequest*, then
+ *sizeAllocate*, for the toplevel widget.
+
+In the following table, the rules are defined in detail. "Within call
+of ..." includes all methods called from the original method: the
+first row (*queueResize*) defines also the rules for
+*markExtremesChanges* and *markExtremesChanges*, and in the second row
+(*sizeAllocate*), even *sizeRequest* has to be considered.
+
+<table>
+ <tr>
+ <th>Within call of ... ↓
+ <th>... is call allowed of ... ? →
+ <th>queueResize
+ <th>sizeAllocate
+ <th>sizeRequest
+ <th>getExtremes
+ <tr>
+ <th colspan=2>queueResize
+ <td>No
+ <td>No<sup>1</sup>
+ <td>No<sup>1</sup>
+ <td>No<sup>1</sup>
+ <tr>
+ <th colspan=2>sizeAllocate
+ <td>Yes
+ <td>Only for children<sup>2</sup>
+ <td>Yes(?)
+ <td>Yes(?)
+ <tr>
+ <th colspan=2>sizeRequest
+ <td>Yes<sup>3</sup>
+ <td>No
+ <td>Limited<sup>4</sup>
+ <td>Limited<sup>4</sup>
+ <tr>
+ <th colspan=2>getExtremes
+ <td>Yes<sup>3</sup>
+ <td>No
+ <td>Limited<sup>4</sup>
+ <td>Limited<sup>4</sup>
+ <tr>
+ <td colspan=6><sup>1</sup>) Otherwise, since these other methods
+may be call *queueResize*, the limitation that *queueResize* must not
+call *queueResize* can be violated.
+
+<sup>2</sup>) Could perhaps be loosened as for *sizeRequest* and
+*getExtremes*, but there is probably no need.
+
+<sup>3</sup>) Therefore the distinction between *RESIZE_QUEUED* and
+*NEEDS_RESIZE*, and *EXTREMES_QUEUED* and *EXTREMES_CHANGED*,
+respectively.
+
+<sup>4</sup>) Calls only for children are safe. In other cases, you
+take a large responsibility to prevent endless recursions by
+(typically indirectly) calling *sizeRequest* / *getExtremes* for
+direct ancestors.
+</table>
+
+Furthermore, *sizeAllocate* can only be called within a call of
+dw::core::Layout::resizeIdleId, so (if you do not touch dw::core) do
+not call it outside of *sizeAllocateImpl*. The other methods can be
+called outsize; e.&nbsp;g. *sizeRequest* is called in
+dw::Textblock::addWidget.
+
+To avoid painful debugging, there are some tests for the cases that
+one method call is strictly forbidden while another method is called.
+
+This could be done furthermore:
+
+- The tests could be refined.
+- Is it possible to define exacter rules, along with a proof that no
+ problems (like endless recursion) can occur?
+
*/
diff --git a/dw/Makefile.am b/dw/Makefile.am
index d0d56d2a..47ec5275 100644
--- a/dw/Makefile.am
+++ b/dw/Makefile.am
@@ -67,6 +67,8 @@ libDw_widgets_a_SOURCES = \
image.hh \
listitem.cc \
listitem.hh \
+ outofflowmgr.cc \
+ outofflowmgr.hh \
ruler.cc \
ruler.hh \
table.cc \
diff --git a/dw/findtext.cc b/dw/findtext.cc
index 4390e3ee..e86116f1 100644
--- a/dw/findtext.cc
+++ b/dw/findtext.cc
@@ -94,7 +94,7 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens,
if (iterator)
delete iterator;
- iterator = new CharIterator (widget);
+ iterator = new CharIterator (widget, true);
if (backwards) {
/* Go to end */
@@ -126,7 +126,7 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens,
} else {
// Nothing found anymore, reset the state for the next trial.
delete iterator;
- iterator = new CharIterator (widget);
+ iterator = new CharIterator (widget, true);
if (backwards) {
/* Go to end */
while (iterator->next ()) ;
diff --git a/dw/iterator.cc b/dw/iterator.cc
index 18d7cd5a..18d62a49 100644
--- a/dw/iterator.cc
+++ b/dw/iterator.cc
@@ -55,6 +55,24 @@ bool Iterator::equals (Object *other)
(getWidget() == otherIt->getWidget() && compareTo(otherIt) == 0);
}
+void Iterator::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append ("{ widget = ");
+ //widget->intoStringBuffer (sb);
+ sb->appendPointer (widget);
+ sb->append (" (");
+ sb->append (widget->getClassName());
+ sb->append (")>");
+
+ sb->append (", mask = ");
+ Content::maskIntoStringBuffer (mask, sb);
+
+ sb->append (", content = ");
+ Content::intoStringBuffer (&content, sb);
+
+ sb->append (" }");
+}
+
/**
* \brief Delete the iterator.
*
@@ -186,6 +204,14 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end,
}
}
+
+void Iterator::print ()
+{
+ misc::StringBuffer sb;
+ intoStringBuffer (&sb);
+ printf ("%s", sb.getChars ());
+}
+
// -------------------
// EmptyIterator
// -------------------
@@ -343,7 +369,7 @@ Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask,
//DEBUG_MSG (1, "%*smoving down (%swards) from %s\n",
// indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
- assert (it->getContent()->type == Content::WIDGET);
+ assert (it->getContent()->type & Content::ANY_WIDGET);
it2 = it->getContent()->widget->iterator (mask, fromEnd);
if (it2 == NULL) {
@@ -356,7 +382,7 @@ Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask,
//DEBUG_MSG (1, "%*sexamining %s\n",
// indent, "", a_Dw_iterator_text (it2));
- if (it2->getContent()->type == Content::WIDGET) {
+ if (it2->getContent()->type & Content::ANY_WIDGET) {
// Another widget. Search in it downwards.
it3 = searchDownward (it2, mask, fromEnd);
if (it3 != NULL) {
@@ -390,11 +416,11 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
//DEBUG_MSG (1, "%*smoving %swards from %s\n",
// indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
- assert (it->getContent()->type == Content::WIDGET);
+ assert (it->getContent()->type & Content::ANY_WIDGET);
it2 = it->cloneIterator ();
while (fromEnd ? it2->prev () : it2->next ()) {
- if (it2->getContent()->type == Content::WIDGET) {
+ if (it2->getContent()->type & Content::ANY_WIDGET) {
// Search downwards in this widget.
it3 = searchDownward (it2, mask, fromEnd);
if (it3 != NULL) {
@@ -416,13 +442,14 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
/* Nothing found, go upwards in the tree (if possible). */
it2->unref ();
- if (it->getWidget()->getParent ()) {
- it2 = it->getWidget()->getParent()->iterator (mask, false);
+ Widget *respParent = getRespectiveParent (it->getWidget(), it->getMask());
+ if (respParent) {
+ it2 = respParent->iterator (mask, false);
while (true) {
if (!it2->next ())
misc::assertNotReached ();
- if (it2->getContent()->type == Content::WIDGET &&
+ if (it2->getContent()->type & Content::ANY_WIDGET &&
it2->getContent()->widget == it->getWidget ()) {
it3 = searchSideward (it2, mask, fromEnd);
it2->unref ();
@@ -440,6 +467,27 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
return NULL;
}
+Widget *DeepIterator::getRespectiveParent (Widget *widget, Content::Type mask)
+{
+ // Return, depending on which is requested indirectly (follow
+ // references or containments) the parent (container) or the
+ // generator. At this point, the type of the parent/generator is
+ // not known (since the parent/generator is not known), so we have
+ // to examine the mask. This is the reason why only one of
+ // WIDGET_OOF_CONT and WIDGET_OOF_REF is allowed.
+
+ return (mask & Content::WIDGET_OOF_REF) ?
+ widget->getGenerator() : widget->getParent();
+}
+
+int DeepIterator::getRespectiveLevel (Widget *widget, Content::Type mask)
+{
+ // Similar to getRespectiveParent.
+
+ return (mask & Content::WIDGET_OOF_REF) ?
+ widget->getGeneratorLevel() : widget->getLevel();
+}
+
/**
* \brief Create a new deep iterator from an existing dw::core::Iterator.
*
@@ -456,6 +504,19 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
*/
DeepIterator::DeepIterator (Iterator *it)
{
+ //printf ("Starting creating DeepIterator %p ...\n", this);
+ //printf ("Initial iterator: ");
+ //it->print ();
+ //printf ("\n");
+
+ // Widgets out of flow are either followed widtin containers, or
+ // generators. Both (and also nothing at all) is not allowed. See
+ // also comment in getRespectiveParent.
+ int oofMask =
+ it->getMask() & (Content::WIDGET_OOF_CONT | Content::WIDGET_OOF_REF);
+ assert (oofMask == Content::WIDGET_OOF_CONT ||
+ oofMask == Content::WIDGET_OOF_REF);
+
//DEBUG_MSG (1, "a_Dw_ext_iterator_new: %s\n", a_Dw_iterator_text (it));
// Clone input iterator, so the iterator passed as parameter
@@ -467,7 +528,7 @@ DeepIterator::DeepIterator (Iterator *it)
// If it points to a widget, find a near non-widget content,
// since an DeepIterator should never return widgets.
- if (it->getContent()->type == Content::WIDGET) {
+ if (it->getContent()->type & Content::ANY_WIDGET) {
Iterator *it2;
// The second argument of searchDownward is actually a matter of
@@ -494,31 +555,50 @@ DeepIterator::DeepIterator (Iterator *it)
// \todo There may be a faster way instead of iterating through the
// parent widgets.
+ //printf ("Starting with: ");
+ //it->print ();
+ //printf ("\n");
+
// Construct the iterators.
- int thisLevel = it->getWidget()->getLevel (), level;
+ int thisLevel = getRespectiveLevel (it->getWidget()), level;
Widget *w;
- for (w = it->getWidget (), level = thisLevel; w->getParent() != NULL;
- w = w->getParent (), level--) {
- Iterator *it = w->getParent()->iterator (mask, false);
+ for (w = it->getWidget (), level = thisLevel;
+ getRespectiveParent (w) != NULL;
+ w = getRespectiveParent (w), level--) {
+ Iterator *it = getRespectiveParent(w)->iterator (mask, false);
+
+ //printf (" parent: %s %p\n", w->getClassName (), w);
+
stack.put (it, level - 1);
while (true) {
+ //printf (" ");
+ //it->print ();
+ //printf ("\n");
+
bool hasNext = it->next();
assert (hasNext);
- if (it->getContent()->type == Content::WIDGET &&
+ if (it->getContent()->type & Content::ANY_WIDGET &&
it->getContent()->widget == w)
break;
}
+
+ //printf (" %d: ", level - 1);
+ //it->print ();
+ //printf ("\n");
}
stack.put (it, thisLevel);
content = *(it->getContent());
}
+
+ //printf ("... done creating DeepIterator %p.\n", this);
}
DeepIterator::~DeepIterator ()
{
+ //printf ("Deleting DeepIterator %p ...\n", this);
}
object::Object *DeepIterator::clone ()
@@ -539,9 +619,29 @@ int DeepIterator::compareTo (object::Comparable *other)
{
DeepIterator *otherDeepIterator = (DeepIterator*)other;
+ //printf ("Compare: %s\n", stack.toString ());
+ //printf (" to: %s\n", otherDeepIterator->stack.toString ());
+
// Search the highest level, where the widgets are the same.
int level = 0;
+ // The Comparable interface does not define "uncomparable". Deep
+ // iterators are only comparable if they belong to the same widget
+ // tree, so have the same widget at the bottom at the
+ // stack. If this is not the case, we abort.
+
+ assert (stack.size() > 0);
+ assert (otherDeepIterator->stack.size() > 0);
+
+ //printf ("Equal? The %s %p (of %p) and the %s %p (of %p)?\n",
+ // stack.get(0)->getWidget()->getClassName(),
+ // stack.get(0)->getWidget(), this,
+ // otherDeepIterator->stack.get(0)->getWidget()->getClassName(),
+ // otherDeepIterator->stack.get(0)->getWidget(), otherDeepIterator);
+
+ assert (stack.get(0)->getWidget()
+ == otherDeepIterator->stack.get(level)->getWidget());
+
while (stack.get(level)->getWidget ()
== otherDeepIterator->stack.get(level)->getWidget ()) {
if (level == stack.size() - 1 ||
@@ -550,10 +650,14 @@ int DeepIterator::compareTo (object::Comparable *other)
level++;
}
+ //printf (" => level = %d (temorally)\n", level);
+
while (stack.get(level)->getWidget ()
!= otherDeepIterator->stack.get(level)->getWidget ())
level--;
+ //printf (" => level = %d (finally)\n", level);
+
return stack.get(level)->compareTo (otherDeepIterator->stack.get(level));
}
@@ -577,7 +681,7 @@ bool DeepIterator::next ()
Iterator *it = stack.getTop ();
if (it->next ()) {
- if (it->getContent()->type == Content::WIDGET) {
+ if (it->getContent()->type & Content::ANY_WIDGET) {
// Widget: new iterator on stack, to search in this widget.
stack.push (it->getContent()->widget->iterator (mask, false));
return next ();
@@ -610,7 +714,7 @@ bool DeepIterator::prev ()
Iterator *it = stack.getTop ();
if (it->prev ()) {
- if (it->getContent()->type == Content::WIDGET) {
+ if (it->getContent()->type & Content::ANY_WIDGET) {
// Widget: new iterator on stack, to search in this widget.
stack.push (it->getContent()->widget->iterator (mask, true));
return prev ();
@@ -642,9 +746,17 @@ CharIterator::CharIterator ()
it = NULL;
}
-CharIterator::CharIterator (Widget *widget)
+/**
+ * \brief ...
+ *
+ * If followReferences is true, only the reference are followed, when
+ * the container and generator for a widget is different. If false,
+ * only the container is followed.
+ */
+CharIterator::CharIterator (Widget *widget, bool followReferences)
{
- Iterator *i = widget->iterator (Content::SELECTION_CONTENT, false);
+ Iterator *i =
+ widget->iterator (Content::maskForSelection (followReferences), false);
it = new DeepIterator (i);
i->unref ();
ch = START;
diff --git a/dw/iterator.hh b/dw/iterator.hh
index d086721c..abf31d0b 100644
--- a/dw/iterator.hh
+++ b/dw/iterator.hh
@@ -31,6 +31,7 @@ private:
public:
bool equals (Object *other);
+ void intoStringBuffer(lout::misc::StringBuffer *sb);
inline Widget *getWidget () { return widget; }
inline Content *getContent () { return &content; }
@@ -85,6 +86,8 @@ public:
static void scrollTo (Iterator *it1, Iterator *it2, int start, int end,
HPosition hpos, VPosition vpos);
+
+ virtual void print ();
};
@@ -168,6 +171,16 @@ private:
inline DeepIterator () { }
+ static Widget *getRespectiveParent (Widget *widget, Content::Type mask);
+ inline Widget *getRespectiveParent (Widget *widget) {
+ return getRespectiveParent (widget, mask);
+ }
+
+ static int getRespectiveLevel (Widget *widget, Content::Type mask);
+ inline int getRespectiveLevel (Widget *widget) {
+ return getRespectiveLevel (widget, mask);
+ }
+
public:
DeepIterator(Iterator *it);
~DeepIterator();
@@ -230,7 +243,7 @@ private:
CharIterator ();
public:
- CharIterator (Widget *widget);
+ CharIterator (Widget *widget, bool followReferences);
~CharIterator ();
lout::object::Object *clone();
diff --git a/dw/layout.cc b/dw/layout.cc
index f7bd7597..ff121250 100644
--- a/dw/layout.cc
+++ b/dw/layout.cc
@@ -245,6 +245,8 @@ Layout::Layout (Platform *platform)
topLevel = NULL;
widgetAtPoint = NULL;
+ queueResizeList = new typed::Vector<Widget> (4, false);
+
DBG_OBJ_CREATE ("dw::core::Layout");
bgColor = NULL;
@@ -277,6 +279,9 @@ Layout::Layout (Platform *platform)
selectionState.setLayout(this);
+ queueResizeCounter = sizeAllocateCounter = sizeRequestCounter =
+ getExtremesCounter = 0;
+
layoutImgRenderer = NULL;
}
@@ -303,6 +308,7 @@ Layout::~Layout ()
topLevel = NULL;
delete w;
}
+ delete queueResizeList;
delete platform;
delete view;
delete anchorsTable;
@@ -318,12 +324,18 @@ void Layout::addWidget (Widget *widget)
topLevel = widget;
widget->layout = this;
+ queueResizeList->clear ();
+ widget->notifySetAsTopLevel();
findtextState.setWidget (widget);
canvasHeightGreater = false;
setSizeHints ();
- queueResize ();
+
+ // Do not directly call Layout::queueResize(), but
+ // Widget::queueResize(), so that all flags are set properly,
+ // queueResizeList is filled, etc.
+ topLevel->queueResize (-1, false);
}
void Layout::removeWidget ()
@@ -332,6 +344,7 @@ void Layout::removeWidget ()
* \bug Some more attributes must be reset here.
*/
topLevel = NULL;
+ queueResizeList->clear ();
widgetAtPoint = NULL;
canvasWidth = canvasAscent = canvasDescent = 0;
scrollX = scrollY = 0;
@@ -774,11 +787,38 @@ void Layout::setBgImage (style::StyleImage *bgImage,
void Layout::resizeIdle ()
{
+ enterQueueResize ();
+
//static int calls = 0;
- //MSG(" Layout::resizeIdle calls = %d\n", ++calls);
+ //printf ("Layout::resizeIdle calls = %d\n", ++calls);
assert (resizeIdleId != -1);
+ for (typed::Iterator <Widget> it = queueResizeList->iterator();
+ it.hasNext (); ) {
+ Widget *widget = it.getNext ();
+
+ //printf (" the %stop-level %s %p was queued (extremes changed: %s)\n",
+ // widget->parent ? "non-" : "", widget->getClassName(), widget,
+ // widget->extremesQueued () ? "yes" : "no");
+
+ if (widget->resizeQueued ()) {
+ widget->setFlags (Widget::NEEDS_RESIZE);
+ widget->unsetFlags (Widget::RESIZE_QUEUED);
+ }
+
+ if (widget->allocateQueued ()) {
+ widget->setFlags (Widget::NEEDS_ALLOCATE);
+ widget->unsetFlags (Widget::ALLOCATE_QUEUED);
+ }
+
+ if (widget->extremesQueued ()) {
+ widget->setFlags (Widget::EXTREMES_CHANGED);
+ widget->unsetFlags (Widget::EXTREMES_QUEUED);
+ }
+ }
+ queueResizeList->clear ();
+
// Reset already here, since in this function, queueResize() may be
// called again.
resizeIdleId = -1;
@@ -786,8 +826,14 @@ void Layout::resizeIdle ()
if (topLevel) {
Requisition requisition;
Allocation allocation;
-
+
topLevel->sizeRequest (&requisition);
+
+ // This method is triggered by Widget::queueResize, which will,
+ // in any case, set NEEDS_ALLOCATE (indirectly, as
+ // ALLOCATE_QUEUED). This assertion helps to find
+ // inconsistences.
+ assert (topLevel->needsAllocate ());
allocation.x = allocation.y = 0;
allocation.width = requisition.width;
@@ -827,6 +873,10 @@ void Layout::resizeIdle ()
}
updateAnchor ();
+
+ //printf ("Layout::resizeIdle end\n");
+
+ leaveQueueResize ();
}
void Layout::setSizeHints ()
@@ -1162,8 +1212,13 @@ void Layout::viewportSizeChanged (View *view, int width, int height)
/* if size changes, redraw this view.
* TODO: this is a resize call (redraw/resize code needs a review). */
- if (viewportWidth != width || viewportHeight != height)
- queueResize();
+ if (viewportWidth != width || viewportHeight != height) {
+ if (topLevel)
+ // similar to addWidget()
+ topLevel->queueResize (-1, false);
+ else
+ queueResize ();
+ }
viewportWidth = width;
viewportHeight = height;
diff --git a/dw/layout.hh b/dw/layout.hh
index 47554b42..64274714 100644
--- a/dw/layout.hh
+++ b/dw/layout.hh
@@ -153,6 +153,7 @@ private:
Platform *platform;
View *view;
Widget *topLevel, *widgetAtPoint;
+ lout::container::typed::Vector<Widget> *queueResizeList;
/* The state, which must be projected into the view. */
style::Color *bgColor;
@@ -236,6 +237,16 @@ private:
void queueResize ();
void removeWidget ();
+ /* For tests regarding the respective Layout and (mostly) Widget
+ methods. Accessed by respective methods (enter..., leave...,
+ ...Entered) defined here and in Widget. */
+
+ int resizeIdleCounter, queueResizeCounter, sizeAllocateCounter,
+ sizeRequestCounter, getExtremesCounter;
+
+ void enterQueueResize () { resizeIdleCounter++; }
+ void leaveQueueResize () { resizeIdleCounter--; }
+
public:
Layout (Platform *platform);
~Layout ();
diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc
new file mode 100644
index 00000000..49babdab
--- /dev/null
+++ b/dw/outofflowmgr.cc
@@ -0,0 +1,1720 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2013 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "outofflowmgr.hh"
+#include "textblock.hh"
+
+using namespace lout::object;
+using namespace lout::container::typed;
+using namespace lout::misc;
+using namespace dw::core;
+using namespace dw::core::style;
+
+namespace dw {
+
+OutOfFlowMgr::Float::Float (OutOfFlowMgr *oofm, Widget *widget,
+ Textblock *generatingBlock, int externalIndex)
+{
+ this->oofm = oofm;
+ this->widget = widget;
+ this->generatingBlock = generatingBlock;
+ this->externalIndex = externalIndex;
+
+ yReq = yReal = size.width = size.ascent = size.descent = 0;
+ dirty = sizeChangedSinceLastAllocation = true;
+ inCBList = false;
+}
+
+void OutOfFlowMgr::Float::intoStringBuffer(StringBuffer *sb)
+{
+ sb->append ("{ widget = ");
+ sb->appendPointer (widget);
+
+ if (widget) {
+ sb->append (" (");
+ sb->append (widget->getClassName ());
+ sb->append (")");
+ }
+
+ sb->append (", index = ");
+ sb->appendInt (index);
+ sb->append (", sideSpanningIndex = ");
+ sb->appendInt (sideSpanningIndex);
+ sb->append (", generatingBlock = ");
+ sb->appendPointer (generatingBlock);
+ sb->append (", yReq = ");
+ sb->appendInt (yReq);
+ sb->append (", yReal = ");
+ sb->appendInt (yReal);
+ sb->append (", size = { ");
+ sb->appendInt (size.width);
+ sb->append (", ");
+ sb->appendInt (size.ascent);
+ sb->append (" + ");
+ sb->appendInt (size.descent);
+ sb->append (" }, dirty = ");
+ sb->appendBool (dirty);
+ sb->append (", sizeChangedSinceLastAllocation = ");
+ sb->appendBool (sizeChangedSinceLastAllocation);
+ sb->append (", inCBList = ");
+ sb->appendBool (inCBList);
+ sb->append (" }");
+}
+
+int OutOfFlowMgr::Float::compareTo(Comparable *other)
+{
+ Float *otherFloat = (Float*)other;
+
+ if (oofm->wasAllocated (generatingBlock)) {
+ assert (oofm->wasAllocated (otherFloat->generatingBlock));
+ return yForContainer() - otherFloat->yForContainer();
+ } else {
+ assert (generatingBlock == otherFloat->generatingBlock);
+ return yReal - otherFloat->yReal;
+ }
+}
+
+int OutOfFlowMgr::Float::yForTextblock (Textblock *textblock, int y)
+{
+ if (oofm->wasAllocated (generatingBlock)) {
+ assert (oofm->wasAllocated (textblock));
+ return oofm->getAllocation(generatingBlock)->y + y
+ - oofm->getAllocation(textblock)->y;
+ } else {
+ assert (textblock == generatingBlock);
+ return y;
+ }
+}
+
+int OutOfFlowMgr::Float::yForContainer (int y)
+{
+ assert (oofm->wasAllocated (generatingBlock));
+ return y + oofm->getAllocation(generatingBlock)->y -
+ oofm->getAllocation(oofm->containingBlock)->y;
+}
+
+bool OutOfFlowMgr::Float::covers (Textblock *textblock, int y, int h)
+{
+ int reqy, fly; // either widget or canvas coordinates
+ if (oofm->wasAllocated (generatingBlock)) {
+ assert (oofm->wasAllocated (textblock));
+ reqy = oofm->getAllocation(textblock)->y + y;
+ fly = oofm->getAllocation(generatingBlock)->y + yReal;
+ } else {
+ assert (textblock == generatingBlock);
+ reqy = y;
+ fly = yReal;
+ }
+
+ oofm->ensureFloatSize (this);
+
+ //printf ("[%p] COVERS (%p, %d, %d) => %d + %d + %d > %d && %d < %d + %d? "
+ // "%s.\n", oofm->containingBlock, textblock, y, h, fly, size.ascent,
+ // size.descent, reqy, fly, reqy, h,
+ // (fly + size.ascent + size.descent > reqy && fly < reqy + h) ?
+ // "yes" : "no");
+
+ return fly + size.ascent + size.descent > reqy && fly < reqy + h;
+}
+
+int OutOfFlowMgr::Float::CompareSideSpanningIndex::compare(Object *o1,
+ Object *o2)
+{
+ return ((Float*)o1)->sideSpanningIndex - ((Float*)o2)->sideSpanningIndex;
+}
+
+int OutOfFlowMgr::Float::CompareGBAndExtIndex::compare(Object *o1, Object *o2)
+{
+ Float *f1 = (Float*)o1, *f2 = (Float*)o2;
+
+ //printf ("[%p] comparing (%p, %d) with (%p, %d) ...\n",
+ // oofm->containingBlock, f1->generatingBlock, f1->externalIndex,
+ // f2->generatingBlock, f2->externalIndex);
+
+ if (f1->generatingBlock == f2->generatingBlock) {
+ //printf (" (a) generating blocks equal => %d - %d = %d\n",
+ // f1->externalIndex, f2->externalIndex,
+ // f1->externalIndex - f2->externalIndex);
+ return f1->externalIndex - f2->externalIndex;
+ } else {
+ TBInfo *t1 = oofm->getTextblock (f1->generatingBlock),
+ *t2 = oofm->getTextblock (f2->generatingBlock);
+
+ for (TBInfo *t = t1; t != NULL; t = t->parent)
+ if (t->parent == t2) {
+ //printf (" (b) %p is an achestor of %p; direct child is %p (%d)"
+ // " => %d - %d = %d\n", t2->textblock, t1->textblock,
+ // t->textblock, t->parentExtIndex, t->parentExtIndex,
+ // f2->externalIndex, t->parentExtIndex - f2->externalIndex);
+ return t->parentExtIndex - f2->externalIndex;
+ }
+
+ for (TBInfo *t = t2; t != NULL; t = t->parent)
+ if (t->parent == t1) {
+ //printf (" (c) %p is an achestor of %p; direct child is %p (%d)"
+ // " => %d - %d = %d\n", t1->textblock, t2->textblock,
+ // t->textblock, t->parentExtIndex, f1->externalIndex,
+ // t->parentExtIndex, f1->externalIndex - t->parentExtIndex);
+ return f1->externalIndex - t->parentExtIndex;
+ }
+
+ //printf (" (d) other => %d - %d = %d\n",
+ // t1->index, t2->index, t1->index - t2->index);
+ return t1->index - t2->index;
+ }
+}
+
+int OutOfFlowMgr::SortedFloatsVector::findFloatIndex (Textblock *lastGB,
+ int lastExtIndex)
+{
+ Float key (oofm, NULL, lastGB, lastExtIndex);
+ Float::CompareGBAndExtIndex cmp (oofm);
+ int i = bsearch (&key, false, &cmp);
+
+ // At position i is the next larger element, so element i should
+ // not included, but i - 1 returned; except if the exact element is
+ // found: then include it and so return i.
+ int r;
+ if (i == size())
+ r = i - 1;
+ else {
+ Float *f = get (i);
+ if (cmp.compare (f, &key) == 0)
+ r = i;
+ else
+ r = i - 1;
+ }
+
+ //printf ("[%p] findFloatIndex (%p, %d) => i = %d, r = %d (size = %d); "
+ // "in %s list %p on the %s side\n",
+ // oofm->containingBlock, lastGB, lastExtIndex, i, r, size (),
+ // type == GB ? "GB" : "CB", this, side == LEFT ? "left" : "right");
+
+ //for (int i = 0; i < size (); i++) {
+ // Float *f = get(i);
+ // TBInfo *t = oofm->getTextblock(f->generatingBlock);
+ // printf (" %d: (%p [%d, %p], %d)\n", i, f->generatingBlock,
+ // t->index, t->parent ? t->parent->textblock : NULL,
+ // get(i)->externalIndex);
+ //}
+
+ return r;
+}
+
+int OutOfFlowMgr::SortedFloatsVector::find (Textblock *textblock, int y,
+ int start, int end)
+{
+ Float key (oofm, NULL, NULL, 0);
+ key.generatingBlock = textblock;
+ key.yReal = y;
+ return bsearch (&key, false, start, end);
+}
+
+int OutOfFlowMgr::SortedFloatsVector::findFirst (Textblock *textblock,
+ int y, int h,
+ Textblock *lastGB,
+ int lastExtIndex)
+{
+ int last = findFloatIndex (lastGB, lastExtIndex);
+ assert (last < size());
+ int i = find (textblock, y, 0, last);
+
+ //printf ("[%p] FIND (%s, %p, allocated: %s, %d, %p, %d) => last = %d, "
+ // "result = %d (of %d)\n", oofm->containingBlock,
+ // type == GB ? "GB" : "CB", textblock,
+ // oofm->wasAllocated (textblock) ? "true" : "false", y, lastGB,
+ // lastExtIndex, last, i, size());
+
+ // Note: The smallest value of "i" is 0, which means that "y" is before or
+ // equal to the first float. The largest value is "last + 1", which means
+ // that "y" is after the last float. In both cases, the first or last,
+ // respectively, float is a candidate. Generally, both floats, before and
+ // at the search position, are candidates.
+
+ if (i > 0 && get(i - 1)->covers (textblock, y, h))
+ return i - 1;
+ else if (i <= last && get(i)->covers (textblock, y, h))
+ return i;
+ else
+ return -1;
+}
+
+int OutOfFlowMgr::SortedFloatsVector::findLastBeforeSideSpanningIndex
+ (int sideSpanningIndex)
+{
+ OutOfFlowMgr::Float::CompareSideSpanningIndex comparator;
+ Float key (NULL, NULL, NULL, 0);
+ key.sideSpanningIndex = sideSpanningIndex;
+ return bsearch (&key, false, &comparator) - 1;
+}
+
+void OutOfFlowMgr::SortedFloatsVector::put (Float *vloat)
+{
+ lout::container::typed::Vector<Float>::put (vloat);
+ vloat->index = size() - 1;
+ vloat->inCBList = type == CB;
+}
+
+OutOfFlowMgr::TBInfo::TBInfo (OutOfFlowMgr *oofm, Textblock *textblock,
+ TBInfo *parent, int parentExtIndex)
+{
+ this->textblock = textblock;
+ this->parent = parent;
+ this->parentExtIndex = parentExtIndex;
+
+ leftFloatsGB = new SortedFloatsVector (oofm, LEFT, SortedFloatsVector::GB);
+ rightFloatsGB = new SortedFloatsVector (oofm, RIGHT, SortedFloatsVector::GB);
+}
+
+OutOfFlowMgr::TBInfo::~TBInfo ()
+{
+ delete leftFloatsGB;
+ delete rightFloatsGB;
+}
+
+OutOfFlowMgr::AbsolutelyPositioned::AbsolutelyPositioned (OutOfFlowMgr *oofm,
+ core::Widget *widget,
+ Textblock
+ *generatingBlock,
+ int externalIndex)
+{
+ this->widget = widget;
+ dirty = true;
+}
+
+OutOfFlowMgr::OutOfFlowMgr (Textblock *containingBlock)
+{
+ //printf ("OutOfFlowMgr::OutOfFlowMgr\n");
+
+ this->containingBlock = containingBlock;
+
+ leftFloatsCB = new SortedFloatsVector (this, LEFT, SortedFloatsVector::CB);
+ rightFloatsCB = new SortedFloatsVector (this, RIGHT, SortedFloatsVector::CB);
+
+ leftFloatsAll = new Vector<Float> (1, true);
+ rightFloatsAll = new Vector<Float> (1, true);
+
+ floatsByWidget = new HashTable <TypedPointer <Widget>, Float> (true, false);
+
+ tbInfos = new Vector<TBInfo> (1, false);
+ tbInfosByTextblock =
+ new HashTable <TypedPointer <Textblock>, TBInfo> (true, true);
+
+ leftFloatsMark = rightFloatsMark = 0;
+ lastLeftTBIndex = lastRightTBIndex = 0;
+
+ absolutelyPositioned = new Vector<AbsolutelyPositioned> (1, true);
+
+ containingBlockWasAllocated = containingBlock->wasAllocated ();
+ if (containingBlockWasAllocated)
+ containingBlockAllocation = *(containingBlock->getAllocation());
+
+ addWidgetInFlow (containingBlock, NULL, 0);
+}
+
+OutOfFlowMgr::~OutOfFlowMgr ()
+{
+ //printf ("OutOfFlowMgr::~OutOfFlowMgr\n");
+
+ delete leftFloatsCB;
+ delete rightFloatsCB;
+
+ // Order is important: tbInfosByTextblock is owner of the instances
+ // of TBInfo.tbInfosByTextblock
+ delete tbInfos;
+ delete tbInfosByTextblock;
+
+ delete floatsByWidget;
+
+ // Order is important, since the instances of Float are owned by
+ // leftFloatsAll and rightFloatsAll, so these should be deleted
+ // last.
+ delete leftFloatsAll;
+ delete rightFloatsAll;
+
+ delete absolutelyPositioned;
+}
+
+void OutOfFlowMgr::sizeAllocateStart (Allocation *containingBlockAllocation)
+{
+ this->containingBlockAllocation = *containingBlockAllocation;
+ containingBlockWasAllocated = true;
+}
+
+void OutOfFlowMgr::sizeAllocateEnd ()
+{
+ //printf ("[%p] SIZE_ALLOCATE_END: leftFloatsMark = %d, "
+ // "rightFloatsMark = %d\n",
+ // containingBlock, leftFloatsMark, rightFloatsMark);
+
+ // 1. Move floats from GB lists to the one CB list.
+ moveFromGBToCB (LEFT);
+ moveFromGBToCB (RIGHT);
+
+ // 2. Floats and absolutely positioned blocks have to be allocated
+ sizeAllocateFloats (LEFT);
+ sizeAllocateFloats (RIGHT);
+ sizeAllocateAbsolutelyPositioned ();
+
+ // 3. Textblocks have already been allocated, but we (i) check
+ // allocation change of textblocks, and (ii) store some information
+ // for later use.
+ for (lout::container::typed::Iterator<TypedPointer <Textblock> > it =
+ tbInfosByTextblock->iterator ();
+ it.hasNext (); ) {
+ TypedPointer <Textblock> *key = it.getNext ();
+ TBInfo *tbInfo = tbInfosByTextblock->get (key);
+ Textblock *tb = key->getTypedValue();
+
+ Allocation *tbAllocation = getAllocation (tb);
+ int xCB = tbAllocation->x - containingBlockAllocation.x;
+ int yCB = tbAllocation->y - containingBlockAllocation.y;
+ int width = tbAllocation->width;
+ int height = tbAllocation->ascent + tbAllocation->descent;
+
+ // (i) Check allocation change of textblocks.
+ if ((!tbInfo->wasAllocated || tbInfo->xCB != xCB || tbInfo->yCB != yCB ||
+ tbInfo->width != width || tbInfo->height != height)) {
+ // Changed: change borders when covered by floats.
+ int oldPos, newPos;
+ Widget *oldFloat, *newFloat;
+ // To calculate the minimum, both allocations, old and new,
+ // have to be tested.
+
+ // Old allocation:
+ bool c1 = isTextblockCoveredByFloats (tb,
+ tbInfo->xCB
+ + containingBlockAllocation.x,
+ tbInfo->yCB
+ + containingBlockAllocation.y,
+ tbInfo->width, tbInfo->height,
+ &oldPos, &oldFloat);
+ // new allocation:
+ int c2 = isTextblockCoveredByFloats (tb, tbAllocation->x,
+ tbAllocation->y, width,
+ height, &newPos, &newFloat);
+ if (c1 || c2) {
+ if (!c1)
+ tb->borderChanged (newPos, newFloat);
+ else if (!c2)
+ tb->borderChanged (oldPos, oldFloat);
+ else {
+ if (oldPos < newPos)
+ tb->borderChanged (oldPos, oldFloat);
+ else
+ tb->borderChanged (newPos, newFloat);
+ }
+ }
+ }
+
+ // TODO Comment and re-number.
+ checkChangedFloatSizes ();
+
+ // (ii) store some information for later use.
+ tbInfo->wasAllocated = true;
+ tbInfo->xCB = xCB;
+ tbInfo->yCB = yCB;
+ tbInfo->width = width;
+ tbInfo->height = height;
+ }
+}
+
+bool OutOfFlowMgr::isTextblockCoveredByFloats (Textblock *tb, int tbx, int tby,
+ int tbWidth, int tbHeight,
+ int *minFloatPos,
+ Widget **minFloat)
+{
+ int leftMinPos, rightMinPos;
+ Widget *leftMinFloat, *rightMinFloat;
+ bool c1 = isTextblockCoveredByFloats (leftFloatsCB, tb, tbx, tby,
+ tbWidth, tbHeight, &leftMinPos,
+ &leftMinFloat);
+ bool c2 = isTextblockCoveredByFloats (rightFloatsCB, tb, tbx, tby,
+ tbWidth, tbHeight, &rightMinPos,
+ &rightMinFloat);
+ if (c1 || c2) {
+ if (!c1) {
+ *minFloatPos = rightMinPos;
+ *minFloat = rightMinFloat;
+ } else if (!c2) {
+ *minFloatPos = leftMinPos;
+ *minFloat = leftMinFloat;
+ } else {
+ if (leftMinPos < rightMinPos) {
+ *minFloatPos = leftMinPos;
+ *minFloat = leftMinFloat;
+ } else{
+ *minFloatPos = rightMinPos;
+ *minFloat = rightMinFloat;
+ }
+ }
+ }
+
+ return c1 || c2;
+}
+
+bool OutOfFlowMgr::isTextblockCoveredByFloats (SortedFloatsVector *list,
+ Textblock *tb, int tbx, int tby,
+ int tbWidth, int tbHeight,
+ int *minFloatPos,
+ Widget **minFloat)
+{
+ bool covered = false;
+
+ for (int i = 0; i < list->size(); i++) {
+ // TODO binary search
+ Float *vloat = list->get(i);
+ int floatPos;
+
+ // TODO Clarify the old condition: tb != v->generatingBlock. Neccessary?
+ if (tb != vloat->generatingBlock &&
+ isTextblockCoveredByFloat (vloat, tb, tbx, tby, tbWidth, tbHeight,
+ &floatPos)) {
+ if (!covered || floatPos < *minFloatPos) {
+ *minFloatPos = floatPos;
+ *minFloat = vloat->widget;
+ }
+ covered = true;
+ }
+
+ // All floarts are searched, to find the minimum. TODO: Are
+ // floats sorted, so this can be shortened? (The first is the
+ // minimum?)
+ }
+
+ return covered;
+}
+
+bool OutOfFlowMgr::isTextblockCoveredByFloat (Float *vloat, Textblock *tb,
+ int tbx, int tby,
+ int tbWidth, int tbHeight,
+ int *floatPos)
+{
+ assert (wasAllocated (vloat->generatingBlock));
+
+ int flh = vloat->dirty ? 0 : vloat->size.ascent + vloat->size.descent;
+ int y1 = getAllocation(vloat->generatingBlock)->y + vloat->yReal;
+ int y2 = y1 + flh;
+
+ // TODO: Also regard horizontal dimension (same for tellFloatPosition)?
+ if (y2 > tby && y1 < tby + tbHeight) {
+ *floatPos = y1 - tby;
+ return true;
+ } else
+ return false;
+}
+
+void OutOfFlowMgr::checkChangedFloatSizes ()
+{
+ checkChangedFloatSizes (leftFloatsCB);
+ checkChangedFloatSizes (rightFloatsCB);
+}
+
+void OutOfFlowMgr::checkChangedFloatSizes (SortedFloatsVector *list)
+{
+ // TODO (i) Comment (ii) linear search?
+ for (int i = 0; i < list->size(); i++) {
+ // TODO binary search
+ Float *vloat = list->get(i);
+
+ if (vloat->sizeChangedSinceLastAllocation &&
+ wasAllocated (vloat->generatingBlock)) {
+ //printf ("=== start checking textblocks ===\n");
+
+ for (lout::container::typed::Iterator<TypedPointer <Textblock> > it =
+ tbInfosByTextblock->iterator ();
+ it.hasNext (); ) {
+ Textblock *tb = it.getNext()->getTypedValue();
+ if (wasAllocated (tb)) {
+ Allocation *tba = getAllocation (tb);
+ int floatPos;
+
+ if (isTextblockCoveredByFloat
+ (vloat, tb, tba->x - containingBlockAllocation.x,
+ tba->y - containingBlockAllocation.y,
+ tba->width, tba->ascent + tba->descent, &floatPos)) {
+ //printf (" ---> yes: %p (parent: %p)\n", tb,
+ // tb->getParent());
+ tb->borderChanged (floatPos, vloat->widget);
+ } //else
+ // printf (" ---> not covered: %p (parent: %p)\n", tb,
+ // tb->getParent());
+ } //else
+ // printf (" ---> not allocated: %p (parent: %p)\n", tb,
+ // tb->getParent());
+ }
+
+ //printf ("=== end checking textblocks ===\n");
+
+ vloat->sizeChangedSinceLastAllocation = false;
+ }
+ }
+}
+
+void OutOfFlowMgr::moveFromGBToCB (Side side)
+{
+ SortedFloatsVector *dest = side == LEFT ? leftFloatsCB : rightFloatsCB;
+ int *floatsMark = side == LEFT ? &leftFloatsMark : &rightFloatsMark;
+
+ for (int mark = 0; mark <= *floatsMark; mark++)
+ for (lout::container::typed::Iterator<TBInfo> it = tbInfos->iterator ();
+ it.hasNext (); ) {
+ TBInfo *tbInfo = it.getNext ();
+ SortedFloatsVector *src =
+ side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB;
+ for (int i = 0; i < src->size (); i++) {
+ Float *vloat = src->get(i);
+ if (!vloat->inCBList && vloat->mark == mark) {
+ dest->put (vloat);
+ //printf("[%p] moving %s float %p (%s %p, mark %d) to CB list\n",
+ // containingBlock, side == LEFT ? "left" : "right",
+ // vloat, vloat->widget->getClassName(), vloat->widget,
+ // vloat->mark);
+ }
+ }
+ }
+
+ *floatsMark = 0;
+
+ /* Old code: GB lists do not have to be cleared, but their contents
+ are still useful after allocation. Soon to be deleted, not only
+ uncommented.
+
+ for (lout::container::typed::Iterator<TBInfo> it = tbInfos->iterator ();
+ it.hasNext (); ) {
+ TBInfo *tbInfo = it.getNext ();
+ SortedFloatsVector *src =
+ side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB;
+ src->clear ();
+ }
+ */
+
+ //printf ("[%p] new %s list:\n",
+ // containingBlock, side == LEFT ? "left" : "right");
+ //for (int i = 0; i < dest->size(); i++)
+ // printf (" %d: %s\n", i, dest->get(i)->toString());
+}
+
+void OutOfFlowMgr::sizeAllocateFloats (Side side)
+{
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+
+ for (int i = 0; i < list->size(); i++) {
+ // TODO Missing: check newly calculated positions, collisions,
+ // and queue resize, when neccessary. TODO: See step 2?
+
+ Float *vloat = list->get(i);
+ ensureFloatSize (vloat);
+
+ Allocation *gbAllocation = getAllocation(vloat->generatingBlock);
+ Allocation *cbAllocation = getAllocation(containingBlock);
+ // In some cases, the actual (allocated) width is too large; we
+ // use the "available" width here.
+ int gbWidth =
+ min (gbAllocation->width, vloat->generatingBlock->getAvailWidth());
+
+ Allocation childAllocation;
+
+ switch (side) {
+ case LEFT:
+ // Left floats are always aligned on the left side of the
+ // generator (content, not allocation).
+ childAllocation.x = gbAllocation->x
+ + vloat->generatingBlock->getStyle()->boxOffsetX();
+ break;
+
+ case RIGHT:
+ // Similar for right floats, but in this case, floats are
+ // shifted to the right when they are too big (instead of
+ // shifting the generator to the right).
+ childAllocation.x =
+ max (gbAllocation->x + gbWidth - vloat->size.width
+ - vloat->generatingBlock->getStyle()->boxRestWidth(),
+ // Do not exceed CB allocation:
+ cbAllocation->x);
+ break;
+ }
+
+ childAllocation.y = gbAllocation->y + vloat->yReal;
+ childAllocation.width = vloat->size.width;
+ childAllocation.ascent = vloat->size.ascent;
+ childAllocation.descent = vloat->size.descent;
+
+ vloat->widget->sizeAllocate (&childAllocation);
+
+ //printf (" allocate %s float #%d -> (%d, %d), %d x (%d + %d)\n",
+ // side == LEFT ? "left" : "right", i, childAllocation.x,
+ // childAllocation.y, childAllocation.width,
+ // childAllocation.ascent, childAllocation.descent);
+ }
+}
+
+
+
+void OutOfFlowMgr::draw (View *view, Rectangle *area)
+{
+ drawFloats (leftFloatsCB, view, area);
+ drawFloats (rightFloatsCB, view, area);
+ drawAbsolutelyPositioned (view, area);
+}
+
+void OutOfFlowMgr::drawFloats (SortedFloatsVector *list, View *view,
+ Rectangle *area)
+{
+ // This could be improved, since the list is sorted: search the
+ // first float fitting into the area, and iterate until one is
+ // found below the area.
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ core::Rectangle childArea;
+ if (vloat->widget->intersects (area, &childArea))
+ vloat->widget->draw (view, &childArea);
+ }
+}
+
+void OutOfFlowMgr::drawAbsolutelyPositioned (View *view, Rectangle *area)
+{
+ for (int i = 0; i < absolutelyPositioned->size(); i++) {
+ AbsolutelyPositioned *abspos = absolutelyPositioned->get(i);
+ core::Rectangle childArea;
+ if (abspos->widget->intersects (area, &childArea))
+ abspos->widget->draw (view, &childArea);
+ }
+}
+
+/**
+ * This method consideres also the attributes not yet considered by
+ * dillo, so that the containing block is determined correctly, which
+ * leads sometimes to a cleaner rendering.
+ */
+bool OutOfFlowMgr::isWidgetOutOfFlow (core::Widget *widget)
+{
+ return
+ widget->getStyle()->vloat != core::style::FLOAT_NONE ||
+ widget->getStyle()->position == core::style::POSITION_ABSOLUTE ||
+ widget->getStyle()->position == core::style::POSITION_FIXED;
+}
+
+bool OutOfFlowMgr::isWidgetHandledByOOFM (core::Widget *widget)
+{
+ // May be extended for fixed (and relative?) positions.
+ return isWidgetFloat (widget);
+ // TODO temporary disabled: || isWidgetAbsolutelyPositioned (widget);
+}
+
+void OutOfFlowMgr::addWidgetInFlow (Textblock *textblock,
+ Textblock *parentBlock, int externalIndex)
+{
+ //printf ("[%p] addWidgetInFlow (%p, %p, %d)\n",
+ // containingBlock, textblock, parentBlock, externalIndex);
+
+ TBInfo *tbInfo =
+ new TBInfo (this, textblock,
+ parentBlock ? getTextblock (parentBlock) : NULL,
+ externalIndex);
+
+ tbInfo->wasAllocated = false;
+ tbInfo->index = tbInfos->size();
+
+ tbInfos->put (tbInfo);
+ tbInfosByTextblock->put (new TypedPointer<Textblock> (textblock), tbInfo);
+}
+
+void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock,
+ int externalIndex)
+{
+ if (isWidgetFloat (widget)) {
+ TBInfo *tbInfo = getTextblock (generatingBlock);
+
+ Float *vloat = new Float (this, widget, generatingBlock, externalIndex);
+
+ // Note: Putting the float first in the GB list, and then,
+ // possibly into the CB list (in that order) will trigger
+ // setting Float::inCBList to the right value.
+
+ switch (widget->getStyle()->vloat) {
+ case FLOAT_LEFT:
+ leftFloatsAll->put (vloat);
+ widget->parentRef = createRefLeftFloat (leftFloatsAll->size() - 1);
+ tbInfo->leftFloatsGB->put (vloat);
+
+ if (wasAllocated (generatingBlock)) {
+ leftFloatsCB->put (vloat);
+ //printf ("[%p] adding left float %p (%s %p) to CB list\n",
+ // containingBlock, vloat, widget->getClassName(), widget);
+ } else {
+ if (tbInfo->index < lastLeftTBIndex)
+ leftFloatsMark++;
+
+ vloat->mark = leftFloatsMark;
+ //printf ("[%p] adding left float %p (%s %p, mark %d) to GB list "
+ // "(index %d, last = %d)\n",
+ // containingBlock, vloat, widget->getClassName(), widget,
+ // vloat->mark, tbInfo->index, lastLeftTBIndex);
+
+ lastLeftTBIndex = tbInfo->index;
+ }
+ break;
+
+ case FLOAT_RIGHT:
+ rightFloatsAll->put (vloat);
+ widget->parentRef = createRefRightFloat (rightFloatsAll->size() - 1);
+ tbInfo->rightFloatsGB->put (vloat);
+
+ if (wasAllocated (generatingBlock)) {
+ rightFloatsCB->put (vloat);
+ //printf ("[%p] adding right float %p (%s %p) to CB list\n",
+ // containingBlock, vloat, widget->getClassName(), widget);
+ } else {
+ if (tbInfo->index < lastRightTBIndex)
+ rightFloatsMark++;
+
+ vloat->mark = rightFloatsMark;
+ //printf ("[%p] adding right float %p (%s %p, mark %d) to GB list "
+ // "(index %d, last = %d)\n",
+ // containingBlock, vloat, widget->getClassName(), widget,
+ // vloat->mark, tbInfo->index, lastRightTBIndex);
+
+ lastRightTBIndex = tbInfo->index;
+ }
+
+ break;
+
+ default:
+ assertNotReached();
+ }
+
+ // "sideSpanningIndex" is only compared, so this simple
+ // assignment is sufficient; differenciation between GB and CB
+ // lists is not neccessary. TODO: Can this also be applied to
+ // "index", to simplify the current code? Check: where is
+ // "index" used.
+ vloat->sideSpanningIndex =
+ leftFloatsAll->size() + rightFloatsAll->size() - 1;
+
+ floatsByWidget->put (new TypedPointer<Widget> (widget), vloat);
+ } else if (isWidgetAbsolutelyPositioned (widget)) {
+ AbsolutelyPositioned *abspos =
+ new AbsolutelyPositioned (this, widget, generatingBlock,
+ externalIndex);
+ absolutelyPositioned->put (abspos);
+ widget->parentRef =
+ createRefAbsolutelyPositioned (absolutelyPositioned->size() - 1);
+ } else
+ // May be extended.
+ assertNotReached();
+}
+
+void OutOfFlowMgr::moveExternalIndices (Textblock *generatingBlock,
+ int oldStartIndex, int diff)
+{
+ TBInfo *tbInfo = getTextblock (generatingBlock);
+ moveExternalIndices (tbInfo->leftFloatsGB, oldStartIndex, diff);
+ moveExternalIndices (tbInfo->rightFloatsGB, oldStartIndex, diff);
+}
+
+void OutOfFlowMgr::moveExternalIndices (SortedFloatsVector *list,
+ int oldStartIndex, int diff)
+{
+ // Could be faster with binary search, but the GB (not CB!) lists
+ // should be rather small.
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ if (vloat->externalIndex >= oldStartIndex)
+ vloat->externalIndex += diff;
+ }
+}
+
+OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget)
+{
+ TypedPointer <Widget> key (widget);
+ Float *vloat = floatsByWidget->get (&key);
+ assert (vloat != NULL);
+ return vloat;
+}
+
+void OutOfFlowMgr::markSizeChange (int ref)
+{
+ //printf ("[%p] MARK_SIZE_CHANGE (%d)\n", containingBlock, ref);
+
+ if (isRefFloat (ref)) {
+ Float *vloat;
+
+ if (isRefLeftFloat (ref)) {
+ int i = getFloatIndexFromRef (ref);
+ vloat = leftFloatsAll->get (i);
+ //printf (" => left float %d\n", i);
+ } else if (isRefRightFloat (ref)) {
+ int i = getFloatIndexFromRef (ref);
+ vloat = rightFloatsAll->get (i);
+ //printf (" => right float %d\n", i);
+ } else {
+ assertNotReached();
+ vloat = NULL; // compiler happiness
+ }
+
+ vloat->dirty = vloat->sizeChangedSinceLastAllocation = true;
+
+ // Effects take place in ensureFloatSize.
+ } else if (isRefAbsolutelyPositioned (ref)) {
+ int i = getAbsolutelyPositionedIndexFromRef (ref);
+ absolutelyPositioned->get(i)->dirty = true;
+ } else
+ assertNotReached();
+}
+
+
+void OutOfFlowMgr::markExtremesChange (int ref)
+{
+ // Nothing to do here.
+}
+
+Widget *OutOfFlowMgr::getWidgetAtPoint (int x, int y, int level)
+{
+ Widget *childAtPoint = getFloatWidgetAtPoint (leftFloatsCB, x, y, level);
+ if (childAtPoint == NULL)
+ childAtPoint = getFloatWidgetAtPoint (rightFloatsCB, x, y, level);
+ if (childAtPoint == NULL)
+ childAtPoint = getAbsolutelyPositionedWidgetAtPoint (x, y, level);
+ return childAtPoint;
+}
+
+Widget *OutOfFlowMgr::getFloatWidgetAtPoint (SortedFloatsVector *list,
+ int x, int y, int level)
+{
+ for (int i = 0; i < list->size(); i++) {
+ // Could use binary search to be faster.
+ Float *vloat = list->get(i);
+ Widget *childAtPoint = vloat->widget->getWidgetAtPoint (x, y, level + 1);
+ if (childAtPoint)
+ return childAtPoint;
+ }
+
+ return NULL;
+}
+
+Widget *OutOfFlowMgr::getAbsolutelyPositionedWidgetAtPoint (int x, int y,
+ int level)
+{
+ for (int i = 0; i < absolutelyPositioned->size(); i++) {
+ AbsolutelyPositioned *abspos = absolutelyPositioned->get(i);
+ Widget *childAtPoint = abspos->widget->getWidgetAtPoint (x, y, level + 1);
+ if (childAtPoint)
+ return childAtPoint;
+ }
+
+ return NULL;
+}
+
+void OutOfFlowMgr::tellPosition (Widget *widget, int yReq)
+{
+ if (isWidgetFloat (widget))
+ tellFloatPosition (widget, yReq);
+
+ // Nothing to do for absolutely positioned blocks.
+}
+
+
+void OutOfFlowMgr::tellFloatPosition (Widget *widget, int yReq)
+{
+ assert (yReq >= 0);
+
+ Float *vloat = findFloatByWidget(widget);
+
+ //printf ("[%p] TELL_FLOAT_POSITION (%p (%s), %d)\n",
+ // containingBlock, widget, widget->getClassName (), yReq);
+ //printf (" this float: %s\n", vloat->toString());
+
+ SortedFloatsVector *listSame, *listOpp;
+ getFloatsLists (vloat, &listSame, &listOpp);
+ ensureFloatSize (vloat);
+ //printf (" ensured size: %s\n", vloat->toString());
+
+ //printf (" all floats on same side (%s):\n",
+ // listSame->type == SortedFloatsVector::GB ? "GB" : "CB");
+ //for (int i = 0; i < listSame->size(); i++)
+ // printf (" %d: %p, %s\n", i, listSame->get(i),
+ // listSame->get(i)->toString());
+
+ int oldY = vloat->yReal;
+
+ // "yReal" may change due to collisions (see below).
+ vloat->yReq = vloat->yReal = yReq;
+
+ // Test collisions (on this side). Only previous float is relevant.
+ int yRealNew;
+ if (vloat->index >= 1 &&
+ collides (vloat, listSame->get (vloat->index - 1), &yRealNew)) {
+ vloat->yReal = yRealNew;
+ //printf (" collides; yReal = %d\n", vloat->yReal);
+ }
+
+ // Test collisions (on the opposite side). Search the last float on
+ // the other size before this float; only this is relevant.
+ int lastOppFloat =
+ listOpp->findLastBeforeSideSpanningIndex (vloat->sideSpanningIndex);
+ if (lastOppFloat >= 0) {
+ Float *last = listOpp->get (lastOppFloat);
+ if (collides (vloat, last, &yRealNew)) {
+ // Here, test also horizontal values.
+ bool collidesH;
+ if (vloat->generatingBlock == last->generatingBlock)
+ collidesH = vloat->size.width + last->size.width +
+ vloat->generatingBlock->getStyle()->boxDiffWidth()
+ > vloat->generatingBlock->getAvailWidth();
+ else {
+ // Here (different generating blocks) it can be assumed
+ // that the allocations are defined, otherwise, the float
+ // "last" would not be found in "listOpp".
+ assert (wasAllocated (vloat->generatingBlock));
+ assert (wasAllocated (last->generatingBlock));
+ Float *left, *right;
+ if (widget->getStyle()->vloat == FLOAT_LEFT) {
+ left = vloat;
+ right = last;
+ } else {
+ left = last;
+ right = vloat;
+ }
+
+ // right border of the left float (canvas coordinates)
+ int rightOfLeft =
+ left->generatingBlock->getAllocation()->x
+ + left->generatingBlock->getStyle()->boxOffsetX()
+ + left->size.width;
+ // left border of the right float (canvas coordinates)
+ int leftOfRight =
+ right->generatingBlock->getAllocation()->x
+ + min (right->generatingBlock->getAllocation()->width,
+ right->generatingBlock->getAvailWidth())
+ - right->generatingBlock->getStyle()->boxRestWidth()
+ - right->size.width;
+
+ collidesH = rightOfLeft > leftOfRight;
+ }
+
+ if (collidesH)
+ vloat->yReal = yRealNew;
+ }
+ }
+
+ // No call neccessary when yReal has not changed. (Notice that
+ // checking for yReq is wrong: yReq may remain the same, when yReal
+ // changes, e. g. when previous float has changes its size.
+ if (vloat->yReal != oldY)
+ checkCoveragePosChanged (vloat, oldY);
+}
+
+bool OutOfFlowMgr::collides (Float *vloat, Float *other, int *yReal)
+{
+ ensureFloatSize (other);
+ int otherH = other->size.ascent + other->size.descent;
+
+ if (wasAllocated (other->generatingBlock)) {
+ assert (wasAllocated (vloat->generatingBlock));
+
+ if (getAllocation(vloat->generatingBlock)->y + vloat->yReal <
+ getAllocation(other->generatingBlock)->y + other->yReal + otherH) {
+ *yReal =
+ getAllocation(other->generatingBlock)->y + other->yReal
+ + otherH - getAllocation(vloat->generatingBlock)->y;
+ return true;
+ }
+ } else {
+ assert (vloat->generatingBlock == other->generatingBlock);
+
+ if (vloat->yReal < other->yReal + otherH) {
+ *yReal = other->yReal + otherH;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void OutOfFlowMgr::checkCoveragePosChanged (Float *vloat, int oldY)
+{
+ // Only this float has been changed (see tellFloatPosition), so
+ // only this float has to be tested against all textblocks.
+ if (wasAllocated (vloat->generatingBlock)) {
+ // TODO This (and similar code) is not very efficient.
+ for (lout::container::typed::Iterator<TypedPointer <Textblock> > it =
+ tbInfosByTextblock->iterator (); it.hasNext (); ) {
+ TypedPointer <Textblock> *key = it.getNext ();
+ Textblock *textblock = key->getTypedValue();
+
+ if (textblock != vloat->generatingBlock && wasAllocated (textblock)) {
+ Allocation *tba = getAllocation (textblock);
+ Allocation *gba = getAllocation (vloat->generatingBlock);
+ int tby1 = tba->y;
+ int tby2 = tba->y + tba->ascent + tba->descent;
+
+ int flh =
+ vloat->dirty ? 0 : vloat->size.ascent + vloat->size.descent;
+ int y1old = gba->y + oldY, y2old = y1old + flh;
+ int y1new = gba->y + vloat->yReal, y2new = y1new + flh;
+
+ bool covered =
+ (y2old > tby1 && y1old < tby2) || (y2new > tby1 && y1new < tby2);
+
+ if (covered) {
+ int yTextblock = gba->y + min (oldY, vloat->yReal) - tba->y;
+ textblock->borderChanged (yTextblock, vloat->widget);
+ }
+ }
+ }
+ }
+}
+
+void OutOfFlowMgr::getFloatsLists (Float *vloat, SortedFloatsVector **listSame,
+ SortedFloatsVector **listOpp)
+{
+ TBInfo *tbInfo = getTextblock (vloat->generatingBlock);
+
+ switch (vloat->widget->getStyle()->vloat) {
+ case FLOAT_LEFT:
+ if (wasAllocated (vloat->generatingBlock)) {
+ if (listSame) *listSame = leftFloatsCB;
+ if (listOpp) *listOpp = rightFloatsCB;
+ } else {
+ if (listSame) *listSame = tbInfo->leftFloatsGB;
+ if (listOpp) *listOpp = tbInfo->rightFloatsGB;
+ }
+ break;
+
+ case FLOAT_RIGHT:
+ if (wasAllocated (vloat->generatingBlock)) {
+ if (listSame) *listSame = rightFloatsCB;
+ if (listOpp) *listOpp = leftFloatsCB;
+ } else {
+ if (listSame) *listSame = tbInfo->rightFloatsGB;
+ if (listOpp) *listOpp = tbInfo->leftFloatsGB;
+ }
+ break;
+
+ default:
+ assertNotReached();
+ }
+}
+
+void OutOfFlowMgr::getSize (int cbWidth, int cbHeight,
+ int *oofWidth, int *oofHeight)
+{
+ // CbWidth and cbHeight *do* contain padding, border, and
+ // margin. See call in dw::Textblock::sizeRequest. (Notice that
+ // this has changed from an earlier version.)
+
+ // Also notice that Float::y includes margins etc.
+
+ // TODO Is it correct to add padding, border, and margin to the
+ // containing block? Check CSS spec.
+
+ //printf ("[%p] GET_SIZE (%d, %d, ...): %d / %d floats...\n",
+ // containingBlock, cbWidth, cbHeight,
+ // leftFloatsCB->size(), rightFloatsCB->size());
+
+ int oofWidthAbsPos, oofHeightAbsPos;
+ getAbsolutelyPositionedSize (&oofWidthAbsPos, &oofHeightAbsPos);
+
+ int oofWidthtLeft, oofWidthRight, oofHeightLeft, oofHeightRight;
+ getFloatsSize (leftFloatsCB, LEFT, &oofWidthtLeft, &oofHeightLeft);
+ getFloatsSize (rightFloatsCB, RIGHT, &oofWidthRight, &oofHeightRight);
+
+ *oofWidth = max (oofWidthtLeft, oofWidthRight, oofWidthAbsPos);
+ *oofHeight = max (oofHeightLeft, oofHeightRight, oofHeightAbsPos);
+
+ //printf (" => %d x %d => %d x %d (%d / %d)\n",
+ // cbWidth, cbHeight, *oofWidth, *oofHeight,
+ // oofHeightLeft, oofHeightRight);
+}
+
+void OutOfFlowMgr::getFloatsSize (SortedFloatsVector *list, Side side,
+ int *width, int *height)
+{
+ *width = 0;
+ *height = containingBlock->getStyle()->boxDiffHeight();
+
+ // Idea for a faster implementation: find the last float; this
+ // should be the relevant one, since the list is sorted.
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ ensureFloatSize (vloat);
+
+ // 1. Height: May play a role when the float is too wide.
+ // Minimum taken, since the relevant case is when the containing
+ // block is otherwise not wide enough.
+ int borderDiff = getMinBorderDiff (vloat, side);
+ *width = max (*width, vloat->size.width + borderDiff);
+
+ // 2. Height: Plays a role for floats hanging over at the bottom
+ // of the page.
+
+ // Notice that all positions are relative to the generating
+ // block, but we need them relative to the containing block.
+
+ // Position of generating block, relative to containing
+ // block. Greater or equal than 0, so dealing with 0 when it
+ // cannot yet be calculated is safe. (No distiction whether it
+ // is defined or not is necessary.)
+
+ int yGBinCB;
+
+ if (vloat->generatingBlock == containingBlock)
+ // Simplest case: the generator is the container.
+ yGBinCB = 0;
+ else {
+ if (wasAllocated (containingBlock)) {
+ if (wasAllocated (vloat->generatingBlock))
+ // Simple case: both containing block and generating
+ // block are defined.
+ yGBinCB = getAllocation(vloat->generatingBlock)->y
+ - containingBlock->getAllocation()->y;
+ else
+ // Generating block not yet allocation; the next
+ // allocation will, when necessary, trigger
+ // sizeRequest. (TODO: Is this really the case?)
+ yGBinCB = 0;
+ } else
+ // Nothing can be done now, but the next allocation
+ // will trigger sizeAllocate. (TODO: Is this really the
+ // case?)
+ yGBinCB = 0;
+ }
+
+ *height =
+ max (*height,
+ yGBinCB + vloat->yReal + vloat->size.ascent + vloat->size.descent
+ + containingBlock->getStyle()->boxRestHeight());
+ //printf (" float %d: (%d + %d) + (%d + %d + %d) => %d\n",
+ // i, yGBinCB, vloat->yReal, vloat->size.ascent,
+ // vloat->size.descent,
+ // containingBlock->getStyle()->boxRestHeight(), *height);
+ }
+}
+
+void OutOfFlowMgr::getExtremes (int cbMinWidth, int cbMaxWidth,
+ int *oofMinWidth, int *oofMaxWidth)
+{
+ *oofMinWidth = *oofMaxWidth = 0;
+ accumExtremes (leftFloatsCB, LEFT, oofMinWidth, oofMaxWidth);
+ accumExtremes (rightFloatsCB, RIGHT, oofMinWidth, oofMaxWidth);
+ // TODO Absolutely positioned elements
+}
+
+void OutOfFlowMgr::accumExtremes (SortedFloatsVector *list, Side side,
+ int *oofMinWidth, int *oofMaxWidth)
+{
+ // Idea for a faster implementation: use incremental resizing?
+
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+
+ int minBorderDiff = getMinBorderDiff (vloat, side);
+ int maxBorderDiff = getMaxBorderDiff (vloat, side);
+ Extremes extr;
+ vloat->widget->getExtremes (&extr);
+
+ *oofMinWidth = max (*oofMinWidth, extr.minWidth + minBorderDiff);
+ *oofMaxWidth = max (*oofMaxWidth, extr.maxWidth + maxBorderDiff);
+ }
+}
+
+/**
+ * Minimal difference between generating block and to containing
+ * block, Greater or equal than 0, so dealing with 0 when it cannot
+ * yet be calculated is safe. (No distiction whether it is defined or
+ * not is necessary.)
+ */
+int OutOfFlowMgr::getMinBorderDiff (Float *vloat, Side side)
+{
+ if (vloat->generatingBlock == containingBlock)
+ // Simplest case: the generator is the container.
+ // Since the way, how left and right floats are positioned when
+ // there is not much space, is not symmetric, the *minimal* value
+ // considered for the margin/border/padding of the generating block
+ // is *zero* for floats on the right.
+ return
+ side == LEFT ? vloat->generatingBlock->getStyle()->boxOffsetX() : 0;
+ else {
+ if (wasAllocated (containingBlock)) {
+ if (wasAllocated (vloat->generatingBlock)) {
+ // Simple case: both containing block and generating block
+ // are defined.
+ Allocation *gba = getAllocation(vloat->generatingBlock);
+ Allocation *cba = getAllocation(containingBlock);
+ if (side == LEFT)
+ return gba->x - cba->x +
+ vloat->generatingBlock->getStyle()->boxOffsetX();
+ else
+ // For margin/border/padding see comment above. Also,
+ // in the worst case, the float can take the whole CB
+ // (not GB!) width. Therefore:
+ return 0;
+ } else
+ // Generating block not yet allocation; the next
+ // allocation will, when necessary, trigger
+ // getExtremes. (TODO: Is this really the case?)
+ return 0;
+ } else
+ // Nothing can be done now, but the next allocation will
+ // trigger getExtremes. (TODO: Is this really the case?)
+ return 0;
+ }
+}
+
+/**
+ * Maximal difference between generating block and to containing
+ * block, i. e. sum on both sides.
+ */
+int OutOfFlowMgr::getMaxBorderDiff (Float *vloat, Side side)
+{
+ if (vloat->generatingBlock == containingBlock)
+ // Simplest case: the generator is the container.
+ return vloat->generatingBlock->getStyle()->boxDiffWidth();
+ else {
+ if (wasAllocated (containingBlock)) {
+ if (wasAllocated (vloat->generatingBlock))
+ // Simple case: both containing block and generating block
+ // are defined.
+ return getAllocation(containingBlock)->width -
+ getAllocation(vloat->generatingBlock)->width +
+ vloat->generatingBlock->getStyle()->boxDiffWidth();
+ else
+ // Generating block not yet allocation; the next
+ // allocation will, when necessary, trigger
+ // getExtremes. (TODO: Is this really the case?)
+ return 0;
+ } else
+ // Nothing can be done now, but the next allocation will
+ // trigger getExtremes. (TODO: Is this really the case?)
+ return 0;
+ }
+}
+
+OutOfFlowMgr::TBInfo *OutOfFlowMgr::getTextblock (Textblock *textblock)
+{
+ TypedPointer<Textblock> key (textblock);
+ TBInfo *tbInfo = tbInfosByTextblock->get (&key);
+ assert (tbInfo);
+ return tbInfo;
+}
+
+/**
+ * Get the left border for the vertical position of *y*, for a height
+ * of *h", based on floats; relative to the allocation of the calling
+ * textblock.
+ *
+ * The border includes marging/border/padding of the calling textblock
+ * but is 0 if there is no float, so a caller should also consider
+ * other borders.
+ */
+int OutOfFlowMgr::getLeftBorder (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ int b = getBorder (textblock, LEFT, y, h, lastGB, lastExtIndex);
+ //printf ("getLeftBorder (%p, %d, %d) => %d\n", textblock, y, h, b);
+ return b;
+}
+
+/**
+ * Get the right border for the vertical position of *y*, for a height
+ * of *h", based on floats.
+ *
+ * See also getLeftBorder(int, int);
+ */
+int OutOfFlowMgr::getRightBorder (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ int b = getBorder (textblock, RIGHT, y, h, lastGB, lastExtIndex);
+ //printf ("getRightBorder (%p, %d, %d) => %d\n", textblock, y, h, b);
+ return b;
+}
+
+int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ //printf ("[%p] GET_BORDER (%p (allocated: %s), %s, %d, %d, %p, %d)\n",
+ // containingBlock, textblock,
+ // wasAllocated (textblock) ? "true" : "false",
+ // side == LEFT ? "LEFT" : "RIGHT", y, h, lastGB, lastExtIndex);
+
+ SortedFloatsVector *list = getFloatsListForTextblock (textblock, side);
+
+ //printf (" searching in list:\n");
+ //for (int i = 0; i < list->size(); i++) {
+ // printf (" %d: %s\n", i, list->get(i)->toString());
+ // //printf (" (widget at (%d, %d))\n",
+ // // list->get(i)->widget->getAllocation()->x,
+ // // list->get(i)->widget->getAllocation()->y);
+ //}
+
+ int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex);
+
+ //printf (" first = %d\n", first);
+
+ if (first == -1)
+ // No float.
+ return 0;
+ else {
+ // It is not sufficient to find the first float, since a line
+ // (with height h) may cover the region of multiple float, of
+ // which the widest has to be choosen.
+ int border = 0;
+ bool covers = true;
+ // TODO Also check against lastGB and lastExtIndex
+ for (int i = first; covers && i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ covers = vloat->covers (textblock, y, h);
+ //printf (" float %d: %s; covers? %s.\n",
+ // i, vloat->toString(), covers ? "yes" : "no");
+
+ if (covers) {
+ int borderDiff = getBorderDiff (textblock, vloat, side);
+ int borderIn = side == LEFT ?
+ vloat->generatingBlock->getStyle()->boxOffsetX() :
+ vloat->generatingBlock->getStyle()->boxRestWidth();
+ border = max (border, vloat->size.width + borderIn + borderDiff);
+ //printf (" => border = %d\n", border);
+ }
+ }
+
+ return border;
+ }
+}
+
+
+OutOfFlowMgr::SortedFloatsVector *OutOfFlowMgr::getFloatsListForTextblock
+ (Textblock *textblock, Side side)
+{
+ if (wasAllocated (textblock))
+ return side == LEFT ? leftFloatsCB : rightFloatsCB;
+ else {
+ TBInfo *tbInfo = getTextblock (textblock);
+ return side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB;
+ }
+}
+
+
+bool OutOfFlowMgr::hasFloatLeft (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ return hasFloat (textblock, LEFT, y, h, lastGB, lastExtIndex);
+}
+
+bool OutOfFlowMgr::hasFloatRight (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ return hasFloat (textblock, RIGHT, y, h, lastGB, lastExtIndex);
+}
+
+bool OutOfFlowMgr::hasFloat (Textblock *textblock, Side side, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ //printf ("[%p] hasFloat (%p, %s, %d, %d, %p, %d)\n",
+ // containingBlock, textblock, side == LEFT ? "LEFT" : "RIGHT", y, h,
+ // lastGB, lastExtIndex);
+ SortedFloatsVector *list = getFloatsListForTextblock(textblock, side);
+ return list->findFirst (textblock, y, h, lastGB, lastExtIndex) != -1;
+}
+
+/**
+ * Returns position relative to the textblock "tb".
+ */
+int OutOfFlowMgr::getClearPosition (Textblock *tb)
+{
+ if (tb->getStyle()) {
+ bool left = false, right = false;
+ switch (tb->getStyle()->clear) {
+ case core::style::CLEAR_NONE: break;
+ case core::style::CLEAR_LEFT: left = true; break;
+ case core::style::CLEAR_RIGHT: right = true; break;
+ case core::style::CLEAR_BOTH: left = right = true; break;
+ default: assertNotReached ();
+ }
+
+ return max (left ? getClearPosition (tb, LEFT) : 0,
+ right ? getClearPosition (tb, RIGHT) : 0);
+ } else
+ return 0;
+}
+
+int OutOfFlowMgr::getClearPosition (Textblock *tb, Side side)
+{
+ if (!wasAllocated (tb))
+ // There is no relation yet to floats generated by other
+ // textblocks, and this textblocks floats are unimportant for
+ // the "clear" property.
+ return 0;
+
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+
+ int i = list->findFloatIndex (tb, 0);
+ if (i < 0)
+ return 0;
+
+ Float *vloat = list->get(i);
+ // We pass this texblock and 0 (first word, or smallest external
+ // index, respectively), but search for the last float before this
+ // position. Therefore this check.
+ if (vloat->generatingBlock== tb)
+ i--;
+ if (i < 0)
+ return 0;
+
+ vloat = list->get(i);
+ ensureFloatSize (vloat);
+ return
+ vloat->yReal + vloat->size.ascent + vloat->size.descent -
+ getAllocation(tb)->y;
+}
+
+void OutOfFlowMgr::ensureFloatSize (Float *vloat)
+{
+ if (vloat->dirty ||
+ // If the size of the containing block has changed (represented
+ // currently by the available width), a recalculation of a
+ // relative float width may also be necessary.
+ (isPerLength (vloat->widget->getStyle()->width) &&
+ vloat->cbAvailWidth != containingBlock->getAvailWidth ())) {
+ // TODO Ugly. Soon to be replaced by cleaner code? See also
+ // comment in Textblock::calcWidgetSize.
+ if (vloat->widget->usesHints ()) {
+ if (isAbsLength (vloat->widget->getStyle()->width))
+ vloat->widget->setWidth
+ (absLengthVal (vloat->widget->getStyle()->width));
+ else if (isPerLength (vloat->widget->getStyle()->width))
+ vloat->widget->setWidth
+ (multiplyWithPerLength (containingBlock->getAvailWidth(),
+ vloat->widget->getStyle()->width));
+ }
+
+ // This is a bit hackish: We first request the size, then set
+ // the available width (also considering the one of the
+ // containing block, and the extremes of the float), then
+ // request the size again, which may of course have a different
+ // result. This is a fix for the bug:
+ //
+ // Text in floats, which are wider because of an image, are
+ // broken at a too narrow width. Reproduce:
+ // test/floats2.html. After the image has been loaded, the
+ // text "Some text in a float." should not be broken
+ // anymore.
+ //
+ // If the call of setWidth not is neccessary, the second call
+ // will read the size from the cache, so no redundant
+ // calculation is necessary.
+
+ // Furthermore, extremes are considered; especially, floats are too
+ // wide, sometimes.
+ Extremes extremes;
+ vloat->widget->getExtremes (&extremes);
+
+ vloat->widget->sizeRequest (&vloat->size);
+
+ // Set width ...
+ int width = vloat->size.width;
+ // Consider the available width of the containing block (when set):
+ if (width > containingBlock->getAvailWidth())
+ width = containingBlock->getAvailWidth();
+ // Finally, consider extremes (as described above).
+ if (width < extremes.minWidth)
+ width = extremes.minWidth;
+ if (width > extremes.maxWidth)
+ width = extremes.maxWidth;
+
+ vloat->widget->setWidth (width);
+ vloat->widget->sizeRequest (&vloat->size);
+
+ //printf (" float %p (%s %p): %d x (%d + %d)\n",
+ // vloat, vloat->widget->getClassName(), vloat->widget,
+ // vloat->size.width, vloat->size.ascent, vloat->size.descent);
+
+ vloat->cbAvailWidth = containingBlock->getAvailWidth ();
+ vloat->dirty = false;
+
+ // "sizeChangedSinceLastAllocation" is reset in sizeAllocateEnd()
+ }
+}
+
+/**
+ * Return the difference between generator and calling textblock. Used
+ * in getBorder() to determine the difference between calling
+ * textblock and float (which position is defined relative to the
+ * generator).
+ */
+int OutOfFlowMgr::getBorderDiff (Textblock *textblock, Float *vloat, Side side)
+{
+ if (textblock == vloat->generatingBlock)
+ return 0;
+ else {
+ assert (wasAllocated (textblock) &&
+ wasAllocated (vloat->generatingBlock));
+
+ switch (side) {
+ case LEFT:
+ return getAllocation(vloat->generatingBlock)->x
+ - getAllocation(textblock)->x;
+
+ case RIGHT:
+ return
+ getAllocation(textblock)->x + getAllocation(textblock)->width
+ - (getAllocation(vloat->generatingBlock)->x +
+ min (getAllocation(vloat->generatingBlock)->width,
+ vloat->generatingBlock->getAvailWidth()));
+
+ default:
+ assertNotReached();
+ return 0;
+ }
+ }
+}
+
+void OutOfFlowMgr::getAbsolutelyPositionedSize (int *oofWidthAbsPos,
+ int *oofHeightAbsPos)
+{
+ *oofWidthAbsPos = *oofHeightAbsPos = 0;
+
+ for (int i = 0; i < absolutelyPositioned->size(); i++) {
+ AbsolutelyPositioned *abspos = absolutelyPositioned->get (i);
+ ensureAbsolutelyPositionedSizeAndPosition (abspos);
+ *oofWidthAbsPos = max (*oofWidthAbsPos, abspos->xCB + abspos->width);
+ *oofHeightAbsPos = max (*oofHeightAbsPos, abspos->yCB + abspos->height);
+ }
+}
+
+void OutOfFlowMgr::ensureAbsolutelyPositionedSizeAndPosition
+ (AbsolutelyPositioned *abspos)
+{
+ // TODO Similar to floats, changes of the available size of the
+ // containing block are not noticed; which is a bit more severe as
+ // for floats. Find a general solution for both floats and
+ // absolutely positioned blocks.
+
+ // TODO Compare to ensureFloatSize: some parts are
+ // missing. Nevertheless, a simpler approach should be focussed on.
+
+ if (abspos->dirty) {
+ Style *style = abspos->widget->getStyle();
+ int availWidth = containingBlock->getAvailWidth();
+ int availHeight =
+ containingBlock->getAvailAscent() + containingBlock->getAvailDescent();
+
+ if (style->left == LENGTH_AUTO)
+ abspos->xCB = 0;
+ else
+ abspos->xCB =
+ calcValueForAbsolutelyPositioned (abspos, style->left, availWidth);
+
+ if (style->top == LENGTH_AUTO)
+ abspos->yCB = 0;
+ else
+ abspos->yCB =
+ calcValueForAbsolutelyPositioned (abspos, style->top, availHeight);
+
+ abspos->width = -1; // undefined
+ if (style->width != LENGTH_AUTO)
+ abspos->width = calcValueForAbsolutelyPositioned (abspos, style->width,
+ availWidth);
+ else if (style->right != LENGTH_AUTO) {
+ int right = calcValueForAbsolutelyPositioned (abspos, style->right,
+ availWidth);
+ abspos->width = max (0, availWidth - (abspos->xCB + right));
+ }
+
+ abspos->height = -1; // undefined
+ if (style->height != LENGTH_AUTO)
+ abspos->height = calcValueForAbsolutelyPositioned (abspos,
+ style->height,
+ availHeight);
+ else if (style->bottom != LENGTH_AUTO) {
+ int bottom = calcValueForAbsolutelyPositioned (abspos, style->bottom,
+ availHeight);
+ abspos->height = max (0, availHeight - (abspos->yCB + bottom));
+ }
+
+ if (abspos->width != -1)
+ abspos->widget->setWidth (abspos->width);
+
+ if (abspos->height != -1) {
+ abspos->widget->setAscent (abspos->height);
+ abspos->widget->setDescent (0); // TODO
+ }
+
+ if (abspos->width == -1 || abspos->height == -1) {
+ Requisition req;
+ abspos->widget->sizeRequest (&req);
+
+ if (abspos->width == -1)
+ abspos->width = req.width;
+
+ if (abspos->height == -1)
+ abspos->height = req.ascent + req.descent;
+ }
+
+ abspos->dirty = false;
+ }
+}
+
+int OutOfFlowMgr::calcValueForAbsolutelyPositioned
+ (AbsolutelyPositioned *abspos, Length styleLen, int refLen)
+{
+ assert (styleLen != LENGTH_AUTO);
+ if (isAbsLength (styleLen))
+ return absLengthVal (styleLen);
+ else if (isPerLength (styleLen))
+ return multiplyWithPerLength (refLen, styleLen);
+ else {
+ assertNotReached ();
+ return 0; // compiler happiness
+ }
+}
+
+void OutOfFlowMgr::sizeAllocateAbsolutelyPositioned ()
+{
+ for (int i = 0; i < absolutelyPositioned->size(); i++) {
+ Allocation *cbAllocation = getAllocation(containingBlock);
+ AbsolutelyPositioned *abspos = absolutelyPositioned->get (i);
+ ensureAbsolutelyPositionedSizeAndPosition (abspos);
+
+ Allocation childAllocation;
+ childAllocation.x = cbAllocation->x + abspos->xCB;
+ childAllocation.y = cbAllocation->y + abspos->yCB;
+ childAllocation.width = abspos->width;
+ childAllocation.ascent = abspos->height;
+ childAllocation.descent = 0; // TODO
+
+ abspos->widget->sizeAllocate (&childAllocation);
+
+ printf ("[%p] allocating child %p at: (%d, %d), %d x (%d + %d)\n",
+ containingBlock, abspos->widget, childAllocation.x,
+ childAllocation.y, childAllocation.width, childAllocation.ascent,
+ childAllocation.descent);
+ }
+}
+
+// TODO Latest change: Check also Textblock::borderChanged: looks OK,
+// but the comment ("... (i) with canvas coordinates ...") looks wrong
+// (and looks as having always been wrong).
+
+// Another issue: does it make sense to call Textblock::borderChanged
+// for generators, when the respective widgets have not been called
+// yet?
+
+} // namespace dw
diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh
new file mode 100644
index 00000000..71599d14
--- /dev/null
+++ b/dw/outofflowmgr.hh
@@ -0,0 +1,380 @@
+#ifndef __DW_OUTOFFLOWMGR_HH__
+#define __DW_OUTOFFLOWMGR_HH__
+
+#include "core.hh"
+
+namespace dw {
+
+class Textblock;
+
+/**
+ * \brief Represents additional data for containing blocks.
+ */
+class OutOfFlowMgr
+{
+private:
+ enum Side { LEFT, RIGHT };
+
+ Textblock *containingBlock;
+
+ // These two values are set by sizeAllocateStart(), and they are
+ // accessable also within sizeAllocateEnd(), and also for the
+ // containing block, for which allocation and WAS_ALLOCATED is set
+ // *after* sizeAllocateEnd(). See the two inline functions below
+ // for usage.
+ core::Allocation containingBlockAllocation;
+ bool containingBlockWasAllocated;
+
+ class Float: public lout::object::Comparable
+ {
+ private:
+ OutOfFlowMgr *oofm;
+
+ public:
+ class CompareSideSpanningIndex: public lout::object::Comparator
+ {
+ int compare(Object *o1, Object *o2);
+ };
+
+ class CompareGBAndExtIndex: public lout::object::Comparator
+ {
+ private:
+ OutOfFlowMgr *oofm;
+
+ public:
+ CompareGBAndExtIndex (OutOfFlowMgr *oofm) { this->oofm = oofm; }
+ int compare(Object *o1, Object *o2);
+ };
+
+ core::Widget *widget;
+ Textblock *generatingBlock;
+ int externalIndex;
+ int yReq, yReal; // relative to generator, not container
+ int index; /* When GB is not yet allocated: position
+ within TBInfo::leftFloatsGB or
+ TBInfo::rightFloatsGB, respectively. When GB
+ is allocated: position within leftFloatsCB
+ or rightFloatsCB, respectively, even when
+ the floats are still elements of
+ TBInfo::*FloatsGB. */
+ int sideSpanningIndex, mark;
+ core::Requisition size;
+ int cbAvailWidth; /* On which the calculation of relative sizes
+ is based. Height not yet used, and probably
+ not added before size redesign. */
+ bool dirty, sizeChangedSinceLastAllocation;
+ bool inCBList; /* Neccessary to prevent floats from being moved
+ twice from GB to CB list. */
+
+ Float (OutOfFlowMgr *oofm, core::Widget *widget,
+ Textblock *generatingBlock, int externalIndex);
+
+ void intoStringBuffer(lout::misc::StringBuffer *sb);
+ int compareTo(Comparable *other);
+
+ int yForTextblock (Textblock *textblock, int y);
+ inline int yForTextblock (Textblock *textblock)
+ { return yForTextblock (textblock, yReal); }
+ int yForContainer (int y);
+ inline int yForContainer () { return yForContainer (yReal); }
+ bool covers (Textblock *textblock, int y, int h);
+ };
+
+ /**
+ * This list is kept sorted.
+ *
+ * To prevent accessing methods of the base class in an
+ * uncontrolled way, the inheritance is private, not public; this
+ * means that all methods must be delegated (see iterator(), size()
+ * etc. below.)
+ *
+ * TODO Update comment: still sorted, but ...
+ *
+ * More: add() and change() may check order again.
+ */
+ class SortedFloatsVector: private lout::container::typed::Vector<Float>
+ {
+ public:
+ enum Type { GB, CB } type;
+
+ private:
+ OutOfFlowMgr *oofm;
+ Side side;
+
+ public:
+ inline SortedFloatsVector (OutOfFlowMgr *oofm, Side side, Type type) :
+ lout::container::typed::Vector<Float> (1, false)
+ { this->oofm = oofm; this->side = side; this->type = type; }
+
+ int findFloatIndex (Textblock *lastGB, int lastExtIndex);
+ int find (Textblock *textblock, int y, int start, int end);
+ int findFirst (Textblock *textblock, int y, int h, Textblock *lastGB,
+ int lastExtIndex);
+ int findLastBeforeSideSpanningIndex (int sideSpanningIndex);
+ void put (Float *vloat);
+
+ inline lout::container::typed::Iterator<Float> iterator()
+ { return lout::container::typed::Vector<Float>::iterator (); }
+ inline int size ()
+ { return lout::container::typed::Vector<Float>::size (); }
+ inline Float *get (int pos)
+ { return lout::container::typed::Vector<Float>::get (pos); }
+ inline void clear ()
+ { lout::container::typed::Vector<Float>::clear (); }
+ };
+
+ class TBInfo: public lout::object::Object
+ {
+ public:
+ bool wasAllocated;
+ int xCB, yCB; // relative to the containing block
+ int width, height;
+ int index; // position within "tbInfos"
+ Textblock *textblock; // for debugging; may be removed again
+
+ TBInfo *parent;
+ int parentExtIndex;
+
+ // These two lists store all floats generated by this textblock,
+ // as long as this textblock is not allocates.
+ SortedFloatsVector *leftFloatsGB, *rightFloatsGB;
+
+ TBInfo (OutOfFlowMgr *oofm, Textblock *textblock,
+ TBInfo *parent, int parentExtIndex);
+ ~TBInfo ();
+ };
+
+ class AbsolutelyPositioned: public lout::object::Object
+ {
+ public:
+ core::Widget *widget;
+ int xCB, yCB; // relative to the containing block
+ int width, height;
+ bool dirty;
+
+ AbsolutelyPositioned (OutOfFlowMgr *oofm, core::Widget *widget,
+ Textblock *generatingBlock, int externalIndex);
+ };
+
+ // These two lists store all floats, in the order in which they are
+ // defined. Only used for iterators.
+ lout::container::typed::Vector<Float> *leftFloatsAll, *rightFloatsAll;
+
+ // These two lists store all floats whose generators are already
+ // allocated.
+ SortedFloatsVector *leftFloatsCB, *rightFloatsCB;
+
+ lout::container::typed::HashTable<lout::object::TypedPointer
+ <dw::core::Widget>, Float> *floatsByWidget;
+
+ lout::container::typed::Vector<TBInfo> *tbInfos;
+ lout::container::typed::HashTable<lout::object::TypedPointer <Textblock>,
+ TBInfo> *tbInfosByTextblock;
+
+ lout::container::typed::Vector<AbsolutelyPositioned> *absolutelyPositioned;
+
+ int lastLeftTBIndex, lastRightTBIndex, leftFloatsMark, rightFloatsMark;
+
+ /**
+ * Variant of Widget::wasAllocated(), which can also be used within
+ * OOFM::sizeAllocateEnd(), and also for the generating block.
+ */
+ inline bool wasAllocated (core::Widget *widget) {
+ return widget->wasAllocated () ||
+ (widget == (core::Widget*)containingBlock &&
+ containingBlockWasAllocated); }
+
+ /**
+ * Variant of Widget::getAllocation(), which can also be used
+ * within OOFM::sizeAllocateEnd(), and also for the generating
+ * block.
+ */
+ inline core::Allocation *getAllocation (core::Widget *widget) {
+ return widget == (core::Widget*)containingBlock ?
+ &containingBlockAllocation : widget->getAllocation (); }
+
+ void moveExternalIndices (SortedFloatsVector *list, int oldStartIndex,
+ int diff);
+ Float *findFloatByWidget (core::Widget *widget);
+
+ void moveFromGBToCB (Side side);
+ void sizeAllocateFloats (Side side);
+ bool isTextblockCoveredByFloats (Textblock *tb, int tbx, int tby,
+ int tbWidth, int tbHeight, int *minFloatPos,
+ core::Widget **minFloat);
+ bool isTextblockCoveredByFloats (SortedFloatsVector *list, Textblock *tb,
+ int tbx, int tby, int tbWidth, int tbHeight,
+ int *minFloatPos, core::Widget **minFloat);
+ bool isTextblockCoveredByFloat (Float *vloat, Textblock *tb,
+ int tbx, int tby, int tbWidth, int tbHeight,
+ int *floatPos);
+ void checkChangedFloatSizes ();
+ void checkChangedFloatSizes (SortedFloatsVector *list);
+
+ void drawFloats (SortedFloatsVector *list, core::View *view,
+ core::Rectangle *area);
+ void drawAbsolutelyPositioned (core::View *view, core::Rectangle *area);
+ core::Widget *getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y,
+ int level);
+ core::Widget *getAbsolutelyPositionedWidgetAtPoint (int x, int y, int level);
+
+ bool collides (Float *vloat, Float *other, int *yReal);
+ void checkCoveragePosChanged (Float *vloat, int oldY);
+
+ void getFloatsLists (Float *vloat, SortedFloatsVector **listSame,
+ SortedFloatsVector **listOpp);
+
+ void getFloatsSize (SortedFloatsVector *list, Side side, int *width,
+ int *height);
+ void accumExtremes (SortedFloatsVector *list, Side side, int *oofMinWidth,
+ int *oofMaxWidth);
+ int getMinBorderDiff (Float *vloat, Side side);
+ int getMaxBorderDiff (Float *vloat, Side side);
+
+ TBInfo *getTextblock (Textblock *textblock);
+ int getBorder (Textblock *textblock, Side side, int y, int h,
+ Textblock *lastGB, int lastExtIndex);
+ SortedFloatsVector *getFloatsListForTextblock (Textblock *textblock,
+ Side side);
+ bool hasFloat (Textblock *textblock, Side side, int y, int h,
+ Textblock *lastGB, int lastExtIndex);
+
+ int getClearPosition (Textblock *tb, Side side);
+
+ void ensureFloatSize (Float *vloat);
+ int getBorderDiff (Textblock *textblock, Float *vloat, Side side);
+
+ void tellFloatPosition (core::Widget *widget, int yReq);
+
+ void getAbsolutelyPositionedSize (int *oofWidthAbsPos, int *oofHeightAbsPos);
+ void ensureAbsolutelyPositionedSizeAndPosition (AbsolutelyPositioned
+ *abspos);
+ int calcValueForAbsolutelyPositioned (AbsolutelyPositioned *abspos,
+ core::style::Length styleLen,
+ int refLen);
+ void sizeAllocateAbsolutelyPositioned ();
+
+ static inline bool isWidgetFloat (core::Widget *widget)
+ { return widget->getStyle()->vloat != core::style::FLOAT_NONE; }
+ static inline bool isWidgetAbsolutelyPositioned (core::Widget *widget)
+ { return widget->getStyle()->position == core::style::POSITION_ABSOLUTE; }
+
+ /*
+ * Format for parent ref (see also below for isRefOutOfFlow,
+ * createRefNormalFlow, and getLineNoFromRef.
+ *
+ * Widget in flow:
+ *
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ * | line number | 0 |
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ *
+ * So, anything with the least signifant bit set to 1 is out of flow.
+ *
+ * Floats:
+ *
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ * | left float index | 0 | 0 | 1 |
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ *
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ * | right float index | 1 | 0 | 1 |
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ *
+ * Absolutely positioned blocks:
+ *
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ * | index | 1 | 1 |
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ */
+
+ inline static bool isRefFloat (int ref)
+ { return ref != -1 && (ref & 3) == 1; }
+ inline static bool isRefLeftFloat (int ref)
+ { return ref != -1 && (ref & 7) == 1; }
+ inline static bool isRefRightFloat (int ref)
+ { return ref != -1 && (ref & 7) == 5; }
+ inline static bool isRefAbsolutelyPositioned (int ref)
+ { return ref != -1 && (ref & 3) == 3; }
+
+ inline static int createRefLeftFloat (int index)
+ { return (index << 3) | 1; }
+ inline static int createRefRightFloat (int index)
+ { return (index << 3) | 5; }
+ inline static int createRefAbsolutelyPositioned (int index)
+ { return (index << 2) | 3; }
+
+ inline static int getFloatIndexFromRef (int ref)
+ { return ref == -1 ? ref : (ref >> 3); }
+ inline static int getAbsolutelyPositionedIndexFromRef (int ref)
+ { return ref == -1 ? ref : (ref >> 2); }
+
+public:
+ OutOfFlowMgr (Textblock *containingBlock);
+ ~OutOfFlowMgr ();
+
+ void sizeAllocateStart (core::Allocation *containingBlockAllocation);
+ void sizeAllocateEnd ();
+ void draw (core::View *view, core::Rectangle *area);
+
+ void markSizeChange (int ref);
+ void markExtremesChange (int ref);
+ core::Widget *getWidgetAtPoint (int x, int y, int level);
+
+ static bool isWidgetOutOfFlow (core::Widget *widget);
+ static bool isWidgetHandledByOOFM (core::Widget *widget);
+ void addWidgetInFlow (Textblock *textblock, Textblock *parentBlock,
+ int externalIndex);
+ void addWidgetOOF (core::Widget *widget, Textblock *generatingBlock,
+ int externalIndex);
+ void moveExternalIndices (Textblock *generatingBlock, int oldStartIndex,
+ int diff);
+
+ void tellPosition (core::Widget *widget, int yReq);
+
+ void getSize (int cbWidth, int cbHeight, int *oofWidth, int *oofHeight);
+ void getExtremes (int cbMinWidth, int cbMaxWidth, int *oofMinWidth,
+ int *oofMaxWidth);
+
+ int getLeftBorder (Textblock *textblock, int y, int h, Textblock *lastGB,
+ int lastExtIndex);
+ int getRightBorder (Textblock *textblock, int y, int h, Textblock *lastGB,
+ int lastExtIndex);
+
+ bool hasFloatLeft (Textblock *textblock, int y, int h, Textblock *lastGB,
+ int lastExtIndex);
+ bool hasFloatRight (Textblock *textblock, int y, int h, Textblock *lastGB,
+ int lastExtIndex);
+
+ int getClearPosition (Textblock *tb);
+
+ inline static bool isRefOutOfFlow (int ref)
+ { return ref != -1 && (ref & 1) != 0; }
+ inline static int createRefNormalFlow (int lineNo) { return lineNo << 1; }
+ inline static int getLineNoFromRef (int ref)
+ { return ref == -1 ? ref : (ref >> 1); }
+
+ // for iterators
+ inline int getNumWidgets () {
+ return leftFloatsAll->size() + rightFloatsAll->size() +
+ absolutelyPositioned->size(); }
+
+ inline core::Widget *getWidget (int i) {
+ if (i < leftFloatsAll->size())
+ return leftFloatsAll->get(i)->widget;
+ else if (i < leftFloatsAll->size() + rightFloatsAll->size())
+ return rightFloatsAll->get(i - leftFloatsAll->size())->widget;
+ else
+ return absolutelyPositioned->get(i - (leftFloatsAll->size() +
+ rightFloatsAll->size()))->widget;
+ }
+
+ inline bool affectsLeftBorder (core::Widget *widget) {
+ return widget->getStyle()->vloat == core::style::FLOAT_LEFT; }
+ inline bool affectsRightBorder (core::Widget *widget) {
+ return widget->getStyle()->vloat == core::style::FLOAT_RIGHT; }
+};
+
+} // namespace dw
+
+#endif // __DW_OUTOFFLOWMGR_HH__
diff --git a/dw/ruler.cc b/dw/ruler.cc
index 115dfaa5..2b5288c2 100644
--- a/dw/ruler.cc
+++ b/dw/ruler.cc
@@ -28,17 +28,28 @@ namespace dw {
Ruler::Ruler ()
{
+ setFlags (USES_HINTS);
setFlags (BLOCK_LEVEL);
unsetFlags (HAS_CONTENTS);
+ availWidth = 0;
}
void Ruler::sizeRequestImpl (core::Requisition *requisition)
{
- requisition->width = getStyle()->boxDiffWidth ();
+ requisition->width =
+ lout::misc::max (availWidth, getStyle()->boxDiffWidth ());
requisition->ascent = getStyle()->boxOffsetY ();
requisition->descent = getStyle()->boxRestHeight ();
}
+void Ruler::setWidth (int width)
+{
+ if (availWidth != width) {
+ availWidth = width;
+ queueResize (0, false);
+ }
+}
+
void Ruler::draw (core::View *view, core::Rectangle *area)
{
drawWidgetBox (view, area, false);
diff --git a/dw/ruler.hh b/dw/ruler.hh
index 32e859a1..863792dd 100644
--- a/dw/ruler.hh
+++ b/dw/ruler.hh
@@ -15,8 +15,12 @@ namespace dw {
*/
class Ruler: public core::Widget
{
+private:
+ int availWidth;
+
protected:
void sizeRequestImpl (core::Requisition *requisition);
+ void setWidth (int width);
void draw (core::View *view, core::Rectangle *area);
public:
diff --git a/dw/style.cc b/dw/style.cc
index 4ab5673a..3f39a39e 100644
--- a/dw/style.cc
+++ b/dw/style.cc
@@ -17,8 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-
#include <stdio.h>
#include <string.h>
#include <unistd.h>
@@ -75,6 +73,10 @@ void StyleAttrs::initValues ()
backgroundPositionX = createPerLength (0);
backgroundPositionY = createPerLength (0);
width = height = lineHeight = LENGTH_AUTO;
+ vloat = FLOAT_NONE;
+ clear = CLEAR_NONE;
+ position = POSITION_STATIC;
+ top = bottom = left = right = LENGTH_AUTO;
textIndent = 0;
margin.setVal (0);
borderWidth.setVal (0);
@@ -101,6 +103,11 @@ void StyleAttrs::resetValues ()
valign = VALIGN_BASELINE;
textAlignChar = '.';
+ vloat = FLOAT_NONE; /** \todo Correct? Check specification. */
+ clear = CLEAR_NONE; /** \todo Correct? Check specification. */
+ position = POSITION_STATIC; /** \todo Correct? Check specification. */
+ top = bottom = left = right = LENGTH_AUTO; /** \todo Correct? Check
+ specification. */
backgroundColor = NULL;
backgroundImage = NULL;
backgroundRepeat = BACKGROUND_REPEAT;
@@ -156,6 +163,13 @@ bool StyleAttrs::equals (object::Object *other) {
valign == otherAttrs->valign &&
textAlignChar == otherAttrs->textAlignChar &&
textTransform == otherAttrs->textTransform &&
+ vloat == otherAttrs->vloat &&
+ clear == otherAttrs->clear &&
+ position == otherAttrs->position &&
+ top == otherAttrs->top &&
+ bottom == otherAttrs->bottom &&
+ left == otherAttrs->left &&
+ right == otherAttrs->right &&
hBorderSpacing == otherAttrs->hBorderSpacing &&
vBorderSpacing == otherAttrs->vBorderSpacing &&
wordSpacing == otherAttrs->wordSpacing &&
@@ -201,6 +215,13 @@ int StyleAttrs::hashValue () {
valign +
textAlignChar +
textTransform +
+ vloat +
+ clear +
+ position +
+ top +
+ bottom +
+ left +
+ right +
hBorderSpacing +
vBorderSpacing +
wordSpacing +
@@ -302,6 +323,13 @@ void Style::copyAttrs (StyleAttrs *attrs)
valign = attrs->valign;
textAlignChar = attrs->textAlignChar;
textTransform = attrs->textTransform;
+ vloat = attrs->vloat;
+ clear = attrs->clear;
+ position = attrs->position;
+ top = attrs->top;
+ bottom = attrs->bottom;
+ left = attrs->left;
+ right = attrs->right;
hBorderSpacing = attrs->hBorderSpacing;
vBorderSpacing = attrs->vBorderSpacing;
wordSpacing = attrs->wordSpacing;
diff --git a/dw/style.hh b/dw/style.hh
index 089cf59b..3fe3b7c9 100644
--- a/dw/style.hh
+++ b/dw/style.hh
@@ -295,7 +295,6 @@ enum ListStylePosition {
LIST_STYLE_POSITION_INSIDE,
LIST_STYLE_POSITION_OUTSIDE
};
-
enum ListStyleType {
LIST_STYLE_TYPE_DISC,
LIST_STYLE_TYPE_CIRCLE,
@@ -331,6 +330,13 @@ enum FontVariant {
FONT_VARIANT_SMALL_CAPS
};
+enum Position {
+ POSITION_STATIC,
+ POSITION_RELATIVE,
+ POSITION_ABSOLUTE,
+ POSITION_FIXED,
+};
+
enum TextDecoration {
TEXT_DECORATION_NONE = 0,
TEXT_DECORATION_UNDERLINE = 1 << 0,
@@ -347,6 +353,19 @@ enum WhiteSpace {
WHITE_SPACE_PRE_LINE,
};
+enum FloatType {
+ FLOAT_NONE,
+ FLOAT_LEFT,
+ FLOAT_RIGHT
+};
+
+enum ClearType {
+ CLEAR_LEFT,
+ CLEAR_RIGHT,
+ CLEAR_BOTH,
+ CLEAR_NONE
+};
+
/**
* \brief Type for representing all lengths within dw::core::style.
*
@@ -502,6 +521,12 @@ public:
VAlignType valign;
char textAlignChar; /* In future, strings will be supported. */
TextTransform textTransform;
+
+ FloatType vloat; /* "float" is a keyword. */
+ ClearType clear;
+
+ Position position;
+ Length top, bottom, left, right;
int hBorderSpacing, vBorderSpacing, wordSpacing;
Length width, height, lineHeight, textIndent;
diff --git a/dw/table.cc b/dw/table.cc
index 8db9ef2a..eef1b6ce 100644
--- a/dw/table.cc
+++ b/dw/table.cc
@@ -480,7 +480,7 @@ void Table::reallocChildren (int newNumCols, int newNumRows)
void Table::calcCellSizes ()
{
- if (needsResize ())
+ if (needsResize () || resizeQueued ())
forceCalcCellSizes ();
}
@@ -638,7 +638,7 @@ void Table::apportionRowSpan ()
*/
void Table::calcColumnExtremes ()
{
- if (extremesChanged ())
+ if (extremesChanged () || extremesQueued ())
forceCalcColumnExtremes ();
}
@@ -1128,7 +1128,7 @@ Table::TableIterator::TableIterator (Table *table,
else if (index >= table->children->size ())
content.type = core::Content::END;
else {
- content.type = core::Content::WIDGET;
+ content.type = core::Content::WIDGET_IN_FLOW;
content.widget = table->children->get(index)->cell.widget;
}
}
@@ -1150,8 +1150,8 @@ bool Table::TableIterator::next ()
if (content.type == core::Content::END)
return false;
- // tables only contain widgets:
- if ((getMask() & core::Content::WIDGET) == 0) {
+ // tables only contain widgets (in flow):
+ if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) {
content.type = core::Content::END;
return false;
}
@@ -1165,7 +1165,7 @@ bool Table::TableIterator::next ()
} while (table->children->get(index) == NULL ||
table->children->get(index)->type != Child::CELL);
- content.type = core::Content::WIDGET;
+ content.type = core::Content::WIDGET_IN_FLOW;
content.widget = table->children->get(index)->cell.widget;
return true;
}
@@ -1177,8 +1177,8 @@ bool Table::TableIterator::prev ()
if (content.type == core::Content::START)
return false;
- // tables only contain widgets:
- if ((getMask() & core::Content::WIDGET) == 0) {
+ // tables only contain widgets (in flow):
+ if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) {
content.type = core::Content::START;
return false;
}
@@ -1192,7 +1192,7 @@ bool Table::TableIterator::prev ()
} while (table->children->get(index) == NULL ||
table->children->get(index)->type != Child::CELL);
- content.type = core::Content::WIDGET;
+ content.type = core::Content::WIDGET_IN_FLOW;
content.widget = table->children->get(index)->cell.widget;
return true;
}
diff --git a/dw/table.hh b/dw/table.hh
index 1d14ec07..6966a163 100644
--- a/dw/table.hh
+++ b/dw/table.hh
@@ -71,7 +71,7 @@ namespace dw {
* is the case.
*
* [C] Whether this function is called, depends on NEEDS_RESIZE /
- * EXTREMES_CHANGED.
+ * RESIZE_QUEUED / EXTREMES_CHANGED / EXTREMES_QUEUED.
*
*
* <h4>Apportionment</h4>
diff --git a/dw/tablecell.cc b/dw/tablecell.cc
index 5a005e3d..edad1524 100644
--- a/dw/tablecell.cc
+++ b/dw/tablecell.cc
@@ -106,7 +106,7 @@ int TableCell::getValue ()
void TableCell::setMaxValue (int maxValue, int value)
{
line1Offset = maxValue - value;
- queueResize (0, true);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), true);
}
} // namespace dw
diff --git a/dw/textblock.cc b/dw/textblock.cc
index 8623a574..c0a902f4 100644
--- a/dw/textblock.cc
+++ b/dw/textblock.cc
@@ -25,13 +25,14 @@
#include "../lout/debug.hh"
#include <stdio.h>
-#include <math.h>
+#include <math.h> // remove again?
+#include <limits.h>
/*
* Local variables
*/
- /* The tooltip under mouse pointer in current textblock. No ref. hold.
- * (having one per view looks not worth the extra clutter). */
+/* The tooltip under mouse pointer in current textblock. No ref. hold.
+ * (having one per view looks not worth the extra clutter). */
static dw::core::style::Tooltip *hoverTooltip = NULL;
@@ -228,6 +229,7 @@ Textblock::Textblock (bool limitTextWidth)
setFlags (USES_HINTS);
setButtonSensitive(true);
+ containingBlock = NULL;
hasListitemValue = false;
innerPadding = 0;
line1Offset = 0;
@@ -250,6 +252,7 @@ Textblock::Textblock (bool limitTextWidth)
nonTemporaryLines = 0;
words = new misc::NotSoSimpleVector <Word> (1);
anchors = new misc::SimpleVector <Anchor> (1);
+ outOfFlowMgr = NULL;
//DBG_OBJ_SET_NUM(this, "num_lines", num_lines);
@@ -267,6 +270,8 @@ Textblock::Textblock (bool limitTextWidth)
availAscent = 100;
availDescent = 0;
+ verticalOffset = 0;
+
this->limitTextWidth = limitTextWidth;
for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
@@ -276,6 +281,8 @@ Textblock::Textblock (bool limitTextWidth)
hlEnd[layer].index = 0;
hlEnd[layer].nChar = 0;
}
+
+ initNewLine ();
}
Textblock::~Textblock ()
@@ -288,8 +295,9 @@ Textblock::~Textblock ()
for (int i = 0; i < words->size(); i++) {
Word *word = words->getRef (i);
- if (word->content.type == core::Content::WIDGET)
+ if (word->content.type == core::Content::WIDGET_IN_FLOW)
delete word->content.widget;
+ /** \todo Widget references? What about texts? */
removeWordImgRenderer (i);
removeSpaceImgRenderer (i);
@@ -309,6 +317,9 @@ Textblock::~Textblock ()
delete words;
delete anchors;
+ if(outOfFlowMgr)
+ delete outOfFlowMgr;
+
/* Make sure we don't own widgets anymore. Necessary before call of
parent class destructor. (???) */
words = NULL;
@@ -360,9 +371,24 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
this, innerPadding, getStyle()->boxDiffWidth ());
requisition->width += innerPadding + getStyle()->boxDiffWidth ();
- requisition->ascent += getStyle()->boxOffsetY ();
+ requisition->ascent += verticalOffset + getStyle()->boxOffsetY ();
requisition->descent += getStyle()->boxRestHeight ();
+ // Dealing with parts out of flow, which may overlap the borders of
+ // the text block. Base lines are ignored here: they do not play a
+ // role (currently) and caring about them (for the future) would
+ // cause too much problems.
+
+ if (outOfFlowMgr) {
+ int oofWidth, oofHeight;
+ outOfFlowMgr->getSize (requisition->width,
+ requisition->ascent + requisition->descent,
+ &oofWidth, &oofHeight);
+ requisition->width = misc::max (requisition->width, oofWidth);
+ if (oofHeight > requisition->ascent + requisition->descent)
+ requisition->descent = oofHeight - requisition->ascent;
+ }
+
if (requisition->width < availWidth)
requisition->width = availWidth;
@@ -375,7 +401,7 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
*/
void Textblock::getWordExtremes (Word *word, core::Extremes *extremes)
{
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
if (word->content.widget->usesHints ())
word->content.widget->getExtremes (extremes);
else {
@@ -423,6 +449,19 @@ void Textblock::getExtremesImpl (core::Extremes *extremes)
extremes->minWidth += diff;
extremes->maxWidth += diff;
+ if (outOfFlowMgr) {
+ int oofMinWidth, oofMaxWidth;
+ outOfFlowMgr->getExtremes (extremes->minWidth, extremes->maxWidth,
+ &oofMinWidth, &oofMaxWidth);
+
+ //printf ("[%p] extremes: %d / %d, corrected: %d / %d\n",
+ // this, extremes->minWidth, extremes->maxWidth,
+ // oofMinWidth, oofMaxWidth);
+
+ extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth);
+ extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth);
+ }
+
PRINTF ("[%p] GET_EXTREMES => %d / %d\n",
this, extremes->minWidth, extremes->maxWidth);
}
@@ -436,6 +475,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
showMissingLines ();
+ if(outOfFlowMgr)
+ outOfFlowMgr->sizeAllocateStart (allocation);
+
int lineIndex, wordIndex;
Line *line;
Word *word;
@@ -449,7 +491,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) {
line = lines->getRef (lineIndex);
- xCursor = lineXOffsetWidget (line);
+ xCursor = line->offsetCompleteWidget;
for (wordIndex = line->firstWord; wordIndex <= line->lastWord;
wordIndex++) {
@@ -459,7 +501,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
redrawY = misc::min (redrawY, lineYOffsetWidget (line));
}
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
/** \todo Justification within the line is done here. */
childAllocation.x = xCursor + allocation->x;
/* align=top:
@@ -473,6 +515,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
lineYOffsetCanvasAllocation (line, allocation)
+ (line->boxAscent - word->size.ascent)
- word->content.widget->getStyle()->margin.top;
+
childAllocation.width = word->size.width;
childAllocation.ascent = word->size.ascent
+ word->content.widget->getStyle()->margin.top;
@@ -531,11 +574,17 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
}
}
+ if(outOfFlowMgr)
+ outOfFlowMgr->sizeAllocateEnd ();
+
for (int i = 0; i < anchors->size(); i++) {
Anchor *anchor = anchors->getRef(i);
int y;
- if (anchor->wordIndex >= words->size()) {
+ if (anchor->wordIndex >= words->size() ||
+ // Also regard not-yet-existing lines.
+ lines->size () <= 0 ||
+ anchor->wordIndex > lines->getLastRef()->lastWord) {
y = allocation->y + allocation->ascent + allocation->descent;
} else {
Line *line = lines->getRef(findLineOfWord (anchor->wordIndex));
@@ -561,47 +610,114 @@ void Textblock::resizeDrawImpl ()
void Textblock::markSizeChange (int ref)
{
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markSizeChange (ref);
+ } else {
+ PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n",
+ this, ref, wrapRefLines);
+
+ /* By the way: ref == -1 may have two different causes: (i) flush()
+ calls "queueResize (-1, true)", when no rewrapping is necessary;
+ and (ii) a word may have parentRef == -1 , when it is not yet
+ added to a line. In the latter case, nothing has to be done
+ now, but addLine(...) will do everything necessary. */
+ if (ref != -1) {
+ if (wrapRefLines == -1)
+ wrapRefLines = OutOfFlowMgr::getLineNoFromRef (ref);
+ else
+ wrapRefLines = misc::min (wrapRefLines,
+ OutOfFlowMgr::getLineNoFromRef (ref));
+ }
+
+ PRINTF (" ... => %d\n", wrapRefLine);
+
+ // It seems that sometimes (even without floats) the lines
+ // structure is changed, so that wrapRefLines may refers to a
+ // line which does not exist anymore. Should be examined
+ // again. Until then, setting wrapRefLines to the same value is
+ // a workaround.
+ markExtremesChange (ref);
+ }
+}
+
+void Textblock::markExtremesChange (int ref)
+{
PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", this, ref, wrapRefLines);
- /* By the way: ref == -1 may have two different causes: (i) flush()
- calls "queueResize (-1, true)", when no rewrapping is necessary;
- and (ii) a word may have parentRef == -1 , when it is not yet
- added to a line. In the latter case, nothing has to be done
- now, but addLine(...) will do everything necessary. */
- if (ref != -1) {
- if (wrapRefLines == -1)
- wrapRefLines = ref;
- else
- wrapRefLines = misc::min (wrapRefLines, ref);
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markExtremesChange (ref);
+ } else {
+ PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n",
+ this, ref, wrapRefParagraphs);
+
+ /* By the way: ref == -1 may have two different causes: (i) flush()
+ calls "queueResize (-1, true)", when no rewrapping is necessary;
+ and (ii) a word may have parentRef == -1 , when it is not yet
+ added to a line. In the latter case, nothing has to be done
+ now, but addLine(...) will do everything necessary. */
+ if (ref != -1) {
+ if (wrapRefParagraphs == -1)
+ wrapRefParagraphs = OutOfFlowMgr::getLineNoFromRef (ref);
+ else
+ wrapRefParagraphs =
+ misc::min (wrapRefParagraphs,
+ OutOfFlowMgr::getLineNoFromRef (ref));
+ }
+
+ PRINTF (" ... => %d\n", wrapRefParagraphs);
}
+}
- PRINTF (" ... => %d\n", wrapRefLine);
+void Textblock::notifySetAsTopLevel()
+{
+ PRINTF ("%p becomes toplevel\n", this);
+ containingBlock = this;
+ PRINTF ("-> %p is its own containing block\n", this);
+}
- // It seems that sometimes the lines structure is changed, so that
- // wrapRefLines may refers to a line which does not exist
- // anymore. Should be examined again. Until then, setting
- // wrapRefLines to the same value is a workaround.
- markExtremesChange (ref);
+bool Textblock::isContainingBlock (Widget *widget)
+{
+ return
+ // Of course, only textblocks are considered as containing
+ // blocks.
+ widget->instanceOf (Textblock::CLASS_ID) &&
+ // The second condition: that this block is "out of flow", in a
+ // wider sense.
+ (// The toplevel widget is "out of flow", since there is no
+ // parent, and so no context.
+ widget->getParent() == NULL ||
+ // A similar reasoning applies to a widget with another parent
+ // than a textblock (typical example: a table cell (this is
+ // also a text block) within a table widget).
+ !widget->getParent()->instanceOf (Textblock::CLASS_ID) ||
+ // Finally, "out of flow" in a narrower sense: floats and
+ // absolute positions.
+ OutOfFlowMgr::isWidgetOutOfFlow (widget));
}
-void Textblock::markExtremesChange (int ref)
+void Textblock::notifySetParent ()
{
- PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n",
- this, ref, wrapRefParagraphs);
-
- /* By the way: ref == -1 may have two different causes: (i) flush()
- calls "queueResize (-1, true)", when no rewrapping is necessary;
- and (ii) a word may have parentRef == -1 , when it is not yet
- added to a line. In the latter case, nothing has to be done
- now, but addLine(...) will do everything necessary. */
- if (ref != -1) {
- if (wrapRefParagraphs == -1)
- wrapRefParagraphs = ref;
- else
- wrapRefParagraphs = misc::min (wrapRefParagraphs, ref);
- }
+ PRINTF ("%p becomes a child of %p\n", this, getParent());
+
+ // Search for containing Box.
+ containingBlock = NULL;
- PRINTF (" ... => %d\n", wrapRefParagraphs);
+ for (Widget *widget = this; widget != NULL && containingBlock == NULL;
+ widget = widget->getParent())
+ if (isContainingBlock (widget)) {
+ containingBlock = (Textblock*)widget;
+
+ if (containingBlock == this) {
+ PRINTF ("-> %p is its own containing block\n", this);
+ } else {
+ PRINTF ("-> %p becomes containing block of %p\n",
+ containingBlock, this);
+ }
+ }
+
+ assert (containingBlock != NULL);
}
void Textblock::setWidth (int width)
@@ -615,7 +731,7 @@ void Textblock::setWidth (int width)
// words->size());
availWidth = width;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
redrawY = 0;
}
@@ -630,7 +746,7 @@ void Textblock::setAscent (int ascent)
// words->size());
availAscent = ascent;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
}
}
@@ -644,7 +760,7 @@ void Textblock::setDescent (int descent)
// words->size());
availDescent = descent;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
}
}
@@ -753,7 +869,8 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
wordIndex = words->size () - 1;
charPos = core::SelectionState::END_OF_WORD;
} else {
- Line *line = lines->getRef (findLineIndex (event->yWidget));
+ Line *line =
+ lines->getRef (findLineIndexWhenAllocated (event->yWidget));
// Pointer within the break space?
if (event->yWidget >
@@ -761,11 +878,11 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
// Choose this break.
wordIndex = line->lastWord;
charPos = core::SelectionState::END_OF_WORD;
- } else if (event->xWidget < lineXOffsetWidget (line)) {
+ } else if (event->xWidget < line->offsetCompleteWidget) {
// Left of the first word in the line.
wordIndex = line->firstWord;
} else {
- int nextWordStartX = lineXOffsetWidget (line);
+ int nextWordStartX = line->offsetCompleteWidget;
for (wordIndex = line->firstWord;
wordIndex <= line->lastWord;
@@ -860,8 +977,9 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
}
}
}
- it = new TextblockIterator (this, core::Content::SELECTION_CONTENT,
- wordIndex);
+
+ it = new TextblockIterator (this, core::Content::maskForSelection (true),
+ false, wordIndex);
r = selectionHandleEvent (eventType, it, charPos, link, event);
it->unref ();
return r;
@@ -894,7 +1012,25 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size)
availDescent = this->availDescent;
if (widget->usesHints ()) {
- widget->setWidth (availWidth);
+ // This is a simplified version of calcAvailWidth (see there for
+ // more details). Until recently, the *attribute* availWidth was
+ // used, widthout any corrections. To limit the damage, only
+ // includde left and right border (by floats), until the Great
+ // Redesign Of Widget Sizes (GROWS).
+ int corrAvailWidth;
+ // Textblocks keep track of borders themselves, so they get the
+ // total available width. (Should once replaced by something
+ // like OOFAware.)
+ if (widget->instanceOf (Textblock::CLASS_ID))
+ corrAvailWidth = availWidth;
+ else
+ corrAvailWidth =
+ misc::max (availWidth - (newLineLeftBorder + newLineRightBorder),
+ 0);
+
+ PRINTF ("setWidth (%d) for the %s %p\n",
+ corrAvailWidth, widget->getClassName(), widget);
+ widget->setWidth (corrAvailWidth);
widget->setAscent (availAscent);
widget->setDescent (availDescent);
widget->sizeRequest (size);
@@ -1245,7 +1381,7 @@ void Textblock::drawSpace(int wordIndex, core::View *view,
*/
void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
{
- int xWidget = lineXOffsetWidget(line);
+ int xWidget = line->offsetCompleteWidget;
int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
for (int wordIndex = line->firstWord;
@@ -1256,10 +1392,10 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
if (xWidget + wordSize + word->hyphenWidth + word->effSpace >= area->x) {
if (word->content.type == core::Content::TEXT ||
- word->content.type == core::Content::WIDGET) {
+ word->content.type == core::Content::WIDGET_IN_FLOW) {
if (word->size.width > 0) {
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
core::Widget *child = word->content.widget;
core::Rectangle childArea;
@@ -1303,10 +1439,36 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
}
/**
- * Find the first line index that includes y, relative to top of widget.
+ * Find the first line index that includes y, which is given in widget
+ * coordinates.
*/
int Textblock::findLineIndex (int y)
{
+ return wasAllocated () ?
+ findLineIndexWhenAllocated (y) : findLineIndexWhenNotAllocated (y);
+}
+
+int Textblock::findLineIndexWhenNotAllocated (int y)
+{
+ if (lines->size() == 0)
+ return -1;
+ else
+ return findLineIndex (y,
+ lines->getRef(0)->boxAscent + verticalOffset +
+ getStyle()->boxOffsetY());
+}
+
+int Textblock::findLineIndexWhenAllocated (int y)
+{
+ assert (wasAllocated ());
+ return findLineIndex (y, allocation.ascent);
+}
+
+int Textblock::findLineIndex (int y, int ascent)
+{
+ core::Allocation alloc;
+ alloc.ascent = ascent; // More is not needed.
+
int maxIndex = lines->size () - 1;
int step, index, low = 0;
@@ -1314,12 +1476,12 @@ int Textblock::findLineIndex (int y)
while ( step > 1 ) {
index = low + step;
if (index <= maxIndex &&
- lineYOffsetWidgetI (index) <= y)
+ lineYOffsetWidgetIAllocation (index, &alloc) <= y)
low = index;
step = (step + 1) >> 1;
}
- if (low < maxIndex && lineYOffsetWidgetI (low + 1) <= y)
+ if (low < maxIndex && lineYOffsetWidgetIAllocation (low + 1, &alloc) <= y)
low++;
/*
@@ -1340,12 +1502,13 @@ int Textblock::findLineIndex (int y)
*/
int Textblock::findLineOfWord (int wordIndex)
{
- int high = lines->size () - 1, index, low = 0;
-
- // TODO regard also not-yet-existing lines?
- if (wordIndex < 0 || wordIndex >= words->size ())
+ if (wordIndex < 0 || wordIndex >= words->size () ||
+ // Also regard not-yet-existing lines.
+ lines->size () <= 0 || wordIndex > lines->getLastRef()->lastWord)
return -1;
+ int high = lines->size () - 1, index, low = 0;
+
while (true) {
index = (low + high) / 2;
if (wordIndex >= lines->getRef(index)->firstWord) {
@@ -1396,14 +1559,14 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace)
*inSpace = false;
- if ((lineIndex = findLineIndex (y)) >= lines->size ())
+ if ((lineIndex = findLineIndexWhenAllocated (y)) >= lines->size ())
return NULL;
line = lines->getRef (lineIndex);
yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
if (yWidgetBase + line->boxDescent <= y)
return NULL;
- xCursor = lineXOffsetWidget (line);
+ xCursor = line->offsetCompleteWidget;
for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
word = words->getRef (wordIndex);
lastXCursor = xCursor;
@@ -1438,9 +1601,17 @@ void Textblock::draw (core::View *view, core::Rectangle *area)
int lineIndex;
Line *line;
- drawWidgetBox (view, area, false);
+ // Instead of drawWidgetBox, use drawBox to include verticalOffset.
+ if (getParent() == NULL) {
+ // The toplevel (parent == NULL) widget is a special case, which
+ // we leave to drawWidgetBox; verticalOffset will here always 0.
+ assert (verticalOffset == 0);
+ drawWidgetBox (view, area, false);
+ } else
+ drawBox (view, getStyle(), area, 0, verticalOffset, allocation.width,
+ getHeight() - verticalOffset, false);
- lineIndex = findLineIndex (area->y);
+ lineIndex = findLineIndexWhenAllocated (area->y);
for (; lineIndex < lines->size (); lineIndex++) {
line = lines->getRef (lineIndex);
@@ -1449,6 +1620,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area)
drawLine (line, view, area);
}
+
+ if(outOfFlowMgr)
+ outOfFlowMgr->draw(view, area);
}
/**
@@ -1935,6 +2109,12 @@ void Textblock::addText0 (const char *text, size_t len, short flags,
word->content.type = core::Content::TEXT;
word->content.text = layout->textZone->strndup(text, len);
+ // The following debug message may be useful to identify the
+ // different textblocks.
+
+ //if (words->size() == 1)
+ // printf ("[%p] first word: '%s'\n", this, text);
+
processWord (words->size () - 1);
}
@@ -1943,28 +2123,48 @@ void Textblock::addText0 (const char *text, size_t len, short flags,
*/
void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
{
- Word *word;
- core::Requisition size;
-
/* We first assign -1 as parent_ref, since the call of widget->size_request
* will otherwise let this Textblock be rewrapped from the beginning.
* (parent_ref is actually undefined, but likely has the value 0.) At the,
* end of this function, the correct value is assigned. */
widget->parentRef = -1;
- PRINTF ("%p becomes child of %p\n", widget, this);
-
- widget->setParent (this);
widget->setStyle (style);
- calcWidgetSize (widget, &size);
- word = addWord (size.width, size.ascent, size.descent, 0, style);
+ PRINTF ("adding the %s %p to %p (word %d) ...\n",
+ widget->getClassName(), widget, this, words->size());
- word->content.type = core::Content::WIDGET;
- word->content.widget = widget;
+ if (containingBlock->outOfFlowMgr == NULL)
+ containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock);
+
+ if (OutOfFlowMgr::isWidgetHandledByOOFM (widget)) {
+ PRINTF (" -> out of flow.\n");
+
+ widget->setParent (containingBlock);
+ widget->setGenerator (this);
+ containingBlock->outOfFlowMgr->addWidgetOOF (widget, this,
+ words->size ());
+ Word *word = addWord (0, 0, 0, 0, style);
+ word->content.type = core::Content::WIDGET_OOF_REF;
+ word->content.widget = widget;
+ word->style = style;
+ } else {
+ PRINTF (" -> within flow.\n");
- //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1,
- // word->content.widget);
+ widget->setParent (this);
+
+ // TODO Replace (perhaps) later "textblock" by "OOF aware widget".
+ if (widget->instanceOf (Textblock::CLASS_ID))
+ containingBlock->outOfFlowMgr->addWidgetInFlow ((Textblock*)widget,
+ this, words->size ());
+
+ core::Requisition size;
+ calcWidgetSize (widget, &size);
+ Word *word =
+ addWord (size.width, size.ascent, size.descent, 0, style);
+ word->content.type = core::Content::WIDGET_IN_FLOW;
+ word->content.widget = widget;
+ }
processWord (words->size () - 1);
//DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
@@ -2066,8 +2266,10 @@ void Textblock::fillSpace (int wordNo, core::style::Style *style)
// TODO: This line does not work: addBreakOption (word, style);
- // Do not override a previously set break penalty.
- if (!word->content.space) {
+ if (// Do not override a previously set break penalty:
+ !word->content.space &&
+ // OOF references are considered specially, and must not have a space:
+ word->content.type != core::Content::WIDGET_OOF_REF) {
setBreakOption (word, style, 0, 0, false);
word->content.space = true;
@@ -2139,7 +2341,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
/* A break may not be the first word of a page, or directly after
the bullet/number (which is the first word) in a list item. (See
- also comment in Dw_page_size_request.) */
+ also comment in sizeRequest.) */
if (words->size () == 0 ||
(hasListitemValue && words->size () == 1)) {
/* This is a bit hackish: If a break is added as the
@@ -2148,23 +2350,23 @@ void Textblock::addParbreak (int space, core::style::Style *style)
a widget is used as a text box (lists, blockquotes, list
items etc) -- then we simply adjust the break before, in a
way that the space is in any case visible. */
- Widget *widget;
-
- /* Find the widget where to adjust the breakSpace. */
- for (widget = this;
- widget->getParent() &&
- widget->getParent()->instanceOf (Textblock::CLASS_ID);
+ /* Find the widget where to adjust the breakSpace. (Only
+ consider normal flow, no floats etc.) */
+ for (Widget *widget = this;
+ widget->getParent() != NULL &&
+ widget->getParent()->instanceOf (Textblock::CLASS_ID) &&
+ !OutOfFlowMgr::isRefOutOfFlow (widget->parentRef);
widget = widget->getParent ()) {
Textblock *textblock2 = (Textblock*)widget->getParent ();
int index = textblock2->hasListitemValue ? 1 : 0;
bool isfirst = (textblock2->words->getRef(index)->content.type
- == core::Content::WIDGET
+ == core::Content::WIDGET_IN_FLOW
&& textblock2->words->getRef(index)->content.widget
== widget);
if (!isfirst) {
- /* The page we searched for has been found. */
+ /* The text block we searched for has been found. */
Word *word2;
- int lineno = widget->parentRef;
+ int lineno = OutOfFlowMgr::getLineNoFromRef (widget->parentRef);
if (lineno > 0 &&
(word2 =
@@ -2173,7 +2375,8 @@ void Textblock::addParbreak (int space, core::style::Style *style)
word2->content.type == core::Content::BREAK) {
if (word2->content.breakSpace < space) {
word2->content.breakSpace = space;
- textblock2->queueResize (lineno, false);
+ textblock2->queueResize
+ (OutOfFlowMgr::createRefNormalFlow (lineno), false);
textblock2->mustQueueResize = false;
}
}
@@ -2181,6 +2384,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
}
/* Otherwise continue to examine parents. */
}
+
/* Return in any case. */
return;
}
@@ -2229,7 +2433,6 @@ void Textblock::addLinebreak (core::style::Style *style)
processWord (words->size () - 1);
}
-
/**
* \brief Search recursively through widget.
*
@@ -2238,6 +2441,10 @@ void Textblock::addLinebreak (core::style::Style *style)
*/
core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
{
+ //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n",
+ // 3 * level, "", getClassName (), this, allocation.x, allocation.y,
+ // allocation.width, allocation.ascent, allocation.descent);
+
int lineIndex, wordIndex;
Line *line;
@@ -2248,7 +2455,15 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
return NULL;
}
- lineIndex = findLineIndex (y - allocation.y);
+ // First, search for widgets out of flow, notably floats, since
+ // there are cases where they overlap child textblocks. Should
+ // later be refined using z-index.
+ Widget *oofWidget =
+ outOfFlowMgr ? outOfFlowMgr->getWidgetAtPoint (x, y, level) : NULL;
+ if (oofWidget)
+ return oofWidget;
+
+ lineIndex = findLineIndexWhenAllocated (y - allocation.y);
if (lineIndex < 0 || lineIndex >= lines->size ()) {
return this;
@@ -2259,7 +2474,7 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
Word *word = words->getRef (wordIndex);
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
core::Widget * childAtPoint;
childAtPoint = word->content.widget->getWidgetAtPoint (x, y,
level + 1);
@@ -2340,7 +2555,7 @@ void Textblock::changeLinkColor (int link, int newColor)
old_style->unref();
break;
}
- case core::Content::WIDGET:
+ case core::Content::WIDGET_IN_FLOW:
{ core::Widget *widget = word->content.widget;
styleAttrs = *widget->getStyle();
styleAttrs.color = core::style::Color::create (layout,
@@ -2392,4 +2607,277 @@ void Textblock::queueDrawRange (int index1, int index2)
}
}
+void Textblock::setVerticalOffset (int verticalOffset)
+{
+ if (this->verticalOffset != verticalOffset) {
+ this->verticalOffset = verticalOffset;
+ mustQueueResize = true;
+ queueDraw (); // Could perhaps be optimized.
+ }
+}
+
+/**
+ * Called by dw::OutOfFlowMgr when the border has changed due to a
+ * float (or some floats).
+ *
+ * "y", which given in widget coordinates, denotes the minimal
+ * position (when more than one float caused this), "vloat" the
+ * floating widget belonging to "y".
+ */
+void Textblock::borderChanged (int y, Widget *vloat)
+{
+ PRINTF ("[%p] BORDER_CHANGED: %d (float %s %p, with generator %p)\n",
+ this, y, vloat->getClassName(), vloat, vloat->getGenerator());
+
+ int lineIndex = findLineIndex (y);
+ PRINTF (" Line index: %d (of %d).\n", lineIndex, lines->size ());
+
+ // Nothing to do at all, when lineIndex >= lines->size (),
+ // i. e. the change is below the bottom of this widget.
+ if (lineIndex < lines->size ()) {
+ int wrapLineIndex;
+ if (lineIndex < 0)
+ // Rewrap all.
+ wrapLineIndex = 0;
+ else
+ wrapLineIndex = lineIndex;
+
+ PRINTF (" Rewrapping from line %d (of %d).\n",
+ wrapLineIndex, lines->size ());
+
+ if (vloat->getGenerator() == this) {
+ bool found = false;
+ // Sometimes, the respective word is not yet part of a
+ // line. Nothing to do, but because of the assertion below
+ // (and also for performace reasons) this should be
+ // considered. TODO: Integrate this below.
+ for (int wordIndex =
+ lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0;
+ !found && wordIndex < words->size(); wordIndex++) {
+ Word *word = words->getRef (wordIndex);
+ if (word->content.type == core::Content::WIDGET_OOF_REF &&
+ word->content.widget == vloat)
+ found = true;
+ }
+
+ // We search for the line of the float reference. There are
+ // two cases when this is not the line corresponsing to y:
+ //
+ // (1) When the float was moved down, due to collisions with
+ // other floats: in this case, the line number gets
+ // smaller (since the float reference is before).
+ //
+ // (2) In some cases, the line number may become larger, due
+ // to the per-line optimization of the words: initially,
+ // lines->size() - 1 is assigned, but it may happen that
+ // the float reference is put into another line.
+ //
+ // Only in the first case, a correction is neccessary, but a
+ // test for the second case is useful. (TODO: I've forgotten
+ // why a correction is neccessary.)
+ //
+ // Searched is done in the following order:
+ //
+ // - wrapLineIndex,
+ // - wrapLineIndex - 1,
+ // - wrapLineIndex + 1,
+ // - wrapLineIndex - 2,
+ // - wrapLineIndex + 2,
+ //
+ // etc. until either the float reference has been found or
+ // all lines have been searched (the latter triggers an
+ // abortion).
+
+ bool exceedsBeginning = false, exceedsEnd = false;
+ for (int i = 0; !found; i++) {
+ bool exceeds;
+ int lineIndex2;
+ if (i % 2 == 0) {
+ // even: +0, +1, +2, ...
+ lineIndex2 = wrapLineIndex + i / 2;
+ if (i > 0)
+ exceeds = exceedsEnd = lineIndex2 >= lines->size ();
+ else
+ exceeds = exceedsEnd = false;
+ } else {
+ // odd: -1, -2, ...
+ lineIndex2 = wrapLineIndex - (i + 1) / 2;
+ exceeds = exceedsBeginning = lineIndex2 < 0;
+ }
+
+ if (exceedsBeginning && exceedsEnd)
+ break;
+
+ if (!exceeds) {
+ Line *line = lines->getRef (lineIndex2);
+ for (int wordIndex = line->firstWord;
+ !found && wordIndex <= line->lastWord; wordIndex++) {
+ Word *word = words->getRef (wordIndex);
+ if (word->content.type == core::Content::WIDGET_OOF_REF &&
+ word->content.widget == vloat) {
+ found = true;
+ // Correct only by smaller values (case (1) above):
+ wrapLineIndex = misc::min (wrapLineIndex, lineIndex2);
+ }
+ }
+ }
+ }
+
+ if (!found)
+ printBorderChangedErrorAndAbort (y, vloat, wrapLineIndex);
+ }
+
+ PRINTF (" Corrected to line %d.\n", wrapLineIndex);
+
+ queueResize (OutOfFlowMgr::createRefNormalFlow (wrapLineIndex), true);
+ lastWordDrawn = lines->getRef(wrapLineIndex)->firstWord;
+ }
+}
+
+void Textblock::printBorderChangedErrorAndAbort (int y, Widget *vloat,
+ int wrapLineIndex)
+{
+ // TODO Should be adjusted to recent changes in borderChanged().
+
+ printf ("*** This call failed! ***\n");
+ printf ("[%p] borderChanged (%d, %p (%s))\n",
+ this, y, vloat, vloat->getClassName());
+ printf (" Initial wrapLineIndex = line %d (of %d lines). "
+ "Has to be corrected.\n", wrapLineIndex, lines->size ());
+
+ printf (" search non-existing line (from %d to %d):\n",
+ lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0,
+ words->size() - 1);
+ for (int wordIndex =
+ lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0;
+ wordIndex < words->size(); wordIndex++) {
+ printf (" word %d: ", wordIndex);
+ printWordShort (words->getRef (wordIndex));
+ printf ("\n");
+ }
+
+ for (int lineIndex2 = wrapLineIndex; lineIndex2 >= 0;
+ lineIndex2--) {
+ Line *line = lines->getRef (lineIndex2);
+ printf (" searching existing line %d (from %d to %d):\n",
+ lineIndex2, line->firstWord, line->lastWord);
+ for (int wordIndex = line->firstWord;
+ wordIndex <= line->lastWord; wordIndex++) {
+ printf (" word %d: ", wordIndex);
+ printWordShort (words->getRef (wordIndex));
+ printf ("\n");
+ }
+ }
+
+ bool found2 = false;
+ for (int lineIndex2 = wrapLineIndex + 1;
+ !found2 && lineIndex2 < lines->size (); lineIndex2++) {
+ Line *line = lines->getRef (lineIndex2);
+ printf (" could have searched existing line %d "
+ "(from %d to %d):\n",
+ lineIndex2, line->firstWord, line->lastWord);
+ for (int wordIndex = line->firstWord;
+ !found2 && wordIndex <= line->lastWord; wordIndex++) {
+ Word *word = words->getRef (wordIndex);
+ if (word->content.type == core::Content::WIDGET_OOF_REF &&
+ word->content.widget == vloat) {
+ found2 = true;
+ printf (" word %d: ", wordIndex);
+ printWordShort (word);
+ printf ("\n");
+ }
+ }
+ }
+
+ lout::misc::assertNotReached ();
+}
+
+Textblock *Textblock::getTextblockForLine (Line *line)
+{
+ return getTextblockForLine (line->firstWord, line->lastWord);
+}
+
+Textblock *Textblock::getTextblockForLine (int lineNo)
+{
+ int firstWord = lineNo == 0 ? 0 :lines->getRef(lineNo - 1)->lastWord + 1;
+ int lastWord = lineNo < lines->size() ?
+ lines->getRef(lineNo)->lastWord : words->size() - 1;
+ return getTextblockForLine (firstWord, lastWord);
+}
+
+Textblock *Textblock::getTextblockForLine (int firstWord, int lastWord)
+{
+ if (firstWord < words->size ()) {
+ //printf ("[%p] GET_TEXTBLOCK_FOR_LINE (%d, %d)\n",
+ // this, firstWord, lastWord);
+
+ // A textblock is always between two line breaks, and so the
+ // first word of the line.
+ Word *word = words->getRef (firstWord);
+
+ if (word->content.type == core::Content::WIDGET_IN_FLOW &&
+ word->content.widget->instanceOf (Textblock::CLASS_ID)) {
+ //printf (" word %d: ", firstWord);
+ //printWordShort (word);
+ //printf ("\n");
+
+ return (Textblock*)word->content.widget;
+ }
+ }
+
+ //printf (" nothing\n");
+ return NULL;
+}
+
+/**
+ * Includes margin, border, and padding.
+ */
+int Textblock::yOffsetOfPossiblyMissingLine (int lineNo)
+{
+ if (lineNo == 0)
+ return verticalOffset + getStyle()->boxOffsetY();
+ else {
+ Line *prevLine = lines->getRef (lineNo - 1);
+ return verticalOffset +
+ prevLine->top + prevLine->boxAscent + prevLine->boxDescent +
+ prevLine->breakSpace + getStyle()->boxOffsetY();
+ }
+}
+
+int Textblock::heightOfPossiblyMissingLine (int lineNo)
+{
+ PRINTF ("[%p] HEIGHT_OF_POSSIBLY_MISSING_LINE (%d)\n", this, lineNo);
+
+ if (lineNo < lines->size()) {
+ // An existing line.
+
+ Line *line = lines->getRef (lineNo);
+
+ // This is sometimes called within addLine, so that the
+ // condition above is true, but line->boxAscent and
+ // line->boxDescent are not set appropriately. We have to
+ // accumulate the heights then.
+
+ if (line->finished) {
+ PRINTF (" Exists and is finished; height = %d + %d = %d\n",
+ line->boxAscent, line->boxDescent,
+ line->boxAscent + line->boxDescent);
+ return line->boxAscent + line->boxDescent;
+ } else {
+ PRINTF (" Exist but is not finished.\n");
+ return misc::max (1, newLineAscent + newLineDescent);
+ }
+ } else if (lineNo == lines->size()) {
+ // The line to be constructed: some words exist, but not the
+ // line. Accumulate the word heights.
+
+ // Old comment: Furthermore, this is in some cases incomplete:
+ // see doc/dw-out-of-flow.doc. -- Still the case?
+
+ PRINTF (" Does not exist.\n");
+ return misc::max (1, newLineAscent + newLineDescent);
+ } else
+ return 1;
+}
+
} // namespace dw
diff --git a/dw/textblock.hh b/dw/textblock.hh
index b85937ba..b9255bb9 100644
--- a/dw/textblock.hh
+++ b/dw/textblock.hh
@@ -4,6 +4,7 @@
#include <limits.h>
#include "core.hh"
+#include "outofflowmgr.hh"
#include "../lout/misc.hh"
// These were used when improved line breaking and hyphenation were
@@ -11,6 +12,8 @@
#define PRINTF(fmt, ...)
#define PUTCHAR(ch)
+//#define DEBUG
+
namespace dw {
/**
@@ -18,10 +21,11 @@ namespace dw {
* of paragraphs.
*
* <div style="border: 2px solid #ffff00; margin-top: 0.5em;
- * margin-bottom: 0.5em; padding: 0.5em 1em;
- * background-color: #ffffe0"><b>Info:</b> The recent changes (line
- * breaking and hyphenation) have not yet been incorporated into this
- * documentation. See \ref dw-line-breaking.</div>
+ * margin-bottom: 0.5em; padding: 0.5em 1em; background-color:
+ * #ffffe0"><b>Info:</b> The recent changes (line breaking and
+ * hyphenation on one hand, floats on the other hand) have not yet
+ * been incorporated into this documentation. See \ref
+ * dw-line-breaking and \ref dw-out-of-flow.</div>
*
* <h3>Signals</h3>
*
@@ -236,6 +240,9 @@ private:
static const char *hyphenDrawChar;
+ Textblock *containingBlock;
+ OutOfFlowMgr *outOfFlowMgr;
+
protected:
/**
* \brief Implementation used for words.
@@ -308,10 +315,21 @@ protected:
int firstWord; /* first word's index in word vector */
int lastWord; /* last word's index in word vector */
- /* "top" is always relative to the top of the first line, i.e.
- * page->lines[0].top is always 0. */
- int top, boxAscent, boxDescent, contentAscent, contentDescent,
- breakSpace, leftOffset;
+
+ int top; /* "top" is always relative to the top
+ of the first line, i.e.
+ page->lines[0].top is always 0. */
+ int boxAscent; /* Maximum of all ascents of the words
+ in this line. This is the actual
+ ascent of the line. */
+ int boxDescent; /* Maximum of all decents of the words
+ in this line. This is the actual
+ descent of the line. */
+ int contentAscent; /* ??? */
+ int contentDescent; /* ??? */
+ int breakSpace; /* Space between this line and the next one. */
+ int leftOffset; /* ??? */
+ int offsetCompleteWidget; /* ??? */
/* This is similar to descent, but includes the bottom margins of the
* widgets within this line. */
@@ -323,6 +341,11 @@ protected:
* changed line breaking), the values were accumulated up to the
* last line, not this line.*/
int maxLineWidth;
+
+ /* Set to false at the beginning of addLine(), and to true at
+ * the end. Should be checked by some methods which are called
+ * by addLine(). */
+ bool finished;
};
struct Word
@@ -409,13 +432,14 @@ protected:
class TextblockIterator: public core::Iterator
{
private:
+ bool oofm;
int index;
public:
TextblockIterator (Textblock *textblock, core::Content::Type mask,
bool atEnd);
TextblockIterator (Textblock *textblock, core::Content::Type mask,
- int index);
+ bool oofm, int index);
lout::object::Object *clone();
int compareTo(lout::object::Comparable *other);
@@ -425,6 +449,7 @@ protected:
void highlight (int start, int end, core::HighlightLayer layer);
void unhighlight (int direction, core::HighlightLayer layer);
void getAllocation (int start, int end, core::Allocation *allocation);
+ void print ();
};
friend class TextblockIterator;
@@ -479,7 +504,31 @@ protected:
/* These values are set by set_... */
int availWidth, availAscent, availDescent;
- int wrapRefLines, wrapRefParagraphs; /* [0 based] */
+ // Additional vertical offset, used for the "clear" attribute.
+ int verticalOffset;
+
+ int wrapRefLines, wrapRefParagraphs; /* 0-based. Important: Both
+ are the line numbers, not
+ the value stored in
+ parentRef. */
+
+ // These four values are calculated by containingBlock->outOfFlowMgr
+ // (when defined; otherwise, they are false, or 0, respectively), for
+ // the newly constructed line, only when needed: when a new line is
+ // added, or if something in the line currently constucted has
+ // changed, e. g. a float has been added.
+
+ bool newLineHasFloatLeft, newLineHasFloatRight;
+ int newLineLeftBorder, newLineRightBorder; /* As returned by
+ outOfFlowMgr->get...Border,
+ or 0, if outOfFlowMgr
+ is NULL */
+
+ // Ascent and descent of the newly constructed line, i. e. maximum
+ // of all words ascent/descent since the end of the last line. Not
+ // neccessary the ascent and descent of the newly added line, since
+ // not all words are added to it.
+ int newLineAscent, newLineDescent;
lout::misc::SimpleVector <Line> *lines;
lout::misc::SimpleVector <Paragraph> *paragraphs;
@@ -492,7 +541,6 @@ protected:
int hoverLink; /* The link under the mouse pointer */
-
void queueDrawRange (int index1, int index2);
void getWordExtremes (Word *word, core::Extremes *extremes);
void justifyLine (Line *line, int diff);
@@ -500,8 +548,10 @@ protected:
void calcWidgetSize (core::Widget *widget, core::Requisition *size);
void rewrap ();
void fillParagraphs ();
+ void initNewLine ();
void showMissingLines ();
void removeTemporaryLines ();
+ void setVerticalOffset (int verticalOffset);
void decorateText (core::View *view, core::style::Style *style,
core::style::Color::Shading shading,
@@ -520,6 +570,9 @@ protected:
int xWidget, int yWidgetBase);
void drawLine (Line *line, core::View *view, core::Rectangle *area);
int findLineIndex (int y);
+ int findLineIndexWhenNotAllocated (int y);
+ int findLineIndexWhenAllocated (int y);
+ int findLineIndex (int y, int ascent);
int findLineOfWord (int wordIndex);
int findParagraphOfWord (int wordIndex);
Word *findWord (int x, int y, bool *inSpace);
@@ -543,25 +596,17 @@ protected:
core::Requisition *size, bool isStart, bool isEnd);
/**
- * \brief Returns the x offset (the indentation plus any offset needed for
- * centering or right justification) for the line.
- *
- * The offset returned is relative to the page *content* (i.e. without
- * border etc.).
+ * Of nested text blocks, only the most inner one must regard the
+ * borders of floats.
*/
- inline int lineXOffsetContents (Line *line)
+ inline bool mustBorderBeRegarded (Line *line)
{
- return innerPadding + line->leftOffset +
- (line == lines->getFirstRef() ? line1OffsetEff : 0);
+ return getTextblockForLine (line) == NULL;
}
- /**
- * \brief Like lineXOffset, but relative to the allocation (i.e.
- * including border etc.).
- */
- inline int lineXOffsetWidget (Line *line)
+ inline bool mustBorderBeRegarded (int lineNo)
{
- return lineXOffsetContents (line) + getStyle()->boxOffsetX ();
+ return getTextblockForLine (lineNo) == NULL;
}
inline int lineYOffsetWidgetAllocation (Line *line,
@@ -581,7 +626,7 @@ protected:
inline int lineYOffsetCanvasAllocation (Line *line,
core::Allocation *allocation)
{
- return allocation->y + lineYOffsetWidgetAllocation(line, allocation);
+ return allocation->y + lineYOffsetWidgetAllocation (line, allocation);
}
/**
@@ -597,6 +642,13 @@ protected:
return lineYOffsetWidget (lines->getRef (lineIndex));
}
+ inline int lineYOffsetWidgetIAllocation (int lineIndex,
+ core::Allocation *allocation)
+ {
+ return lineYOffsetWidgetAllocation (lines->getRef (lineIndex),
+ allocation);
+ }
+
inline int lineYOffsetCanvasI (int lineIndex)
{
return lineYOffsetCanvas (lines->getRef (lineIndex));
@@ -612,6 +664,14 @@ protected:
(Word::DIV_CHAR_AT_EOL | Word::PERM_DIV_CHAR)) ? 1 : 0;
}
+ Textblock *getTextblockForLine (Line *line);
+ Textblock *getTextblockForLine (int lineNo);
+ Textblock *getTextblockForLine (int firstWord, int lastWord);
+ void printBorderChangedErrorAndAbort (int y, Widget *vloat,
+ int wrapLineIndex);
+ int yOffsetOfPossiblyMissingLine (int lineNo);
+ int heightOfPossiblyMissingLine (int lineNo);
+
bool sendSelectionEvent (core::SelectionState::EventType eventType,
core::MousePositionEvent *event);
@@ -619,11 +679,15 @@ protected:
int *maxOfMinWidth, int *sumOfMaxWidth);
void processWord (int wordIndex);
virtual bool wordWrap (int wordIndex, bool wrapAll);
+ bool wrapWordInFlow (int wordIndex, bool wrapAll);
+ void checkPossibleLineHeightChange (int wordIndex);
+ bool wrapWordOofRef (int wordIndex, bool wrapAll);
+ void updateBorders (int wordIndex, bool left, bool right);
int searchMinBap (int firstWord, int lastWordm, int penaltyIndex,
bool correctAtEnd);
int considerHyphenation (int firstIndex, int breakPos);
bool isHyphenationCandidate (Word *word);
-
+
void handleWordExtremes (int wordIndex);
void correctLastWordExtremes ();
@@ -645,6 +709,8 @@ protected:
void markSizeChange (int ref);
void markExtremesChange (int ref);
+ void notifySetAsTopLevel();
+ void notifySetParent();
void setWidth (int width);
void setAscent (int ascent);
void setDescent (int descent);
@@ -664,6 +730,7 @@ protected:
core::style::Style *style,
int numBreaks, int *breakPos,
core::Requisition *wordSize);
+ static bool isContainingBlock (Widget *widget);
public:
static int CLASS_ID;
@@ -699,6 +766,11 @@ public:
void changeLinkColor (int link, int newColor);
void changeWordStyle (int from, int to, core::style::Style *style,
bool includeFirstSpace, bool includeLastSpace);
+
+ void borderChanged (int y, core::Widget *vloat);
+ inline int getAvailWidth () { return availWidth; }
+ inline int getAvailAscent () { return availAscent; }
+ inline int getAvailDescent () { return availDescent; }
};
} // namespace dw
diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc
index 7531ecd5..c26b7f6e 100644
--- a/dw/textblock_iterator.cc
+++ b/dw/textblock_iterator.cc
@@ -35,20 +35,34 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
bool atEnd):
core::Iterator (textblock, mask, atEnd)
{
- index = atEnd ? textblock->words->size () : -1;
+ if (atEnd) {
+ if (textblock->outOfFlowMgr) {
+ oofm = true;
+ index = textblock->outOfFlowMgr->getNumWidgets();
+ } else {
+ oofm = false;
+ index = textblock->words->size();
+ }
+ } else {
+ oofm = false;
+ index = -1;
+ }
+
content.type = atEnd ? core::Content::END : core::Content::START;
}
Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
core::Content::Type mask,
- int index):
+ bool oofm, int index):
core::Iterator (textblock, mask, false)
{
+ this->oofm = oofm;
this->index = index;
+ // TODO To be completely exact, oofm should be considered here.
if (index < 0)
content.type = core::Content::START;
- else if (index >= textblock->words->size ())
+ else if (index >= textblock->words->size())
content.type = core::Content::END;
else
content = textblock->words->getRef(index)->content;
@@ -56,12 +70,20 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
object::Object *Textblock::TextblockIterator::clone()
{
- return new TextblockIterator ((Textblock*)getWidget(), getMask(), index);
+ return
+ new TextblockIterator ((Textblock*)getWidget(), getMask(), oofm, index);
}
int Textblock::TextblockIterator::compareTo(object::Comparable *other)
{
- return index - ((TextblockIterator*)other)->index;
+ TextblockIterator *otherTI = (TextblockIterator*)other;
+
+ if (oofm && !otherTI->oofm)
+ return +1;
+ else if (!oofm && otherTI->oofm)
+ return -1;
+ else
+ return index - otherTI->index;
}
bool Textblock::TextblockIterator::next ()
@@ -71,15 +93,52 @@ bool Textblock::TextblockIterator::next ()
if (content.type == core::Content::END)
return false;
+ short type;
+
do {
index++;
- if (index >= textblock->words->size ()) {
- content.type = core::Content::END;
- return false;
+
+ if (oofm) {
+ // Iterating over OOFM.
+ if (index >= textblock->outOfFlowMgr->getNumWidgets()) {
+ // End of OOFM list reached.
+ content.type = core::Content::END;
+ return false;
+ }
+ type = core::Content::WIDGET_OOF_CONT;
+ } else {
+ // Iterating over words list.
+ if (index < textblock->words->size ())
+ // Still words left.
+ type = textblock->words->getRef(index)->content.type;
+ else {
+ // End of words list reached.
+ if (textblock->outOfFlowMgr) {
+ oofm = true;
+ index = 0;
+ if (textblock->outOfFlowMgr->getNumWidgets() > 0)
+ // Start with OOFM widgets.
+ type = core::Content::WIDGET_OOF_CONT;
+ else {
+ // No OOFM widgets (number is 0).
+ content.type = core::Content::END;
+ return false;
+ }
+ } else {
+ // No OOFM widgets (no OOFM agt all).
+ content.type = core::Content::END;
+ return false;
+ }
+ }
}
- } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
+ } while ((type & getMask()) == 0);
+
+ if (oofm) {
+ content.type = core::Content::WIDGET_OOF_CONT;
+ content.widget = textblock->outOfFlowMgr->getWidget (index);
+ } else
+ content = textblock->words->getRef(index)->content;
- content = textblock->words->getRef(index)->content;
return true;
}
@@ -90,132 +149,188 @@ bool Textblock::TextblockIterator::prev ()
if (content.type == core::Content::START)
return false;
+ short type;
+
do {
index--;
- if (index < 0) {
- content.type = core::Content::START;
- return false;
+
+ if (oofm) {
+ // Iterating over OOFM.
+ if (index >= 0)
+ // Still widgets left.
+ type = core::Content::WIDGET_OOF_CONT;
+ else {
+ // Beginning of OOFM list reached. Continue with words.
+ oofm = false;
+ index = textblock->words->size() - 1;
+ if (index < 0) {
+ // There are no words. (Actually, this case should not
+ // happen: When there are OOF widgets, ther must be OOF
+ // references, or widgets in flow, which contain
+ // references.
+ content.type = core::Content::END;
+ return false;
+ }
+ type = textblock->words->getRef(index)->content.type;
+ }
+ } else {
+ // Iterating over words list.
+ if (index < 0) {
+ // Beginning of words list reached.
+ content.type = core::Content::START;
+ return false;
+ }
+ type = textblock->words->getRef(index)->content.type;
}
- } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
+ } while ((type & getMask()) == 0);
+
+ if (oofm) {
+ content.type = core::Content::WIDGET_OOF_CONT;
+ content.type = false;
+ content.widget = textblock->outOfFlowMgr->getWidget (index);
+ } else
+ content = textblock->words->getRef(index)->content;
- content = textblock->words->getRef(index)->content;
return true;
}
void Textblock::TextblockIterator::highlight (int start, int end,
core::HighlightLayer layer)
{
- Textblock *textblock = (Textblock*)getWidget();
- int index1 = index, index2 = index;
-
- int oldStartIndex = textblock->hlStart[layer].index;
- int oldStartChar = textblock->hlStart[layer].nChar;
- int oldEndIndex = textblock->hlEnd[layer].index;
- int oldEndChar = textblock->hlEnd[layer].nChar;
-
- if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
- /* nothing is highlighted */
- textblock->hlStart[layer].index = index;
- textblock->hlEnd[layer].index = index;
- }
-
- if (textblock->hlStart[layer].index >= index) {
- index2 = textblock->hlStart[layer].index;
- textblock->hlStart[layer].index = index;
- textblock->hlStart[layer].nChar = start;
- }
-
- if (textblock->hlEnd[layer].index <= index) {
- index2 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index;
- textblock->hlEnd[layer].nChar = end;
+ if (!oofm) {
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ int oldStartIndex = textblock->hlStart[layer].index;
+ int oldStartChar = textblock->hlStart[layer].nChar;
+ int oldEndIndex = textblock->hlEnd[layer].index;
+ int oldEndChar = textblock->hlEnd[layer].nChar;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
+ /* nothing is highlighted */
+ textblock->hlStart[layer].index = index;
+ textblock->hlEnd[layer].index = index;
+ }
+
+ if (textblock->hlStart[layer].index >= index) {
+ index2 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index;
+ textblock->hlStart[layer].nChar = start;
+ }
+
+ if (textblock->hlEnd[layer].index <= index) {
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index;
+ textblock->hlEnd[layer].nChar = end;
+ }
+
+ if (oldStartIndex != textblock->hlStart[layer].index ||
+ oldStartChar != textblock->hlStart[layer].nChar ||
+ oldEndIndex != textblock->hlEnd[layer].index ||
+ oldEndChar != textblock->hlEnd[layer].nChar)
+ textblock->queueDrawRange (index1, index2);
}
- if (oldStartIndex != textblock->hlStart[layer].index ||
- oldStartChar != textblock->hlStart[layer].nChar ||
- oldEndIndex != textblock->hlEnd[layer].index ||
- oldEndChar != textblock->hlEnd[layer].nChar)
- textblock->queueDrawRange (index1, index2);
+ // TODO What about OOF widgets?
}
void Textblock::TextblockIterator::unhighlight (int direction,
core::HighlightLayer layer)
{
- Textblock *textblock = (Textblock*)getWidget();
- int index1 = index, index2 = index;
-
- if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
- return;
-
- int oldStartIndex = textblock->hlStart[layer].index;
- int oldStartChar = textblock->hlStart[layer].nChar;
- int oldEndIndex = textblock->hlEnd[layer].index;
- int oldEndChar = textblock->hlEnd[layer].nChar;
-
- if (direction == 0) {
- index1 = textblock->hlStart[layer].index;
- index2 = textblock->hlEnd[layer].index;
- textblock->hlStart[layer].index = 1;
- textblock->hlEnd[layer].index = 0;
- } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
- index1 = textblock->hlStart[layer].index;
- textblock->hlStart[layer].index = index + 1;
- textblock->hlStart[layer].nChar = 0;
- } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
- index1 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index - 1;
- textblock->hlEnd[layer].nChar = INT_MAX;
+ if (!oofm) {
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
+ return;
+
+ int oldStartIndex = textblock->hlStart[layer].index;
+ int oldStartChar = textblock->hlStart[layer].nChar;
+ int oldEndIndex = textblock->hlEnd[layer].index;
+ int oldEndChar = textblock->hlEnd[layer].nChar;
+
+ if (direction == 0) {
+ index1 = textblock->hlStart[layer].index;
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlStart[layer].index = 1;
+ textblock->hlEnd[layer].index = 0;
+ } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
+ index1 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index + 1;
+ textblock->hlStart[layer].nChar = 0;
+ } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
+ index1 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index - 1;
+ textblock->hlEnd[layer].nChar = INT_MAX;
+ }
+
+ if (oldStartIndex != textblock->hlStart[layer].index ||
+ oldStartChar != textblock->hlStart[layer].nChar ||
+ oldEndIndex != textblock->hlEnd[layer].index ||
+ oldEndChar != textblock->hlEnd[layer].nChar)
+ textblock->queueDrawRange (index1, index2);
}
- if (oldStartIndex != textblock->hlStart[layer].index ||
- oldStartChar != textblock->hlStart[layer].nChar ||
- oldEndIndex != textblock->hlEnd[layer].index ||
- oldEndChar != textblock->hlEnd[layer].nChar)
- textblock->queueDrawRange (index1, index2);
+ // TODO What about OOF widgets?
}
void Textblock::TextblockIterator::getAllocation (int start, int end,
core::Allocation *allocation)
{
Textblock *textblock = (Textblock*)getWidget();
- int lineIndex = textblock->findLineOfWord (index);
- Line *line = textblock->lines->getRef (lineIndex);
- Word *word = textblock->words->getRef (index);
-
- allocation->x =
- textblock->allocation.x + textblock->lineXOffsetWidget (line);
- for (int i = line->firstWord; i < index; i++) {
- Word *w = textblock->words->getRef(i);
- allocation->x += w->size.width + w->effSpace;
- }
- if (start > 0 && word->content.type == core::Content::TEXT) {
- allocation->x += textblock->textWidth (word->content.text, 0, start,
- word->style,
- word->flags & Word::WORD_START,
- (word->flags & Word::WORD_END)
- && word->content.text[start] == 0);
- }
- allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
- word->size.ascent;
-
- allocation->width = word->size.width;
- if (word->content.type == core::Content::TEXT) {
- int wordEnd = strlen(word->content.text);
-
- if (start > 0 || end < wordEnd) {
- end = misc::min(end, wordEnd); /* end could be INT_MAX */
- allocation->width =
- textblock->textWidth (word->content.text, start, end - start,
- word->style,
- (word->flags & Word::WORD_START)
- && start == 0,
- (word->flags & Word::WORD_END)
- && word->content.text[end] == 0);
+ if (oofm) {
+ // TODO Consider start and end?
+ *allocation =
+ *(textblock->outOfFlowMgr->getWidget(index)->getAllocation());
+ } else {
+ int lineIndex = textblock->findLineOfWord (index);
+ Line *line = textblock->lines->getRef (lineIndex);
+ Word *word = textblock->words->getRef (index);
+
+ allocation->x =
+ textblock->allocation.x + line->offsetCompleteWidget;
+
+ for (int i = line->firstWord; i < index; i++) {
+ Word *w = textblock->words->getRef(i);
+ allocation->x += w->size.width + w->effSpace;
+ }
+ if (start > 0 && word->content.type == core::Content::TEXT) {
+ allocation->x += textblock->textWidth (word->content.text, 0, start,
+ word->style,
+ word->flags & Word::WORD_START,
+ (word->flags & Word::WORD_END)
+ && word->content.text[start]
+ == 0);
+ }
+ allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
+ word->size.ascent;
+
+ allocation->width = word->size.width;
+ if (word->content.type == core::Content::TEXT) {
+ int wordEnd = strlen(word->content.text);
+
+ if (start > 0 || end < wordEnd) {
+ end = misc::min(end, wordEnd); /* end could be INT_MAX */
+ allocation->width =
+ textblock->textWidth (word->content.text, start, end - start,
+ word->style,
+ (word->flags & Word::WORD_START)
+ && start == 0,
+ (word->flags & Word::WORD_END)
+ && word->content.text[end] == 0);
+ }
}
+ allocation->ascent = word->size.ascent;
+ allocation->descent = word->size.descent;
}
- allocation->ascent = word->size.ascent;
- allocation->descent = word->size.descent;
+}
+
+void Textblock::TextblockIterator::print ()
+{
+ Iterator::print ();
+ printf (", oofm = %s, index = %d", oofm ? "true" : "false", index);
+
}
} // namespace dw
diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc
index c07dc602..1c1a3c1f 100644
--- a/dw/textblock_linebreaking.cc
+++ b/dw/textblock_linebreaking.cc
@@ -232,21 +232,7 @@ void Textblock::BadnessAndPenalty::print ()
void Textblock::printWordShort (Word *word)
{
- switch(word->content.type) {
- case core::Content::TEXT:
- printf ("\"%s\"", word->content.text);
- break;
- case core::Content::WIDGET:
- printf ("<widget: %p (%s)>",
- word->content.widget, word->content.widget->getClassName());
- break;
- case core::Content::BREAK:
- printf ("<break>");
- break;
- default:
- printf ("<?>");
- break;
- }
+ core::Content::print (&(word->content));
}
void Textblock::printWordFlags (short flags)
@@ -341,9 +327,23 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
PRINTF ("[%p] ADD_LINE (%d, %d) => %d\n",
this, firstWord, lastWord, lines->size ());
- Word *lastWordOfLine = words->getRef(lastWord);
- // Word::totalWidth includes the hyphen (which is what we want here).
- int lineWidth = lastWordOfLine->totalWidth;
+ //for (int i = firstWord; i <= lastWord; i++) {
+ // printf (" word %d: ", i);
+ // printWord (words->getRef (i));
+ // printf ("\n");
+ //}
+
+ int lineWidth;
+ if (lastWord >= firstWord) {
+ Word *lastWordOfLine = words->getRef(lastWord);
+ PRINTF (" words[%d]->totalWidth = %d\n", lastWord,
+ lastWordOfLine->totalWidth);
+ // Word::totalWidth includes the hyphen (which is what we want here).
+ lineWidth = lastWordOfLine->totalWidth;
+ } else
+ // empty line
+ lineWidth = 0;
+
// "lineWidth" is relative to leftOffset, so we may have to add
// "line1OffsetEff" (remember: this is, for list items, negative).
if (lines->size () == 0)
@@ -353,9 +353,6 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
accumulateWordExtremes (firstWord, lastWord, &maxOfMinWidth,
&sumOfMaxWidth);
- PRINTF (" words[%d]->totalWidth = %d\n", lastWord,
- lastWordOfLine->totalWidth);
-
PRINTF ("[%p] ##### LINE ADDED: %d, from %d to %d #####\n",
this, lines->size (), firstWord, lastWord);
@@ -379,6 +376,7 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
line->marginDescent = 0;
line->breakSpace = 0;
line->leftOffset = 0;
+ line->finished = false;
alignLine (lineIndex);
for (int i = line->firstWord; i < line->lastWord; i++) {
@@ -400,6 +398,19 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
for(int i = line->firstWord; i <= line->lastWord; i++)
accumulateWordForLine (lineIndex, i);
+ // Especially empty lines (possible when there are floats) have
+ // zero height, which may cause endless loops. For this reasons,
+ // the height should be positive.
+ line->boxAscent = misc::max (line->boxAscent, 1);
+
+ // Calculate offsetCompleteWidget, which includes also floats.
+ int leftBorder = mustBorderBeRegarded (line) ? newLineLeftBorder : 0;
+ line->offsetCompleteWidget =
+ misc::max (leftBorder,
+ getStyle()->boxOffsetX() + innerPadding
+ + (lineIndex == 0 ? line1OffsetEff : 0))
+ + line->leftOffset;
+
PRINTF (" line[%d].top = %d\n", lines->size () - 1, line->top);
PRINTF (" line[%d].boxAscent = %d\n", lines->size () - 1, line->boxAscent);
PRINTF (" line[%d].boxDescent = %d\n",
@@ -408,9 +419,10 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
line->contentAscent);
PRINTF (" line[%d].contentDescent = %d\n",
lines->size () - 1, line->contentDescent);
-
- PRINTF (" line[%d].maxLineWidth = %d\n",
- lines->size () - 1, line->maxLineWidth);
+ PRINTF (" line[%d].maxLineWidth = %d (lineWidth = %d)\n",
+ lines->size () - 1, line->maxLineWidth, lineWidth);
+ PRINTF (" line[%d].offsetCompleteWidget = %d\n",
+ lines->size () - 1, line->offsetCompleteWidget);
mustQueueResize = true;
@@ -421,7 +433,7 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
//words->getRef(line->lastWord)->badnessAndPenalty.print ();
//printf ("\n");
- int xWidget = lineXOffsetWidget(line);
+ int xWidget = line->offsetCompleteWidget;
for (int i = firstWord; i <= lastWord; i++) {
Word *word = words->getRef (i);
if (word->wordImgRenderer)
@@ -431,6 +443,10 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
xWidget += word->size.width + word->effSpace;
}
+ line->finished = true;
+
+ initNewLine ();
+
return line;
}
@@ -510,15 +526,12 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll)
PRINTF ("[%p] WORD_WRAP (%d, %s)\n",
this, wordIndex, wrapAll ? "true" : "false");
- Word *word;
- bool wordListChanged = false;
-
if (!wrapAll)
removeTemporaryLines ();
initLine1Offset (wordIndex);
- word = words->getRef (wordIndex);
+ Word *word = words->getRef (wordIndex);
word->effSpace = word->origSpace;
accumulateWordData (wordIndex);
@@ -527,16 +540,55 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll)
//printWord (word);
//printf ("\n");
+ switch (word->content.type) {
+ case core::Content::WIDGET_OOF_REF:
+ return wrapWordOofRef (wordIndex, wrapAll);
+ break;
+
+ default:
+ return wrapWordInFlow (wordIndex, wrapAll);
+ break;
+ }
+}
+
+bool Textblock::wrapWordInFlow (int wordIndex, bool wrapAll)
+{
+ Word *word = words->getRef (wordIndex);
+ bool wordListChanged = false;
+
int penaltyIndex = calcPenaltyIndexForNewLine ();
+ checkPossibleLineHeightChange (wordIndex);
+
bool newLine;
do {
+ // This variable, thereWillBeMoreSpace, is set to true, if, due
+ // to floats, this line is smaller than following lines will be
+ // (and, at the end, there will be surely lines without
+ // floats). If this is the case, lines may, in an extreme case,
+ // be left empty.
+
+ // (In other cases, lines are never left empty, even if this means
+ // that the contents is wider than the available witdh. Leaving
+ // lines empty does not make sense without floats, since there will
+ // be no possibility with more space anymore.)
+
+ bool thereWillBeMoreSpace =
+ mustBorderBeRegarded (lines->size ()) ?
+ newLineHasFloatLeft || newLineHasFloatRight :
+ false;
+
bool tempNewLine = false;
int firstIndex =
lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1;
int searchUntil;
- if (wrapAll && wordIndex >= firstIndex && wordIndex == words->size() -1) {
+ if (wordIndex < firstIndex)
+ // Current word is already part of a line (ending with
+ // firstIndex - 1), so no new line has to be added.
+ newLine = false;
+ else if (wrapAll && wordIndex >= firstIndex &&
+ wordIndex == words->size() -1) {
newLine = true;
searchUntil = wordIndex;
tempNewLine = true;
@@ -552,13 +604,21 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll)
// Break the line when too tight, but only when there is a
// possible break point so far. (TODO: I've forgotten the
// original bug which is fixed by this.)
+
+ // Exception of the latter rule: thereWillBeMoreSpace; see
+ // above, where it is defined.
+
bool possibleLineBreak = false;
- for (int i = firstIndex; !possibleLineBreak && i <= wordIndex - 1; i++)
+ for (int i = firstIndex;
+ !(thereWillBeMoreSpace || possibleLineBreak)
+ && i <= wordIndex - 1;
+ i++)
if (words->getRef(i)->badnessAndPenalty
.lineCanBeBroken (penaltyIndex))
possibleLineBreak = true;
- if (possibleLineBreak && word->badnessAndPenalty.lineTooTight ()) {
+ if ((thereWillBeMoreSpace || possibleLineBreak)
+ && word->badnessAndPenalty.lineTooTight ()) {
newLine = true;
searchUntil = wordIndex - 1;
PRINTF (" NEW LINE: line too tight\n");
@@ -575,65 +635,78 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll)
// newLine is calculated as "true".
mustQueueResize = true;
+ PRINTF ("[%p] special case? newLine = %s, wrapAll = %s => "
+ "mustQueueResize = %s\n", this, newLine ? "true" : "false",
+ wrapAll ? "true" : "false", mustQueueResize ? "true" : "false");
+
if(newLine) {
accumulateWordData (wordIndex);
int wordIndexEnd = wordIndex;
bool lineAdded;
do {
- int breakPos =
- searchMinBap (firstIndex, searchUntil, penaltyIndex, wrapAll);
- int hyphenatedWord = considerHyphenation (firstIndex, breakPos);
-
- //printf ("[%p] breakPos = %d (", this, breakPos);
- //printWordShort (words->getRef (breakPos));
- //printf ("), hyphenatedWord = %d", hyphenatedWord);
- //if (hyphenatedWord != -1) {
- // printf (" (");
- // printWordShort (words->getRef (hyphenatedWord));
- // printf (")");
- //}
- //printf ("\n");
-
- if(hyphenatedWord == -1) {
- addLine (firstIndex, breakPos, tempNewLine);
- PRINTF ("[%p] new line %d (%s), from %d to %d\n",
- this, lines->size() - 1,
- tempNewLine ? "temporally" : "permanently",
- firstIndex, breakPos);
+ if (firstIndex > searchUntil) {
+ // empty line
+ assert (searchUntil == firstIndex - 1);
+ addLine (firstIndex, firstIndex - 1, tempNewLine);
lineAdded = true;
penaltyIndex = calcPenaltyIndexForNewLine ();
} else {
- // TODO hyphenateWord() should return whether something has
- // changed at all. So that a second run, with
- // !word->canBeHyphenated, is unnecessary.
- // TODO Update: for this, searchUntil == 0 should be checked.
- PRINTF ("[%p] old searchUntil = %d ...\n", this, searchUntil);
- int n = hyphenateWord (hyphenatedWord);
- searchUntil += n;
- if (hyphenatedWord <= wordIndex)
- wordIndexEnd += n;
- PRINTF ("[%p] -> new searchUntil = %d ...\n", this, searchUntil);
- lineAdded = false;
+ int breakPos =
+ searchMinBap (firstIndex, searchUntil, penaltyIndex, wrapAll);
+ int hyphenatedWord = considerHyphenation (firstIndex, breakPos);
+
+ //printf ("[%p] breakPos = %d (", this, breakPos);
+ //printWordShort (words->getRef (breakPos));
+ //printf ("), hyphenatedWord = %d", hyphenatedWord);
+ //if (hyphenatedWord != -1) {
+ // printf (" (");
+ // printWordShort (words->getRef (hyphenatedWord));
+ // printf (")");
+ //}
+ //printf ("\n");
- // update word pointer as hyphenateWord() can trigger a
- // reorganization of the words structure
- word = words->getRef (wordIndex);
+ if(hyphenatedWord == -1) {
+ addLine (firstIndex, breakPos, tempNewLine);
+ PRINTF ("[%p] new line %d (%s), from %d to %d\n",
+ this, lines->size() - 1,
+ tempNewLine ? "temporally" : "permanently",
+ firstIndex, breakPos);
+ lineAdded = true;
+ penaltyIndex = calcPenaltyIndexForNewLine ();
+ } else {
+ // TODO hyphenateWord() should return whether
+ // something has changed at all. So that a second
+ // run, with !word->canBeHyphenated, is unnecessary.
+ // TODO Update: for this, searchUntil == 0 should be
+ // checked.
+ PRINTF ("[%p] old searchUntil = %d ...\n", this, searchUntil);
+ int n = hyphenateWord (hyphenatedWord);
+ searchUntil += n;
+ if (hyphenatedWord <= wordIndex)
+ wordIndexEnd += n;
+ PRINTF ("[%p] -> new searchUntil = %d ...\n",
+ this, searchUntil);
+ lineAdded = false;
+
+ // update word pointer as hyphenateWord() can trigger a
+ // reorganization of the words structure
+ word = words->getRef (wordIndex);
- if (n > 0 && hyphenatedWord <= wordIndex)
- wordListChanged = true;
+ if (n > 0 && hyphenatedWord <= wordIndex)
+ wordListChanged = true;
+ }
+
+ PRINTF ("[%p] accumulating again from %d to %d\n",
+ this, breakPos + 1, wordIndexEnd);
+ for(int i = breakPos + 1; i <= wordIndexEnd; i++)
+ accumulateWordData (i);
}
-
- PRINTF ("[%p] accumulating again from %d to %d\n",
- this, breakPos + 1, wordIndexEnd);
- for(int i = breakPos + 1; i <= wordIndexEnd; i++)
- accumulateWordData (i);
-
} while(!lineAdded);
}
} while (newLine);
- if(word->content.type == core::Content::WIDGET) {
+ if(word->content.type == core::Content::WIDGET_IN_FLOW) {
// Set parentRef for the child, when necessary.
//
// parentRef is set for the child already, when a line is
@@ -650,7 +723,8 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll)
firstWordWithoutLine = lines->getLastRef()->lastWord + 1;
if (wordIndex >= firstWordWithoutLine) {
- word->content.widget->parentRef = lines->size ();
+ word->content.widget->parentRef =
+ OutOfFlowMgr::createRefNormalFlow (lines->size ());
PRINTF ("The %s %p is assigned parentRef = %d.\n",
word->content.widget->getClassName(), word->content.widget,
word->content.widget->parentRef);
@@ -660,6 +734,87 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll)
return wordListChanged;
}
+/**
+ * Check wheather the newly wrapped word will change the height of the
+ * newly constructed line. If yes, the borders due to floats may
+ * change. (Line height is an argument to the calculation of borders.)
+ *
+ * Also update newLineAscent and newLineDescent with values of the new
+ * word.
+ */
+void Textblock::checkPossibleLineHeightChange (int wordIndex)
+{
+ Word *w = words->getRef (wordIndex);
+ bool heightIncreased =
+ containingBlock->outOfFlowMgr &&
+ (w->size.ascent > newLineAscent || w->size.descent >= newLineDescent);
+
+ newLineAscent = misc::max (newLineAscent, w->size.ascent);
+ newLineDescent = misc::max (newLineDescent, w->size.descent);
+
+ if (heightIncreased)
+ updateBorders (wordIndex, true, true);
+}
+
+bool Textblock::wrapWordOofRef (int wordIndex, bool wrapAll)
+{
+ assert (containingBlock->outOfFlowMgr);
+
+ int y = yOffsetOfPossiblyMissingLine (lines->size ());
+ Widget *widget = words->getRef(wordIndex)->content.widget;
+ containingBlock->outOfFlowMgr->tellPosition (widget, y);
+
+ // For better performance: Ignore OOF references which do not have
+ // an effect on borders (e. g. soon absolute positions); and also
+ // distinguish between left and right border.
+
+ bool left = containingBlock->outOfFlowMgr->affectsLeftBorder (widget);
+ bool right = containingBlock->outOfFlowMgr->affectsRightBorder (widget);
+ if (left || right)
+ updateBorders (wordIndex, left, right);
+
+ return false; // Actually, the words list is never changed here.
+}
+
+/**
+ * Recalculate borders (due to floats) for new line.
+ */
+void Textblock::updateBorders (int wordIndex, bool left, bool right)
+{
+ assert (left || right);
+
+ int y = yOffsetOfPossiblyMissingLine (lines->size ());
+ int h = heightOfPossiblyMissingLine (lines->size ());
+
+ if (left) {
+ newLineHasFloatLeft =
+ containingBlock->outOfFlowMgr->hasFloatLeft (this, y, h, this,
+ wordIndex);
+ newLineHasFloatRight =
+ containingBlock->outOfFlowMgr->hasFloatRight (this, y, h, this,
+ wordIndex);
+ }
+
+ if (right) {
+ newLineLeftBorder =
+ containingBlock->outOfFlowMgr->getLeftBorder (this, y, h, this,
+ wordIndex);
+ newLineRightBorder =
+ containingBlock->outOfFlowMgr->getRightBorder (this, y, h, this,
+ wordIndex);
+ }
+
+ int firstIndex =
+ lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1;
+
+ // Notice, that firstIndex may be larger than wordIndex, due to
+ // hyphenation. This means, that the word with the index
+ // wordIndex is already part of a line. Nothing to do then.
+
+ for (int i = firstIndex; i <= wordIndex; i++)
+ accumulateWordData (i);
+}
+
int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex,
bool correctAtEnd)
{
@@ -763,7 +918,6 @@ bool Textblock::isHyphenationCandidate (Word *word)
Hyphenator::isHyphenationCandidate (word->content.text);
}
-
/**
* Counter part to wordWrap(), but for extremes, not size calculation.
*/
@@ -886,6 +1040,10 @@ int Textblock::hyphenateWord (int wordIndex)
initWord (wordIndex + i);
PRINTF ("[%p] ... => %d words\n", this, words->size ());
+ if (containingBlock->outOfFlowMgr)
+ containingBlock->outOfFlowMgr->moveExternalIndices (this, wordIndex,
+ numBreaks);
+
// Adjust anchor indexes.
for (int i = 0; i < anchors->size (); i++) {
Anchor *anchor = anchors->getRef (i);
@@ -941,13 +1099,20 @@ int Textblock::hyphenateWord (int wordIndex)
}
}
- accumulateWordData (wordIndex + i);
-
//printf ("[%p] %d: hyphenated word part: ", this, wordIndex + i);
//printWordWithFlags (w);
//printf ("\n");
}
+ // AccumulateWordData() will calculate the width, which depends
+ // on the borders (possibly limited by floats), which depends on
+ // the widgeds so far. For this reason, it is important to first
+ // make all words consistent before calling
+ // accumulateWordData(); therefore the second loop.
+
+ for (int i = 0; i < numBreaks + 1; i++)
+ accumulateWordData (wordIndex + i);
+
PRINTF (" finished\n");
//delete origword->content.text; TODO: Via textZone?
@@ -966,8 +1131,12 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
Line *line = lines->getRef (lineIndex);
Word *word = words->getRef (wordIndex);
- PRINTF (" %d + %d / %d + %d\n", line->boxAscent, line->boxDescent,
+ PRINTF ("[%p] ACCUMULATE_WORD_FOR_LINE (%d, %d): %d + %d / %d + %d\n",
+ this, lineIndex, wordIndex, line->boxAscent, line->boxDescent,
word->size.ascent, word->size.descent);
+ //printf (" ");
+ //printWord (word);
+ //printf ("\n");
line->boxAscent = misc::max (line->boxAscent, word->size.ascent);
line->boxDescent = misc::max (line->boxDescent, word->size.descent);
@@ -982,7 +1151,7 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
len += word->style->font->ascent / 3;
line->contentDescent = misc::max (line->contentDescent, len);
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
int collapseMarginTop = 0;
line->marginDescent =
@@ -1006,7 +1175,8 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
+ word->content.widget->getStyle()->margin.top
- collapseMarginTop);
- word->content.widget->parentRef = lineIndex;
+ word->content.widget->parentRef =
+ OutOfFlowMgr::createRefNormalFlow (lineIndex);
} else {
line->marginDescent =
misc::max (line->marginDescent, line->boxDescent);
@@ -1080,18 +1250,35 @@ void Textblock::accumulateWordData (int wordIndex)
int Textblock::calcAvailWidth (int lineIndex)
{
- int availWidth =
- this->availWidth - getStyle()->boxDiffWidth() - innerPadding;
+ PRINTF ("[%p] CALC_AVAIL_WIDTH (%d of %d) ...\n",
+ this, lineIndex, lines->size());
+
+ int availWidth = this->availWidth - innerPadding;
if (limitTextWidth &&
layout->getUsesViewport () &&
- availWidth > layout->getWidthViewport () - 10)
+ // margin/border/padding will be subtracted later, via OOFM.
+ availWidth - getStyle()->boxDiffWidth()
+ > layout->getWidthViewport () - 10)
availWidth = layout->getWidthViewport () - 10;
if (lineIndex == 0)
availWidth -= line1OffsetEff;
- //PRINTF("[%p] CALC_AVAIL_WIDTH => %d - %d - %d = %d\n",
- // this, this->availWidth, getStyle()->boxDiffWidth(), innerPadding,
- // availWidth);
+ int leftBorder, rightBorder;
+ if (mustBorderBeRegarded (lineIndex)) {
+ leftBorder = newLineLeftBorder;
+ rightBorder = newLineRightBorder;
+ } else
+ leftBorder = rightBorder = 0;
+
+ leftBorder = misc::max (leftBorder, getStyle()->boxOffsetX());
+ rightBorder = misc::max (rightBorder, getStyle()->boxRestWidth());
+
+ availWidth -= (leftBorder + rightBorder);
+
+ PRINTF ("[%p] CALC_AVAIL_WIDTH (%d of %d) => %d - %d - (%d + %d)"
+ " = %d\n", this, lineIndex, lines->size(), this->availWidth,
+ innerPadding, leftBorder, rightBorder,
+ availWidth);
return availWidth;
}
@@ -1108,7 +1295,7 @@ void Textblock::initLine1Offset (int wordIndex)
} else {
int indent = 0;
- if (word->content.type == core::Content::WIDGET &&
+ if (word->content.type == core::Content::WIDGET_IN_FLOW &&
word->content.widget->blockLevel() == true) {
/* don't use text-indent when nesting blocks */
} else {
@@ -1180,7 +1367,7 @@ void Textblock::alignLine (int lineIndex)
*/
void Textblock::rewrap ()
{
- PRINTF ("[%p] REWRAP: wrapRef = %d\n", this, wrapRef);
+ PRINTF ("[%p] REWRAP: wrapRefLines = %d\n", this, wrapRefLines);
if (wrapRefLines == -1)
/* page does not have to be rewrapped */
@@ -1191,16 +1378,21 @@ void Textblock::rewrap ()
lines->setSize (wrapRefLines);
nonTemporaryLines = misc::min (nonTemporaryLines, wrapRefLines);
+ initNewLine ();
+
int firstWord;
- if (lines->size () > 0)
- firstWord = lines->getLastRef()->lastWord + 1;
- else
+ if (lines->size () > 0) {
+ Line *lastLine = lines->getLastRef();
+ firstWord = lastLine->lastWord + 1;
+ } else
firstWord = 0;
+ PRINTF (" starting with word %d\n", firstWord);
+
for (int i = firstWord; i < words->size (); i++) {
Word *word = words->getRef (i);
-
- if (word->content.type == core::Content::WIDGET)
+
+ if (word->content.type == core::Content::WIDGET_IN_FLOW)
calcWidgetSize (word->content.widget, &word->size);
wordWrap (i, false);
@@ -1266,6 +1458,49 @@ void Textblock::fillParagraphs ()
wrapRefParagraphs = -1;
}
+void Textblock::initNewLine ()
+{
+ // At the very beginning, in Textblock::Textblock, where this
+ // method is called, containingBlock is not yet defined.
+
+ if (containingBlock && containingBlock->outOfFlowMgr) {
+ if (lines->size () == 0) {
+ int clearPosition =
+ containingBlock->outOfFlowMgr->getClearPosition (this);
+ setVerticalOffset (misc::max (clearPosition, 0));
+ }
+
+ int y = yOffsetOfPossiblyMissingLine (lines->size ());
+ int h = heightOfPossiblyMissingLine (lines->size ());
+ int lastRef = lines->size() > 0 ? lines->getLastRef()->lastWord : -1;
+
+ newLineHasFloatLeft =
+ containingBlock->outOfFlowMgr->hasFloatLeft (this, y, h, this,
+ lastRef);
+ newLineHasFloatRight =
+ containingBlock->outOfFlowMgr->hasFloatRight (this, y, h, this,
+ lastRef);
+ newLineLeftBorder =
+ containingBlock->outOfFlowMgr->getLeftBorder (this, y, h, this,
+ lastRef);
+ newLineRightBorder =
+ containingBlock->outOfFlowMgr->getRightBorder (this, y, h, this,
+ lastRef);
+
+ PRINTF ("[%p] INIT_NEW_LINE: %d (%s) / %d (%s), at %d (%d), until %d\n",
+ this, newLineLeftBorder, newLineHasFloatLeft ? "true" : "false",
+ newLineRightBorder, newLineHasFloatRight ? "true" : "false",
+ y, h, lastRef);
+ } else {
+ newLineHasFloatLeft = newLineHasFloatRight = false;
+ newLineLeftBorder = newLineRightBorder = 0;
+
+ PRINTF ("[%p] INIT_NEW_LINE: no CB or OOFM\n", this);
+ }
+
+ newLineAscent = newLineDescent = 0;
+}
+
void Textblock::showMissingLines ()
{
int firstWordToWrap = lines->size () > 0 ?
diff --git a/dw/types.cc b/dw/types.cc
index 86836bc1..56af66d1 100644
--- a/dw/types.cc
+++ b/dw/types.cc
@@ -268,5 +268,90 @@ void Region::addRectangle (Rectangle *rPointer)
rectangleList->append (r);
}
+Content::Type Content::maskForSelection (bool followReferences)
+{
+ Content::Type widgetMask = (Content::Type)
+ (Content::WIDGET_IN_FLOW |
+ (followReferences ? Content::WIDGET_OOF_REF : Content::WIDGET_OOF_CONT));
+ return (Content::Type)(Content::SELECTION_CONTENT | widgetMask);
+}
+
+void Content::intoStringBuffer(Content *content, misc::StringBuffer *sb)
+{
+ switch(content->type) {
+ case START:
+ sb->append ("<start>");
+ break;
+ case END:
+ sb->append ("<end>");
+ break;
+ case TEXT:
+ sb->append ("\"");
+ sb->append (content->text);
+ sb->append ("\"");
+ break;
+ case WIDGET_IN_FLOW:
+ sb->append ("<widget in flow: ");
+ sb->appendPointer (content->widget);
+ sb->append (" (");
+ sb->append (content->widget->getClassName());
+ sb->append (")>");
+ break;
+ case WIDGET_OOF_REF:
+ sb->append ("<widget oof ref: ");
+ sb->appendPointer (content->widget);
+ sb->append (" (");
+ sb->append (content->widget->getClassName());
+ sb->append (")>");
+ break;
+ case WIDGET_OOF_CONT:
+ sb->append ("<widget oof cont: ");
+ sb->appendPointer (content->widget);
+ sb->append (" (");
+ sb->append (content->widget->getClassName());
+ sb->append (")>");
+ break;
+ case BREAK:
+ sb->append ("<break>");
+ break;
+ default:
+ sb->append ("<");
+ sb->appendInt (content->type);
+ sb->append ("?>");
+ break;
+ }
+}
+
+void Content::maskIntoStringBuffer(Type mask, misc::StringBuffer *sb)
+{
+ sb->append ((mask & START) ? "st" : "--");
+ sb->append (":");
+ sb->append ((mask & END) ? "en" : "--");
+ sb->append (":");
+ sb->append ((mask & TEXT) ? "tx" : "--");
+ sb->append (":");
+ sb->append ((mask & WIDGET_IN_FLOW) ? "wf" : "--");
+ sb->append (":");
+ sb->append ((mask & WIDGET_OOF_REF) ? "Wr" : "--");
+ sb->append (":");
+ sb->append ((mask & WIDGET_OOF_CONT) ? "Wc" : "--");
+ sb->append (":");
+ sb->append ((mask & BREAK) ? "br" : "--");
+}
+
+void Content::print (Content *content)
+{
+ misc::StringBuffer sb;
+ intoStringBuffer (content, &sb);
+ printf ("%s", sb.getChars ());
+}
+
+void Content::printMask (Type mask)
+{
+ misc::StringBuffer sb;
+ maskIntoStringBuffer (mask, &sb);
+ printf ("%s", sb.getChars ());
+}
+
} // namespace core
} // namespace dw
diff --git a/dw/types.hh b/dw/types.hh
index f04fc138..e910d296 100644
--- a/dw/types.hh
+++ b/dw/types.hh
@@ -188,11 +188,27 @@ struct Content
START = 1 << 0,
END = 1 << 1,
TEXT = 1 << 2,
- WIDGET = 1 << 3,
- BREAK = 1 << 4,
+
+ /** \brief widget in normal flow, so that _this_ widget
+ (containing this content) is both container (parent) and
+ generator */
+ WIDGET_IN_FLOW = 1 << 3,
+
+ /** \brief widget out of flow (OOF); _this_ widget (containing
+ this content) is only the container (parent), but _not_
+ generator */
+ WIDGET_OOF_CONT = 1 << 4,
+
+ /** \brief reference to a widget out of flow (OOF); _this_
+ widget (containing this content) is only the generator
+ (parent), but _not_ container */
+ WIDGET_OOF_REF = 1 << 5,
+ BREAK = 1 << 6,
+
ALL = 0xff,
REAL_CONTENT = 0xff ^ (START | END),
- SELECTION_CONTENT = TEXT | WIDGET | BREAK
+ SELECTION_CONTENT = TEXT | BREAK, // WIDGET_* must be set additionally
+ ANY_WIDGET = WIDGET_IN_FLOW | WIDGET_OOF_CONT | WIDGET_OOF_REF,
};
/* Content is embedded in struct Word therefore we
@@ -205,6 +221,13 @@ struct Content
Widget *widget;
int breakSpace;
};
+
+ static Content::Type maskForSelection (bool followReferences);
+
+ static void intoStringBuffer(Content *content, lout::misc::StringBuffer *sb);
+ static void maskIntoStringBuffer(Type mask, lout::misc::StringBuffer *sb);
+ static void print (Content *content);
+ static void printMask (Type mask);
};
} // namespace core
diff --git a/dw/ui.cc b/dw/ui.cc
index 8963d3bf..17cf2288 100644
--- a/dw/ui.cc
+++ b/dw/ui.cc
@@ -270,7 +270,7 @@ void ComplexButtonResource::LayoutReceiver::canvasSizeChanged (int width,
/**
* \todo Verify that this is correct.
*/
- resource->queueResize (resource->childWidget->extremesChanged ());
+ resource->queueResize (resource->childWidget->extremesQueued ());
}
ComplexButtonResource::ComplexButtonResource ()
diff --git a/dw/widget.cc b/dw/widget.cc
index b09c51e9..83b9956a 100644
--- a/dw/widget.cc
+++ b/dw/widget.cc
@@ -70,7 +70,7 @@ Widget::Widget ()
registerName ("dw::core::Widget", &CLASS_ID);
flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS);
- parent = NULL;
+ parent = generator = NULL;
layout = NULL;
allocation.x = -1;
@@ -148,15 +148,17 @@ void Widget::setParent (Widget *parent)
buttonSensitive = parent->buttonSensitive;
DBG_OBJ_ASSOC_PARENT (parent);
-
//printf ("The %s %p becomes a child of the %s %p\n",
// getClassName(), this, parent->getClassName(), parent);
+
+ notifySetParent();
}
void Widget::queueDrawArea (int x, int y, int width, int height)
{
/** \todo Maybe only the intersection? */
- layout->queueDraw (x + allocation.x, y + allocation.y, width, height);
+ if (layout)
+ layout->queueDraw (x + allocation.x, y + allocation.y, width, height);
_MSG("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height);
}
@@ -165,40 +167,64 @@ void Widget::queueDrawArea (int x, int y, int width, int height)
*/
void Widget::queueResize (int ref, bool extremesChanged)
{
+ assert (!queueResizeEntered ());
+
+ enterQueueResize ();
+
Widget *widget2, *child;
- //printf("The %stop-level %s %p with parentRef = %d has changed its size.\n",
- // parent ? "non-" : "", getClassName(), this, parentRef);
+ //printf("The %stop-level %s %p with parentRef = %d has changed its size. "
+ // "Layout = %p.\n",
+ // parent ? "non-" : "", getClassName(), this, parentRef, layout);
- setFlags (NEEDS_RESIZE);
- setFlags (NEEDS_ALLOCATE);
- markSizeChange (ref);
+ Flags resizeFlag, extremesFlag;
+
+ if (layout) {
+ // If RESIZE_QUEUED is set, this widget is already in the list.
+ if (!resizeQueued ())
+ layout->queueResizeList->put (this);
+
+ resizeFlag = RESIZE_QUEUED;
+ extremesFlag = EXTREMES_QUEUED;
+ } else {
+ resizeFlag = NEEDS_RESIZE;
+ extremesFlag = EXTREMES_CHANGED;
+ }
+ setFlags (resizeFlag);
+ setFlags (ALLOCATE_QUEUED);
+ markSizeChange (ref);
+
if (extremesChanged) {
- setFlags (EXTREMES_CHANGED);
+ setFlags (extremesFlag);
markExtremesChange (ref);
}
-
- for (widget2 = parent, child = this;
- widget2;
- child = widget2, widget2 = widget2->parent) {
- widget2->setFlags (NEEDS_RESIZE);
- widget2->markSizeChange (child->parentRef);
- widget2->setFlags (NEEDS_ALLOCATE);
-
- //printf (" Setting DW_NEEDS_RESIZE and NEEDS_ALLOCATE for the "
+
+ for (widget2 = parent, child = this; widget2;
+ child = widget2, widget2 = widget2->parent) {
+ //printf (" Setting %s and ALLOCATE_QUEUED for the "
// "%stop-level %s %p with parentRef = %d\n",
+ // resizeFlag == RESIZE_QUEUED ? "RESIZE_QUEUED" : "NEEDS_RESIZE",
// widget2->parent ? "non-" : "", widget2->getClassName(), widget2,
// widget2->parentRef);
+ if (layout && !widget2->resizeQueued ())
+ layout->queueResizeList->put (widget2);
+
+ widget2->setFlags (resizeFlag);
+ widget2->markSizeChange (child->parentRef);
+ widget2->setFlags (ALLOCATE_QUEUED);
+
if (extremesChanged) {
- widget2->setFlags (EXTREMES_CHANGED);
+ widget2->setFlags (extremesFlag);
widget2->markExtremesChange (child->parentRef);
}
}
if (layout)
layout->queueResize ();
+
+ leaveQueueResize ();
}
@@ -208,6 +234,25 @@ void Widget::queueResize (int ref, bool extremesChanged)
*/
void Widget::sizeRequest (Requisition *requisition)
{
+ assert (!queueResizeEntered ());
+
+ enterSizeRequest ();
+
+ //printf ("The %stop-level %s %p with parentRef = %d: needsResize: %s, "
+ // "resizeQueued = %s\n",
+ // parent ? "non-" : "", getClassName(), this, parentRef,
+ // needsResize () ? "true" : "false",
+ // resizeQueued () ? "true" : "false");
+
+ if (resizeQueued ()) {
+ // This method is called outside of Layout::resizeIdle.
+ setFlags (NEEDS_RESIZE);
+ unsetFlags (RESIZE_QUEUED);
+ // The widget is not taken out of Layout::queueResizeList, since
+ // other *_QUEUED flags may still be set and processed in
+ // Layout::resizeIdle.
+ }
+
if (needsResize ()) {
/** \todo Check requisition == &(this->requisition) and do what? */
sizeRequestImpl (requisition);
@@ -219,6 +264,11 @@ void Widget::sizeRequest (Requisition *requisition)
DBG_OBJ_SET_NUM ("requisition.descent", requisition->descent);
} else
*requisition = this->requisition;
+
+ //printf (" ==> Result: %d x (%d + %d)\n",
+ // requisition->width, requisition->ascent, requisition->descent);
+
+ leaveSizeRequest ();
}
/**
@@ -226,6 +276,19 @@ void Widget::sizeRequest (Requisition *requisition)
*/
void Widget::getExtremes (Extremes *extremes)
{
+ assert (!queueResizeEntered ());
+
+ enterGetExtremes ();
+
+ if (extremesQueued ()) {
+ // This method is called outside of Layout::resizeIdle.
+ setFlags (EXTREMES_CHANGED);
+ unsetFlags (EXTREMES_QUEUED);
+ // The widget is not taken out of Layout::queueResizeList, since
+ // other *_QUEUED flags may still be set and processed in
+ // Layout::resizeIdle.
+ }
+
if (extremesChanged ()) {
getExtremesImpl (extremes);
this->extremes = *extremes;
@@ -235,6 +298,8 @@ void Widget::getExtremes (Extremes *extremes)
DBG_OBJ_SET_NUM ("extremes.maxWidth", extremes->maxWidth);
} else
*extremes = this->extremes;
+
+ leaveGetExtremes ();
}
/**
@@ -243,6 +308,23 @@ void Widget::getExtremes (Extremes *extremes)
*/
void Widget::sizeAllocate (Allocation *allocation)
{
+ assert (!queueResizeEntered ());
+ assert (!sizeRequestEntered ());
+ assert (!getExtremesEntered ());
+ assert (resizeIdleEntered ());
+
+ enterSizeAllocate ();
+
+ /*printf ("The %stop-level %s %p is allocated:\n",
+ parent ? "non-" : "", getClassName(), this);
+ printf (" old = (%d, %d, %d + (%d + %d))\n",
+ this->allocation.x, this->allocation.y, this->allocation.width,
+ this->allocation.ascent, this->allocation.descent);
+ printf (" new = (%d, %d, %d + (%d + %d))\n",
+ allocation->x, allocation->y, allocation->width, allocation->ascent,
+ allocation->descent);
+ printf (" NEEDS_ALLOCATE = %s\n", needsAllocate () ? "true" : "false");*/
+
if (needsAllocate () ||
allocation->x != this->allocation.x ||
allocation->y != this->allocation.y ||
@@ -283,6 +365,8 @@ void Widget::sizeAllocate (Allocation *allocation)
}
/*unsetFlags (NEEDS_RESIZE);*/
+
+ leaveSizeAllocate ();
}
bool Widget::buttonPress (EventButton *event)
@@ -503,6 +587,25 @@ int Widget::getLevel ()
}
/**
+ * \brief Get the level of the widget within the tree, regarting the
+ * generators, not the parents.
+ *
+ * The root widget has the level 0.
+ */
+int Widget::getGeneratorLevel ()
+{
+ Widget *widget = this;
+ int level = 0;
+
+ while (widget->getGenerator ()) {
+ level++;
+ widget = widget->getGenerator ();
+ }
+
+ return level;
+}
+
+/**
* \brief Get the widget with the highest level, which is a direct ancestor of
* widget1 and widget2.
*/
@@ -562,7 +665,9 @@ Widget *Widget::getWidgetAtPoint (int x, int y, int level)
* is such a child, it is returned. Otherwise, this widget is returned.
*/
childAtPoint = NULL;
- it = iterator (Content::WIDGET, false);
+ it = iterator ((Content::Type)
+ (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT),
+ false);
while (childAtPoint == NULL && it->next ())
childAtPoint = it->getContent()->widget->getWidgetAtPoint (x, y,
@@ -623,6 +728,25 @@ void Widget::markExtremesChange (int ref)
{
}
+/**
+ * \brief This method is called after a widget has been set as the top of a
+ * widget tree.
+ *
+ * A widget may override this method when it is necessary to be notified.
+ */
+void Widget::notifySetAsTopLevel()
+{
+}
+
+/**
+ * \brief This method is called after a widget has been added to a parent.
+ *
+ * A widget may override this method when it is necessary to be notified.
+ */
+void Widget::notifySetParent()
+{
+}
+
void Widget::setWidth (int width)
{
}
diff --git a/dw/widget.hh b/dw/widget.hh
index 34b35efa..c9aeb427 100644
--- a/dw/widget.hh
+++ b/dw/widget.hh
@@ -27,23 +27,46 @@ class Widget: public lout::identity::IdentifiableObject
protected:
enum Flags {
/**
+ * \todo Comment this.
+ */
+ RESIZE_QUEUED = 1 << 0,
+
+ /**
+ * \todo Comment this.
+ */
+ EXTREMES_QUEUED = 1 << 1,
+
+ /**
* \brief Set, when dw::core::Widget::requisition is not up to date
* anymore.
+ *
+ * \todo Update, see RESIZE_QUEUED.
*/
- NEEDS_RESIZE = 1 << 0,
+ NEEDS_RESIZE = 1 << 2,
/**
* \brief Only used internally, set to enforce size allocation.
*
- * (I've forgotten the case, for which this is necessary.)
+ * In some cases, the size of a widget remains the same, but the
+ * children are allocated at different positions and in
+ * different sizes, so that a simple comparison of old and new
+ * allocation is insufficient. Therefore, this flag is set
+ * (indirectly, as ALLOCATE_QUEUED) in queueResize.
+ */
+ NEEDS_ALLOCATE = 1 << 3,
+
+ /**
+ * \todo Comment this.
*/
- NEEDS_ALLOCATE = 1 << 1,
+ ALLOCATE_QUEUED = 1 << 4,
/**
* \brief Set, when dw::core::Widget::extremes is not up to date
* anymore.
+ *
+ * \todo Update, see RESIZE_QUEUED.
*/
- EXTREMES_CHANGED = 1 << 2,
+ EXTREMES_CHANGED = 1 << 5,
/**
* \brief Set by the widget itself (in the constructor), when set...
@@ -51,7 +74,7 @@ protected:
*
* Will hopefully be removed, after redesigning the size model.
*/
- USES_HINTS = 1 << 3,
+ USES_HINTS = 1 << 6,
/**
* \brief Set by the widget itself (in the constructor), when it contains
@@ -59,19 +82,19 @@ protected:
*
* Will hopefully be removed, after redesigning the size model.
*/
- HAS_CONTENTS = 1 << 4,
+ HAS_CONTENTS = 1 << 7,
/**
* \brief Set, when a widget was already once allocated,
*
* The dw::Image widget uses this flag, see dw::Image::setBuffer.
*/
- WAS_ALLOCATED = 1 << 5,
+ WAS_ALLOCATED = 1 << 8,
/**
* \brief Set for block-level widgets (as opposed to inline widgets)
*/
- BLOCK_LEVEL = 1 << 6,
+ BLOCK_LEVEL = 1 << 9,
};
/**
@@ -101,6 +124,14 @@ private:
* \brief The parent widget, NULL for top-level widgets.
*/
Widget *parent;
+
+ /**
+ * \brief The generating widget, NULL for top-level widgets, or if
+ * not set; in the latter case, the effective generator (see
+ * getGenerator) is the parent.
+ */
+ Widget *generator;
+
style::Style *style;
Flags flags;
@@ -200,6 +231,9 @@ protected:
*/
virtual void markExtremesChange (int ref);
+ virtual void notifySetAsTopLevel();
+ virtual void notifySetParent();
+
virtual bool buttonPressImpl (EventButton *event);
virtual bool buttonReleaseImpl (EventButton *event);
virtual bool motionNotifyImpl (EventMotion *event);
@@ -249,14 +283,37 @@ public:
inline void setDeleteCallback(DW_Callback_t func, void *data)
{ deleteCallbackFunc = func; deleteCallbackData = data; }
+private:
+ bool resizeIdleEntered () { return layout && layout->resizeIdleCounter; }
+
+ void enterQueueResize () { if (layout) layout->queueResizeCounter++; }
+ void leaveQueueResize () { if (layout) layout->queueResizeCounter--; }
+ bool queueResizeEntered () { return layout && layout->queueResizeCounter; }
+
+ void enterSizeAllocate () { if (layout) layout->sizeAllocateCounter++; }
+ void leaveSizeAllocate () { if (layout) layout->sizeAllocateCounter--; }
+ bool sizeAllocateEntered () { return layout && layout->sizeAllocateCounter; }
+
+ void enterSizeRequest () { if (layout) layout->sizeRequestCounter++; }
+ void leaveSizeRequest () { if (layout) layout->sizeRequestCounter--; }
+ bool sizeRequestEntered () { return layout && layout->sizeRequestCounter; }
+
+ void enterGetExtremes () { if (layout) layout->getExtremesCounter++; }
+ void leaveGetExtremes () { if (layout) layout->getExtremesCounter--; }
+ bool getExtremesEntered () { return layout && layout->getExtremesCounter; }
+
+
public:
static int CLASS_ID;
Widget ();
~Widget ();
+ inline bool resizeQueued () { return flags & RESIZE_QUEUED; }
+ inline bool extremesQueued () { return flags & EXTREMES_QUEUED; }
inline bool needsResize () { return flags & NEEDS_RESIZE; }
inline bool needsAllocate () { return flags & NEEDS_ALLOCATE; }
+ inline bool allocateQueued () { return flags & ALLOCATE_QUEUED; }
inline bool extremesChanged () { return flags & EXTREMES_CHANGED; }
inline bool wasAllocated () { return flags & WAS_ALLOCATED; }
inline bool usesHints () { return flags & USES_HINTS; }
@@ -265,6 +322,8 @@ public:
void setParent (Widget *parent);
+ void setGenerator (Widget *generator) { this->generator = generator; }
+
inline style::Style *getStyle () { return style; }
/** \todo I do not like this. */
inline Allocation *getAllocation () { return &allocation; }
@@ -302,8 +361,11 @@ public:
inline Widget *getParent () { return parent; }
Widget *getTopLevel ();
int getLevel ();
+ int getGeneratorLevel ();
Widget *getNearestCommonAncestor (Widget *otherWidget);
+ inline Widget *getGenerator () { return generator ? generator : parent; }
+
inline Layout *getLayout () { return layout; }
virtual Widget *getWidgetAtPoint (int x, int y, int level);
diff --git a/lout/container.cc b/lout/container.cc
index de36a6f7..908df4ae 100644
--- a/lout/container.cc
+++ b/lout/container.cc
@@ -188,9 +188,10 @@ void Vector::remove(int pos)
/**
* Sort the elements in the vector. Assumes that all elements are Comparable's.
*/
-void Vector::sort()
+void Vector::sort(Comparator *comparator)
{
- qsort (array, numElements, sizeof(Object*), Comparable::compareFun);
+ Comparator::compareFunComparator = comparator;
+ qsort (array, numElements, sizeof(Object*), Comparator::compareFun);
}
/**
@@ -202,18 +203,20 @@ void Vector::sort()
* size of the array. (This is the value which can be used for
* insertion; see insertSortet()).
*/
-int Vector::bsearch(Object *key, bool mustExist)
+int Vector::bsearch(Object *key, bool mustExist, int start, int end,
+ Comparator *comparator)
{
// The case !mustExist is not handled by bsearch(3), so here is a
// new implementation.
- if (numElements == 0)
- return mustExist ? -1 : 0;
+
+ if (start > end)
+ return mustExist ? -1 : start;
- int high = numElements - 1, low = 0;
+ int low = start, high = end;
while (true) {
int index = (low + high) / 2;
- int c = ((Comparable*) key)->compareTo ((Comparable*)array[index]);
+ int c = comparator->compare (key, array[index]);
if (c == 0)
return index;
else {
@@ -233,6 +236,7 @@ int Vector::bsearch(Object *key, bool mustExist)
/*
+ Comparator::compareFunComparator = comparator;
void *result = ::bsearch (&key, array, numElements, sizeof (Object*),
Comparable::compareFun);
if (result)
diff --git a/lout/container.hh b/lout/container.hh
index c87eb10c..9180b9e0 100644
--- a/lout/container.hh
+++ b/lout/container.hh
@@ -137,16 +137,23 @@ public:
* Notice that insertion is not very efficient, unless the position
* is rather at the end.
*/
- inline void insertSorted(object::Object *newElement)
- { insert (newElement, bsearch (newElement, false)); }
+ inline void insertSorted(object::Object *newElement,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { insert (newElement, bsearch (newElement, false, comparator)); }
void remove(int pos);
inline object::Object *get(int pos)
{ return (pos >= 0 && pos < numElements) ? array[pos] : NULL; }
inline int size() { return numElements; }
void clear();
- void sort();
- int bsearch(Object *key, bool mustExist);
+ void sort(object::Comparator *comparator = &object::standardComparator);
+ int bsearch(Object *key, bool mustExist, int start, int end,
+ object::Comparator *comparator = &object::standardComparator);
+ inline int bsearch(Object *key, bool mustExist,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { return bsearch (key, mustExist, 0, size () - 1, comparator); }
};
@@ -406,16 +413,28 @@ public:
{ ((untyped::Vector*)this->base)->put(newElement, newPos); }
inline void insert(T *newElement, int pos)
{ ((untyped::Vector*)this->base)->insert(newElement, pos); }
- inline void insertSorted(T *newElement)
- { ((untyped::Vector*)this->base)->insertSorted(newElement); }
+ inline void insertSorted(T *newElement,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { ((untyped::Vector*)this->base)->insertSorted(newElement, comparator); }
inline void remove(int pos) { ((untyped::Vector*)this->base)->remove(pos); }
inline T *get(int pos)
{ return (T*)((untyped::Vector*)this->base)->get(pos); }
inline int size() { return ((untyped::Vector*)this->base)->size(); }
inline void clear() { ((untyped::Vector*)this->base)->clear(); }
- inline void sort() { ((untyped::Vector*)this->base)->sort(); }
- inline int bsearch(T *key, bool mustExist)
- { return ((untyped::Vector*)this->base)->bsearch(key, mustExist); }
+ inline void sort(object::Comparator *comparator =
+ &object::standardComparator)
+ { ((untyped::Vector*)this->base)->sort(comparator); }
+ inline int bsearch(T *key, bool mustExist, int start, int end,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { return ((untyped::Vector*)this->base)->bsearch(key, mustExist, start, end,
+ comparator); }
+ inline int bsearch(T *key, bool mustExist,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { return ((untyped::Vector*)this->base)->bsearch(key, mustExist,
+ comparator); }
};
diff --git a/lout/identity.cc b/lout/identity.cc
index 6fe679b4..61f59ace 100644
--- a/lout/identity.cc
+++ b/lout/identity.cc
@@ -17,8 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-
#include "identity.hh"
#include <stdio.h>
@@ -41,6 +39,22 @@ IdentifiableObject::Class::Class (IdentifiableObject::Class *parent, int id,
this->className = className;
}
+void IdentifiableObject::Class::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append ("<class ");
+ sb->append (className);
+ sb->append (" (");
+ sb->appendInt (id);
+ sb->append (")");
+
+ if (parent) {
+ sb->append (", parent: ");
+ parent->intoStringBuffer (sb);
+ }
+
+ sb->append (">");
+}
+
HashTable <ConstString, IdentifiableObject::Class>
*IdentifiableObject::classesByName =
new HashTable<ConstString, IdentifiableObject::Class> (true, true);
@@ -55,7 +69,9 @@ IdentifiableObject::IdentifiableObject ()
void IdentifiableObject::intoStringBuffer(misc::StringBuffer *sb)
{
- sb->append("<instance of ");
+ sb->append("<instance ");
+ sb->appendPointer(this);
+ sb->append(" of ");
sb->append(getClassName());
sb->append(">");
}
@@ -78,6 +94,7 @@ void IdentifiableObject::registerName (const char *className, int *classId)
}
this->classId = klass->id;
+ *classId = klass->id;
currentlyConstructedClass = klass;
}
diff --git a/lout/identity.hh b/lout/identity.hh
index 1f0b4bdf..df42b204 100644
--- a/lout/identity.hh
+++ b/lout/identity.hh
@@ -106,6 +106,8 @@ private:
const char *className;
Class (Class *parent, int id, const char *className);
+
+ void intoStringBuffer(misc::StringBuffer *sb);
};
static container::typed::HashTable <object::ConstString,
@@ -121,7 +123,7 @@ protected:
public:
IdentifiableObject ();
- virtual void intoStringBuffer(misc::StringBuffer *sb);
+ void intoStringBuffer(misc::StringBuffer *sb);
/**
* \brief Returns the class identifier.
diff --git a/lout/misc.hh b/lout/misc.hh
index cff8e05b..2ed5e1b0 100644
--- a/lout/misc.hh
+++ b/lout/misc.hh
@@ -515,6 +515,11 @@ public:
* about memory management.
*/
inline void append(const char *str) { appendNoCopy(strdup(str)); }
+ inline void appendInt(int n)
+ { char buf[32]; sprintf (buf, "%d", n); append (buf); }
+ inline void appendPointer(void *p)
+ { char buf[32]; sprintf (buf, "%p", p); append (buf); }
+ inline void appendBool(bool b) { append (b ? "true" : "false"); }
void appendNoCopy(char *str);
const char *getChars();
void clear ();
diff --git a/lout/object.cc b/lout/object.cc
index 99b5902d..74328d22 100644
--- a/lout/object.cc
+++ b/lout/object.cc
@@ -94,7 +94,9 @@ const char *Object::toString()
*/
void Object::intoStringBuffer(misc::StringBuffer *sb)
{
- sb->append("<not further specified object>");
+ sb->append("<not further specified object ");
+ sb->appendPointer(this);
+ sb->append(">");
}
/**
@@ -107,29 +109,44 @@ size_t Object::sizeOf()
}
// ----------------
-// Comparable
+// Comparator
// ----------------
+Comparator *Comparator::compareFunComparator = NULL;
+
/**
* \brief This static method may be used as compare function for
* qsort(3) and bsearch(3), for an array of Object* (Object*[] or
* Object**).
+ *
+ * "compareFunComparator" should be set before.
+ *
+ * \todo Not reentrant. Consider switching to reentrant variants
+ * (qsort_r), and compare function with an additional argument.
*/
-int Comparable::compareFun(const void *p1, const void *p2)
+int Comparator::compareFun(const void *p1, const void *p2)
{
- Comparable *c1 = *(Comparable**)p1;
- Comparable *c2 = *(Comparable**)p2;
+ return compareFunComparator->compare (*(Object**)p1, *(Object**)p2);
+}
+
+// ------------------------
+// StandardComparator
+// ------------------------
- if (c1 && c2)
- return ((c1)->compareTo(c2));
- else if (c1)
+int StandardComparator::compare(Object *o1, Object *o2)
+{
+ if (o1 && o2)
+ return ((Comparable*)o1)->compareTo ((Comparable*)o2);
+ else if (o1)
return 1;
- else if (c2)
+ else if (o2)
return -1;
else
return 0;
}
+StandardComparator standardComparator;
+
// -------------
// Pointer
// -------------
diff --git a/lout/object.hh b/lout/object.hh
index fd612863..5a4935c5 100644
--- a/lout/object.hh
+++ b/lout/object.hh
@@ -42,10 +42,11 @@ class Comparable: public Object
{
public:
/**
- * \brief Compare two objects c1 and c2.
+ * \brief Compare two objects, this and other.
*
- * Return a value < 0, when c1 is less than c2, a value > 0, when c1
- * is greater than c2, or 0, when c1 and c2 are equal.
+ * Return a value < 0, when this is less than other, a value > 0,
+ * when this is greater than other, or 0, when this and other are
+ * equal.
*
* If c1.equals(c2) (as defined in Object), c1.compareTo(c2) must
* be 0, but, unlike you may expect, the reversed is not
@@ -55,10 +56,43 @@ public:
* care about.
*/
virtual int compareTo(Comparable *other) = 0;
+};
+
+/**
+ * \brief Used for other orders as the one defined by Comparable.
+ *
+ * Compared objects must not neccessary be instances of Comparable.
+ */
+class Comparator: public Object
+{
+public:
+ /**
+ * \brief Compare two objects o1 and o2.
+ *
+ * Return a value < 0, when o1 is less than o2, a value > 0, when o1
+ * is greater than o2, or 0, when o1 and o2 are equal.
+ *
+ * If o1.equals(o2) (as defined in Object), compare(o1, o2) must be
+ * 0, but, unlike you may expect, the reversed is not necessarily
+ * true. This method returns 0, if, according to the rules for
+ * sorting, there is no difference, but there may still be
+ * differences (not relevant for sorting), which "equals" will care
+ * about.
+ */
+ virtual int compare(Object *o1, Object *o2) = 0;
+ static Comparator *compareFunComparator;
static int compareFun(const void *p1, const void *p2);
};
+class StandardComparator: public Comparator
+{
+public:
+ int compare(Object *o1, Object *o2);
+};
+
+extern StandardComparator standardComparator;
+
/**
* \brief An object::Object wrapper for void pointers.
*/
diff --git a/src/cssparser.cc b/src/cssparser.cc
index b283957a..4ff8f4f7 100644
--- a/src/cssparser.cc
+++ b/src/cssparser.cc
@@ -72,6 +72,10 @@ static const char *const Css_border_width_enum_vals[] = {
"thin", "medium", "thick", NULL
};
+static const char *const Css_clear_enum_vals[] = {
+ "left", "right", "both", "none", NULL
+};
+
static const char *const Css_cursor_enum_vals[] = {
"crosshair", "default", "pointer", "move", "e-resize", "ne-resize",
"nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize",
@@ -84,6 +88,10 @@ static const char *const Css_display_enum_vals[] = {
"table-cell", NULL
};
+static const char *const Css_float_enum_vals[] = {
+ "none", "left", "right", NULL
+};
+
static const char *const Css_font_size_enum_vals[] = {
"large", "larger", "medium", "small", "smaller", "xx-large", "xx-small",
"x-large", "x-small", NULL
@@ -121,6 +129,10 @@ static const char *const Css_list_style_type_enum_vals[] = {
"katakana-iroha", "none", NULL
};
+static const char *const Css_position_enum_vals[] = {
+ "static", "relative", "absolute", "fixed", NULL
+};
+
static const char *const Css_text_align_enum_vals[] = {
"left", "right", "center", "justify", "string", NULL
};
@@ -182,9 +194,9 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
Css_border_style_enum_vals},
{"border-top-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
Css_border_width_enum_vals},
- {"bottom", {CSS_TYPE_UNUSED}, NULL},
+ {"bottom", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
{"caption-side", {CSS_TYPE_UNUSED}, NULL},
- {"clear", {CSS_TYPE_UNUSED}, NULL},
+ {"clear", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_clear_enum_vals},
{"clip", {CSS_TYPE_UNUSED}, NULL},
{"color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
{"content", {CSS_TYPE_STRING, CSS_TYPE_UNUSED}, NULL},
@@ -194,7 +206,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
{"direction", {CSS_TYPE_UNUSED}, NULL},
{"display", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_display_enum_vals},
{"empty-cells", {CSS_TYPE_UNUSED}, NULL},
- {"float", {CSS_TYPE_UNUSED}, NULL},
+ {"float", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_float_enum_vals},
{"font-family", {CSS_TYPE_SYMBOL, CSS_TYPE_UNUSED}, NULL},
{"font-size", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED},
Css_font_size_enum_vals},
@@ -239,9 +251,9 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
{"padding-left", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
{"padding-right", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
{"padding-top", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
- {"position", {CSS_TYPE_UNUSED}, NULL},
+ {"position", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_position_enum_vals},
{"quotes", {CSS_TYPE_UNUSED}, NULL},
- {"right", {CSS_TYPE_UNUSED}, NULL},
+ {"right", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
{"text-align", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_text_align_enum_vals},
{"text-decoration", {CSS_TYPE_MULTI_ENUM, CSS_TYPE_UNUSED},
Css_text_decoration_enum_vals},
@@ -249,7 +261,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
{"text-shadow", {CSS_TYPE_UNUSED}, NULL},
{"text-transform", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
Css_text_transform_enum_vals},
- {"top", {CSS_TYPE_UNUSED}, NULL},
+ {"top", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
{"unicode-bidi", {CSS_TYPE_UNUSED}, NULL},
{"vertical-align",{CSS_TYPE_ENUM, CSS_TYPE_UNUSED},Css_vertical_align_vals},
{"visibility", {CSS_TYPE_UNUSED}, NULL},
diff --git a/src/html.cc b/src/html.cc
index 1257c102..74b90577 100644
--- a/src/html.cc
+++ b/src/html.cc
@@ -361,7 +361,8 @@ static void Html_add_textblock(DilloHtml *html, int space)
Textblock *textblock = new Textblock (prefs.limit_text_width);
HT2TB(html)->addParbreak (space, html->wordStyle ());
- HT2TB(html)->addWidget (textblock, html->style ());
+ HT2TB(html)->addWidget (textblock, html->style ()); /* Works also for floats
+ etc. */
HT2TB(html)->addParbreak (space, html->wordStyle ());
S_TOP(html)->textblock = html->dw = textblock;
S_TOP(html)->hand_over_break = true;
diff --git a/src/styleengine.cc b/src/styleengine.cc
index 98b31d32..2da9d8f7 100644
--- a/src/styleengine.cc
+++ b/src/styleengine.cc
@@ -606,6 +606,12 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
computeValue (&attrs->hBorderSpacing, p->value.intVal,attrs->font);
computeValue (&attrs->vBorderSpacing, p->value.intVal,attrs->font);
break;
+ case CSS_PROPERTY_BOTTOM:
+ computeLength (&attrs->bottom, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_CLEAR:
+ attrs->clear = (ClearType) p->value.intVal;
+ break;
case CSS_PROPERTY_COLOR:
attrs->color = Color::create (layout, p->value.intVal);
break;
@@ -615,6 +621,12 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
case CSS_PROPERTY_DISPLAY:
attrs->display = (DisplayType) p->value.intVal;
break;
+ case CSS_PROPERTY_FLOAT:
+ attrs->vloat = (FloatType) p->value.intVal;
+ break;
+ case CSS_PROPERTY_LEFT:
+ computeLength (&attrs->left, p->value.intVal, attrs->font);
+ break;
case CSS_PROPERTY_LINE_HEIGHT:
if (p->type == CSS_TYPE_ENUM) { //only valid enum value is "normal"
attrs->lineHeight = dw::core::style::LENGTH_AUTO;
@@ -666,6 +678,12 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
case CSS_PROPERTY_PADDING_RIGHT:
computeValue (&attrs->padding.right, p->value.intVal, attrs->font);
break;
+ case CSS_PROPERTY_POSITION:
+ attrs->position = (Position) p->value.intVal;
+ break;
+ case CSS_PROPERTY_RIGHT:
+ computeLength (&attrs->right, p->value.intVal, attrs->font);
+ break;
case CSS_PROPERTY_TEXT_ALIGN:
attrs->textAlign = (TextAlignType) p->value.intVal;
break;
@@ -678,6 +696,9 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
case CSS_PROPERTY_TEXT_TRANSFORM:
attrs->textTransform = (TextTransform) p->value.intVal;
break;
+ case CSS_PROPERTY_TOP:
+ computeLength (&attrs->top, p->value.intVal, attrs->font);
+ break;
case CSS_PROPERTY_VERTICAL_ALIGN:
attrs->valign = (VAlignType) p->value.intVal;
break;
diff --git a/test/Makefile.am b/test/Makefile.am
index 156c8667..35689ec3 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -7,6 +7,7 @@ noinst_PROGRAMS = \
dw-anchors-test \
dw-example \
dw-find-test \
+ dw-float-test \
dw-links \
dw-links2 \
dw-image-background \
@@ -52,6 +53,14 @@ dw_find_test_LDADD = \
$(top_builddir)/lout/liblout.a \
@LIBFLTK_LIBS@
+dw_float_test_SOURCES = dw_float_test.cc
+dw_float_test_LDADD = \
+ ../dw/libDw-widgets.a \
+ ../dw/libDw-fltk.a \
+ ../dw/libDw-core.a \
+ ../lout/liblout.a \
+ @LIBFLTK_LIBS@
+
dw_links_SOURCES = dw_links.cc
dw_links_LDADD = \
$(top_builddir)/dw/libDw-widgets.a \
diff --git a/test/containers.cc b/test/containers.cc
index 993a299d..9b93f158 100644
--- a/test/containers.cc
+++ b/test/containers.cc
@@ -4,6 +4,16 @@
using namespace lout::object;
using namespace lout::container::typed;
+class ReverseComparator: public Comparator
+{
+private:
+ Comparator *reversed;
+
+public:
+ ReverseComparator (Comparator *reversed) { this->reversed = reversed; }
+ int compare(Object *o1, Object *o2) { return - reversed->compare (o1, o2); }
+};
+
void testHashSet ()
{
puts ("--- testHashSet ---");
@@ -38,6 +48,8 @@ void testHashTable ()
void testVector1 ()
{
+ ReverseComparator reverse (&standardComparator);
+
puts ("--- testVector (1) ---");
Vector<String> v (true, 1);
@@ -47,6 +59,9 @@ void testVector1 ()
v.put (new String ("three"));
puts (v.toString());
+ v.sort (&reverse);
+ puts (v.toString());
+
v.sort ();
puts (v.toString());
}
@@ -95,12 +110,31 @@ void testVector2 ()
}
}
+void testVector3 ()
+{
+ // Regression test: resulted once incorrently (0, 2, 3), should
+ // result in (1, 2, 3).
+
+ puts ("--- testVector (3) ---");
+
+ Vector<String> v (true, 1);
+ String k ("omega");
+
+ v.put (new String ("alpha"));
+ printf (" -> %d\n", v.bsearch (&k, false));
+ v.put (new String ("beta"));
+ printf (" -> %d\n", v.bsearch (&k, false));
+ v.put (new String ("gamma"));
+ printf (" -> %d\n", v.bsearch (&k, false));
+}
+
int main (int argc, char *argv[])
{
testHashSet ();
testHashTable ();
testVector1 ();
testVector2 ();
+ testVector3 ();
return 0;
}
diff --git a/test/dw_float_test.cc b/test/dw_float_test.cc
new file mode 100644
index 00000000..70273e89
--- /dev/null
+++ b/test/dw_float_test.cc
@@ -0,0 +1,145 @@
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+
+#include "../dw/core.hh"
+#include "../dw/fltkcore.hh"
+#include "../dw/fltkviewport.hh"
+#include "../dw/textblock.hh"
+
+using namespace dw;
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace dw::fltk;
+
+static Textblock *firstFloat;
+static Style *wordStyle;
+
+static void addTextToFloatTimeout (void *data)
+{
+ printf("addTextToFloatTimeout\n");
+
+ const char *fWords[] = { "This", "is", "a", "float,", "which", "is",
+ "set", "aside", "from", "the", "main",
+ "text.", NULL };
+
+ for(int k = 0; fWords[k]; k++) {
+ firstFloat->addText(fWords[k], wordStyle);
+ firstFloat->addSpace(wordStyle);
+ }
+
+ firstFloat->flush();
+
+ Fl::repeat_timeout (2, addTextToFloatTimeout, NULL);
+}
+
+int main(int argc, char **argv)
+{
+ FltkPlatform *platform = new FltkPlatform ();
+ Layout *layout = new Layout (platform);
+
+ Fl_Window *window = new Fl_Window(400, 600, "Dw Floats Example");
+ window->begin();
+
+ FltkViewport *viewport = new FltkViewport (0, 0, 400, 600);
+ layout->attachView (viewport);
+
+ StyleAttrs styleAttrs;
+ styleAttrs.initValues ();
+ styleAttrs.margin.setVal (5);
+
+ FontAttrs fontAttrs;
+ fontAttrs.name = "Bitstream Charter";
+ fontAttrs.size = 14;
+ fontAttrs.weight = 400;
+ fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
+ styleAttrs.font = core::style::Font::create (layout, &fontAttrs);
+
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
+
+ Style *widgetStyle = Style::create (&styleAttrs);
+
+ styleAttrs.borderWidth.setVal (1);
+ styleAttrs.setBorderColor (Color::create (layout, 0x808080));
+ styleAttrs.setBorderStyle (BORDER_DASHED);
+ styleAttrs.width = createAbsLength(100);
+ styleAttrs.vloat = FLOAT_LEFT;
+ Style *leftFloatStyle = Style::create (&styleAttrs);
+
+ styleAttrs.width = createAbsLength(80);
+ styleAttrs.vloat = FLOAT_RIGHT;
+ Style *rightFloatStyle = Style::create (&styleAttrs);
+
+ Textblock *textblock = new Textblock (false);
+ textblock->setStyle (widgetStyle);
+ layout->setWidget (textblock);
+
+ widgetStyle->unref();
+
+ styleAttrs.borderWidth.setVal (0);
+ styleAttrs.width = LENGTH_AUTO;
+ styleAttrs.vloat = FLOAT_NONE;
+ styleAttrs.margin.setVal (0);
+ styleAttrs.backgroundColor = NULL;
+
+ wordStyle = Style::create (&styleAttrs);
+
+ for(int i = 1; i <= 10; i++) {
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d%s",
+ i, (i == 1 ? "st" : (i == 2 ? "nd" : (i == 3 ? "rd" : "th"))));
+
+ const char *words[] = { "This", "is", "the", buf, "paragraph.",
+ "Here", "comes", "some", "more", "text",
+ "to", "demonstrate", "word", "wrapping.",
+ NULL };
+
+ for(int j = 0; words[j]; j++) {
+ textblock->addText(words[j], wordStyle);
+ textblock->addSpace(wordStyle);
+
+ if ((i == 3 || i == 5) && j == 8) {
+ textblock->addText("[float]", wordStyle);
+ textblock->addSpace(wordStyle);
+
+ Textblock *vloat = new Textblock (false);
+ textblock->addWidget(vloat, i == 3 ? leftFloatStyle : rightFloatStyle);
+
+ const char *fWords[] = { "This", "is", "a", "float,", "which", "is",
+ "set", "aside", "from", "the", "main",
+ "text.", NULL };
+
+ vloat->addText(i == 3 ? "Left:" : "Right:", wordStyle);
+ vloat->addSpace(wordStyle);
+
+ for(int k = 0; fWords[k]; k++) {
+ vloat->addText(fWords[k], wordStyle);
+ vloat->addSpace(wordStyle);
+ }
+
+ vloat->flush ();
+
+ if(i == 3)
+ firstFloat = vloat;
+ }
+ }
+
+ textblock->addParbreak(10, wordStyle);
+ }
+
+ leftFloatStyle->unref();
+ rightFloatStyle->unref();
+
+ textblock->flush ();
+
+ window->resizable(viewport);
+ window->show();
+ Fl::add_timeout (2, addTextToFloatTimeout, NULL);
+ int errorCode = Fl::run();
+
+ wordStyle->unref();
+ delete layout;
+
+ return errorCode;
+}
diff --git a/test/floats-and-absolute.html b/test/floats-and-absolute.html
new file mode 100644
index 00000000..658ab16b
--- /dev/null
+++ b/test/floats-and-absolute.html
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+ <head>
+ <title>Floats And Absolute Positions</title>
+ <style type="text/css">
+ div.main {
+ margin: 0 0 0 100px;
+ top: 3cm;
+ position: absolute;
+ }
+
+ div.margin {
+ position: absolute;
+ top: 3cm;
+ width: 120px;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Floats And Absolute Positions</h1>
+ <div class="main">
+ <img style="float: left" src="http://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Library_of_Ashurbanipal_The_Flood_Tablet.jpg/218px-Library_of_Ashurbanipal_The_Flood_Tablet.jpg" />
+ <p>Sed ut perspiciatis, unde omnis iste natus error sit
+ voluptatem accusantium doloremque laudantium, totam rem
+ aperiam eaque ipsa, quae ab illo inventore veritatis et quasi
+ architecto beatae vitae dicta sunt, explicabo. nemo enim ipsam
+ voluptatem, quia voluptas sit, aspernatur aut odit aut fugit,
+ sed quia consequuntur magni dolores eos, qui ratione
+ voluptatem sequi nesciunt, neque porro quisquam est, qui
+ dolorem ipsum, quia dolor sit, amet, consectetur, adipisci
+ velit, sed quia non numquam eius modi tempora incidunt, ut
+ labore et dolore magnam aliquam quaerat voluptatem. ut enim ad
+ minima veniam, quis nostrum exercitationem ullam corporis
+ suscipit laboriosam, nisi ut aliquid ex ea commodi
+ consequatur? quis autem vel eum iure reprehenderit, qui in ea
+ voluptate velit esse, quam nihil molestiae consequatur, vel
+ illum, qui dolorem eum fugiat, quo voluptas nulla
+ pariatur?</p>
+ <p>Ἐν ἀρχῇ ἦν ὁ Λόγος, καὶ ὁ Λόγος ἦν πρὸς τὸν Θεόν, καὶ Θεὸς ἦν
+ ὁ Λόγος. Οὗτος ἦν ἐν ἀρχῇ πρὸς τὸν Θεόν. πάντα δι' αὐτοῦ
+ ἐγένετο, καὶ χωρὶς αὐτοῦ ἐγένετο οὐδὲ ἕν ὃ γέγονεν. ἐν αὐτῷ
+ ζωὴ ἦν, καὶ ἡ ζωὴ ἦν τὸ φῶς τῶν ἀνθρώπων. καὶ τὸ φῶς ἐν τῇ
+ σκοτίᾳ φαίνει, καὶ ἡ σκοτία αὐτὸ οὐ κατέλαβεν.</p>
+ </div>
+ <div class="margin">Margin, actually on the left side.</div>
+ <body>
+</html>
+
+
+
diff --git a/test/floats-and-margins.html b/test/floats-and-margins.html
new file mode 100644
index 00000000..ac64b9e1
--- /dev/null
+++ b/test/floats-and-margins.html
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+ <head>
+ <title>Floats And Margins</title>
+ <style type="text/css">
+ </style>
+ </head>
+ <body>
+ <p>
+ <img src="http://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Library_of_Ashurbanipal_The_Flood_Tablet.jpg/200px-Library_of_Ashurbanipal_The_Flood_Tablet.jpg" style="float: left; margin: 1cm 1cm 1cm 0" />
+ Ἐν ἀρχῇ ἦν ὁ Λόγος, καὶ ὁ Λόγος ἦν πρὸς τὸν Θεόν, καὶ Θεὸς ἦν ὁ
+ Λόγος. Οὗτος ἦν ἐν ἀρχῇ πρὸς τὸν Θεόν. πάντα δι' αὐτοῦ ἐγένετο,
+ καὶ χωρὶς αὐτοῦ ἐγένετο οὐδὲ ἕν ὃ γέγονεν. ἐν αὐτῷ ζωὴ ἦν, καὶ ἡ
+ ζωὴ ἦν τὸ φῶς τῶν ἀνθρώπων. καὶ τὸ φῶς ἐν τῇ σκοτίᾳ φαίνει, καὶ
+ ἡ σκοτία αὐτὸ οὐ κατέλαβεν.
+ </p>
+ <p style="margin: 0 3cm">
+ <img src="http://upload.wikimedia.org/wikipedia/commons/thumb/2/26/GilgameshTablet.jpg/200px-GilgameshTablet.jpg" style="float: right; margin: 1cm 0 1cm 1cm"/>
+ Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+ accusantium doloremque laudantium, totam rem aperiam eaque ipsa,
+ quae ab illo inventore veritatis et quasi architecto beatae
+ vitae dicta sunt, explicabo. nemo enim ipsam voluptatem, quia
+ voluptas sit, aspernatur aut odit aut fugit, sed quia
+ consequuntur magni dolores eos, qui ratione voluptatem sequi
+ nesciunt, neque porro quisquam est, qui dolorem ipsum, quia
+ dolor sit, amet, consectetur, adipisci velit, sed quia non
+ numquam eius modi tempora incidunt, ut labore et dolore magnam
+ aliquam quaerat voluptatem. ut enim ad minima veniam, quis
+ nostrum exercitationem ullam corporis suscipit laboriosam, nisi
+ ut aliquid ex ea commodi consequatur? quis autem vel eum iure
+ reprehenderit, qui in ea voluptate velit esse, quam nihil
+ molestiae consequatur, vel illum, qui dolorem eum fugiat, quo
+ voluptas nulla pariatur?
+ </p>
+ <body>
+</html>
+
+
+
diff --git a/test/floats-table.html b/test/floats-table.html
new file mode 100644
index 00000000..77d77563
--- /dev/null
+++ b/test/floats-table.html
@@ -0,0 +1,24 @@
+<p>Demonstrating how to include floats into witdth extremes.
+
+<table>
+ <tr>
+ <td style="border: 1px dashed black">
+ <div style="float:left; border: 1px dashed black">Somelongwordwhichmustnotbebrokensotakingmuchspaceinatablecolumn</div>
+ Some short text.
+ <td style="border: 1px dashed black">
+ Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+ accusantium doloremque laudantium, totam rem aperiam eaque ipsa,
+ quae ab illo inventore veritatis et quasi architecto beatae
+ vitae dicta sunt, explicabo. nemo enim ipsam voluptatem, quia
+ voluptas sit, aspernatur aut odit aut fugit, sed quia
+ consequuntur magni dolores eos, qui ratione voluptatem sequi
+ nesciunt, neque porro quisquam est, qui dolorem ipsum, quia
+ dolor sit, amet, consectetur, adipisci velit, sed quia non
+ numquam eius modi tempora incidunt, ut labore et dolore magnam
+ aliquam quaerat voluptatem. ut enim ad minima veniam, quis
+ nostrum exercitationem ullam corporis suscipit laboriosam, nisi
+ ut aliquid ex ea commodi consequatur? quis autem vel eum iure
+ reprehenderit, qui in ea voluptate velit esse, quam nihil
+ molestiae consequatur, vel illum, qui dolorem eum fugiat, quo
+ voluptas nulla pariatur?
+</table>
diff --git a/test/floats-worm.html b/test/floats-worm.html
new file mode 100644
index 00000000..9e050933
--- /dev/null
+++ b/test/floats-worm.html
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+ <head>
+ <title>Floats and iterators: search for "worm" and pay attention
+ to the order.</title>
+ <style type="text/css">
+ .float1, .float2 {
+ padding: 0.5em;
+ border: 1px dashed #404040;
+ width: 30%;
+ }
+ .float1 {
+ float: left;
+ margin: 0.5em 0.5em 0.5em 0;
+ }
+ .float2 {
+ float: right;
+ margin: 0.5em 0 0.5em 0.5em;
+ }
+
+ </style>
+ </head>
+ <body>
+ <div class="float1">1: apple apple worm apple apple</div>
+ <p>2: apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple worm apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple</p>
+ <div class="float2">3: apple apple worm apple apple</div>
+ <p>4: apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple worm apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple apple apple
+ apple apple apple apple apple apple apple apple</p>
+ <div class="float1">5: apple apple worm apple apple</div>
+ <body>
+</html>
+
+
+
diff --git a/test/floats1.html b/test/floats1.html
new file mode 100644
index 00000000..eae30c4a
--- /dev/null
+++ b/test/floats1.html
@@ -0,0 +1,46 @@
+<div>First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First
+paragraph. <div style="float: left; border: 1px dashed black">Left
+float. Left float. Left float. Left float. Left float. Left
+float. Left float. Left float. Left float. Left float. Left
+float. Left float. Left float. Left float. Left float. Left
+float. Left float. Left float. Left float. Left float.</div> First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph. First paragraph. First
+paragraph. First paragraph. First paragraph.</div>
+<div><div>Second paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph.<div style="float:
+right; border: 1px dashed black">Right float. Right float. Right
+float. Right float. Right float. Right float.</div> Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph. Second
+paragraph. Second paragraph. Second paragraph.</div></div>
diff --git a/test/floats2.html b/test/floats2.html
new file mode 100644
index 00000000..b7978706
--- /dev/null
+++ b/test/floats2.html
@@ -0,0 +1,17 @@
+Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae
+ab illo inventore veritatis et quasi architecto beatae vitae dicta
+sunt, explicabo.
+<div style="float:left; border: 1px dashed black">Some text in a
+float.<br /><img src="http://www.dillo.org/dw/html/not-so-simple-container.png" /></div>
+nemo enim ipsam voluptatem, quia voluptas sit,
+aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,
+qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit,
+sed quia non numquam eius modi tempora incidunt, ut labore et dolore
+magnam aliquam quaerat voluptatem. ut enim ad minima veniam, quis
+nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut
+aliquid ex ea commodi consequatur? quis autem vel eum iure
+reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae
+consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla
+pariatur?
diff --git a/test/floats3.html b/test/floats3.html
new file mode 100644
index 00000000..4d57e453
--- /dev/null
+++ b/test/floats3.html
@@ -0,0 +1,16 @@
+<p>Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae
+ab illo inventore veritatis et quasi architecto beatae vitae dicta
+sunt, explicabo.</p>
+<div style="float:left; border: 1px dashed black">Some text in a
+float.<br /><img src="http://www.dillo.org/dw/html/not-so-simple-container.png" /></div>
+<p>nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit
+aut fugit, sed quia consequuntur magni dolores eos, qui ratione
+voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem
+ipsum, quia dolor sit, amet, consectetur, adipisci velit, sed quia non
+numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam
+quaerat voluptatem. ut enim ad minima veniam, quis nostrum
+exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex
+ea commodi consequatur? quis autem vel eum iure reprehenderit, qui in
+ea voluptate velit esse, quam nihil molestiae consequatur, vel illum,
+qui dolorem eum fugiat, quo voluptas nulla pariatur?</p>
diff --git a/test/floats4.html b/test/floats4.html
new file mode 100644
index 00000000..965ed68d
--- /dev/null
+++ b/test/floats4.html
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+ <head>
+ <title>Floats 4</title>
+ <style type="text/css">
+ .border {
+ background-color: #e0e0ff;
+ padding: 1cm;
+ }
+ .float1, .float2 {
+ margin: 1cm;
+ padding: 1cm;
+ border: 1px dashed red;
+ background-color: #f0fff0;
+ float: right;
+ }
+ .float1 {
+ float: left;
+ }
+ .float2 {
+ float: right;
+ }
+
+ .wide {
+ margin: 1cm 0;
+ padding: 1cm;
+ border: 1px dashed red;
+ background-color: #ffffd0;
+ width: 40cm;
+ }
+ </style>
+ </head>
+ <body class="border">
+ <div class="float2">Some text in a float.</div>
+
+ <p>Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+ accusantium doloremque laudantium, totam rem aperiam eaque ipsa,
+ quae ab illo inventore veritatis et quasi architecto beatae
+ vitae dicta sunt, explicabo. nemo enim ipsam voluptatem, quia
+ voluptas sit, aspernatur aut odit aut fugit, sed quia
+ consequuntur magni dolores eos, qui ratione voluptatem sequi
+ nesciunt, neque porro quisquam est, qui dolorem ipsum, quia
+ dolor sit, amet, consectetur, adipisci velit, sed quia non
+ numquam eius modi tempora incidunt, ut labore et dolore magnam
+ aliquam quaerat voluptatem. ut enim ad minima veniam, quis
+ nostrum exercitationem ullam corporis suscipit laboriosam, nisi
+ ut aliquid ex ea commodi consequatur? quis autem vel eum iure
+ reprehenderit, qui in ea voluptate velit esse, quam nihil
+ molestiae consequatur, vel illum, qui dolorem eum fugiat, quo
+ voluptas nulla pariatur?</p>
+
+ <table class="wide"><tbody><tr><td>Ἐν ἀρχῇ ἦν ὁ Λόγος, καὶ ὁ Λόγος
+ ἦν πρὸς τὸν Θεόν, καὶ Θεὸς ἦν ὁ Λόγος. Οὗτος ἦν ἐν ἀρχῇ πρὸς τὸν
+ Θεόν. πάντα δι' αὐτοῦ ἐγένετο, καὶ χωρὶς αὐτοῦ ἐγένετο οὐδὲ ἕν ὃ
+ γέγονεν. ἐν αὐτῷ ζωὴ ἦν, καὶ ἡ ζωὴ ἦν τὸ φῶς τῶν ἀνθρώπων. καὶ
+ τὸ φῶς ἐν τῇ σκοτίᾳ φαίνει, καὶ ἡ σκοτία αὐτὸ οὐ
+ κατέλαβεν.</tbody></tr></td></table>
+ <body>
+</html>
+
+
+
diff --git a/test/floats5.html b/test/floats5.html
new file mode 100644
index 00000000..c8c6564a
--- /dev/null
+++ b/test/floats5.html
@@ -0,0 +1,16 @@
+Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae
+ab illo inventore veritatis et quasi architecto beatae vitae dicta
+sunt, explicabo.
+<div style="float:left; border: 1px dashed black"><img src="http://www.dillo.org/Icons/ProgramIcon16.png" /></div>
+nemo enim ipsam voluptatem, quia voluptas sit,
+aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,
+qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit,
+sed quia non numquam eius modi tempora incidunt, ut labore et dolore
+magnam aliquam quaerat voluptatem. ut enim ad minima veniam, quis
+nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut
+aliquid ex ea commodi consequatur? quis autem vel eum iure
+reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae
+consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla
+pariatur?