summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore1
-rw-r--r--ChangeLog2
-rw-r--r--configure.ac9
-rw-r--r--doc/Makefile.am3
-rw-r--r--doc/dw-floats-01.pngbin0 -> 11196 bytes
-rw-r--r--doc/dw-out-of-flow-2.doc70
-rw-r--r--doc/dw-out-of-flow.doc185
-rw-r--r--doc/dw-widget-sizes.doc231
-rw-r--r--dw/Makefile.am6
-rw-r--r--dw/findtext.cc4
-rw-r--r--dw/fltkui.cc80
-rw-r--r--dw/fltkui.hh4
-rw-r--r--dw/iterator.cc146
-rw-r--r--dw/iterator.hh15
-rw-r--r--dw/layout.cc106
-rw-r--r--dw/layout.hh29
-rw-r--r--dw/outofflowmgr.cc2114
-rw-r--r--dw/outofflowmgr.hh429
-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.cc943
-rw-r--r--dw/textblock.hh240
-rw-r--r--dw/textblock_iterator.cc331
-rw-r--r--dw/textblock_linebreaking.cc1156
-rw-r--r--dw/types.cc85
-rw-r--r--dw/types.hh29
-rw-r--r--dw/ui.cc58
-rw-r--r--dw/ui.hh3
-rw-r--r--dw/widget.cc217
-rw-r--r--dw/widget.hh82
-rw-r--r--lout/Makefile.am3
-rw-r--r--lout/container.cc86
-rw-r--r--lout/container.hh37
-rw-r--r--lout/debug.hh149
-rw-r--r--lout/identity.cc23
-rw-r--r--lout/identity.hh4
-rw-r--r--lout/misc.hh18
-rw-r--r--lout/object.cc35
-rw-r--r--lout/object.hh40
-rw-r--r--src/Makefile.am2
-rw-r--r--src/cssparser.cc24
-rw-r--r--src/dillo.cc8
-rw-r--r--src/html.cc3
-rw-r--r--src/styleengine.cc21
-rw-r--r--test/Makefile.am13
-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
61 files changed, 6850 insertions, 786 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 d44f3dbb..fbb5bec2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,4 @@
-=============================================================================
+b=============================================================================
Dillo project
=============================================================================
diff --git a/configure.ac b/configure.ac
index d22355ea..c9807deb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -56,6 +56,14 @@ AC_TYPE_UINT16_T
AC_TYPE_INT32_T
AC_TYPE_UINT32_T
+dnl -----------------------------------------------------------------
+dnl Check for absolute path of working directory.
+dnl This is needed for RTFL, to get full the full paths of the source
+dnl file names
+dnl -----------------------------------------------------------------
+dnl
+BASE_CUR_WORKING_DIR=`pwd`
+
dnl --------------------------------------
dnl Check whether to add /usr/local or not
dnl (this is somewhat a religious problem)
@@ -482,6 +490,7 @@ if eval "test x$GCC = xyes"; then
CXXFLAGS="$CXXFLAGS -Wall -W -Wno-unused-parameter -fno-rtti -fno-exceptions"
fi
+AC_SUBST(BASE_CUR_WORKING_DIR)
AC_SUBST(LIBJPEG_LIBS)
AC_SUBST(LIBJPEG_LDFLAGS)
AC_SUBST(LIBJPEG_CPPFLAGS)
diff --git a/doc/Makefile.am b/doc/Makefile.am
index d48e3e73..8ade3d15 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -11,6 +11,8 @@ EXTRA_DIST = \
dw-widget-sizes.doc \
dw-changes.doc \
dw-images-and-backgrounds.doc \
+ dw-dw-out-of-flow.doc \
+ dw-dw-out-of-flow-2.doc \
fltk-problems.doc \
rounding-errors.doc \
uml-legend.doc \
@@ -27,6 +29,7 @@ EXTRA_DIST = \
dw-textblock-collapsing-spaces-1-2.png \
dw-textblock-collapsing-spaces-2-1.png \
dw-textblock-collapsing-spaces-2-2.png \
+ dw-floats-01.png \
not-so-simple-container.png \
Cache.txt \
Cookies.txt \
diff --git a/doc/dw-floats-01.png b/doc/dw-floats-01.png
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-2.doc b/doc/dw-out-of-flow-2.doc
new file mode 100644
index 00000000..de15c672
--- /dev/null
+++ b/doc/dw-out-of-flow-2.doc
@@ -0,0 +1,70 @@
+/** \page dw-out-of-flow-2 Handling Elements Out Of Flow (cont.)
+
+(Some notes, to be integrated into \ref dw-out-of-flow; focus mainly
+on performance aspects.)
+
+Constructing a page with floats
+-------------------------------
+When a page is constructed (dw::Textblock::addWord), the *generating*
+block tells the positions of floats (or, generally, widgets out of
+flow) via dw::OutOfFlowMgr::tellPosition. This method considers
+already collisions with other floats (only previous floats; floats
+following this float are not considered); after the call,
+dw::OutOfFlowMgr::getBorder will return correct values.
+
+dw::OutOfFlowMgr::tellPosition also checks for overlaps of this float
+with other textblocks, except this textblock (the *generator*, which
+is just constructed, so nothing has to be done). The fact that the
+position of the float is the top, and so the float has only an
+allocation below this position, leads to the effect that only the
+textblocks following the generator are affected. (**Check:** Can the
+search be limited here?) When a page is constructed, no textblocks
+should be following the generating block, so no textblocks are
+affected.
+
+**Todo:** Clarify details of line breaking (\ref dw-line-breaking).
+
+Float changes its size
+----------------------
+The float itself will call queueResize, which will result in a call of
+markSizeChange for the *containing* block, which will then call
+dw::OutOfFlowMgr::markSizeChange. Here, the vloat is only *marked* as
+dirty; the size will be calculated later (in
+dw::OutOfFlowMgr::ensureFloatSize).
+
+This will trigger the resize idle function, so sizeRequest and
+sizeAllocate for all floats and textblocks. In this run,
+dw::OutOfFlowMgr::hasRelationChanged will return *true*, and so result
+in a call of dw::Textblock::borderChanged, and trigger a second run of
+the resize idle function, dealing correctly with the new size.
+
+(This case is handles in a not perfectly optimal way, since two runs
+of the resize idle function are neccessary; but size changes of floats
+is not a very common case.
+
+When a page is constructed (see above), a changing size of a float
+currently constructed typically only affects the most bottom
+textblock; the other textblocks are not covered by this float.)
+
+**Error:** In this case, new collisions are not yet considered.
+
+
+Changing the width of the page
+------------------------------
+
+When the page width is changed, this will result in a reconstruction
+of the page; see *Constructing a page with floats*. Anyway, checking
+for overlaps will play a more important role. This is handled in an
+optimal way by dw::OutOfFlowMgr::hasRelationChanged.
+
+**Check:** Are "cascades" avoided, like this:
+
+1. All textblocks are constructed. A float in textblock 1 overlaps
+ with textblock 2, so dw::Textblock::borderChanged is called for
+ textblock 2.
+2. In another resize idle run, textblock 2 is constructed again. A
+ float in textblock 2 overlaps with textblock 3, so that
+ dw::Textblock::borderChanged is called for textblock 3.
+3. Etc.
+
+*/ \ No newline at end of file
diff --git a/doc/dw-out-of-flow.doc b/doc/dw-out-of-flow.doc
new file mode 100644
index 00000000..16523952
--- /dev/null
+++ b/doc/dw-out-of-flow.doc
@@ -0,0 +1,185 @@
+/** \page dw-out-of-flow Handling Elements Out Of Flow
+
+<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
+padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
+Incomplete; some parts have been removed, beause they are not up to
+date.</div>
+
+<div style="border: 2px solid #ffff00; margin-bottom: 0.5em;
+padding: 0.5em 1em; background-color: #ffffe0"><b>Info:</b>
+Should be incorporated into dw::Textblock.</div>
+
+This texts deals with both floats and absolute positions, which have
+in common that there is a distinction between generating block and
+containing block (we are here using the same notation as in the
+CSS&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, especially the section *Rules for
+Methods Related to Resizing*.
+
+The size/position model of ::dw consists mainly of the following two
+steps:
+
+1. First, the size of the toplevel widget is calculated. Size
+ calculation typically depends on the sizes of the widgets, which
+ are calculated recursively, but not more.
+2. After this, the toplevel widget is allocated at position (0, 0),
+ with the previosly calculated size. Each widget must allocate its
+ children; here, the condition for the toplevel widget (allocated
+ size equals requested size) is not necessary; instead, each widget
+ may be allocated at every size.
+
+Especially for floats, this model becomes a bit difficult, for reasons
+described below. For the solutions, much is centralized at the level
+of the containing block, which delegates most to an instance of
+dw::OutOfFlowMgr (details below).
+
+**The size of a widget depends on the size not only of the children.**
+In the example above, the last list item (green, following the
+generating list item) must know the size of the the float (which is
+not a child or, generally, descendant) to determine the borders, which
+is done in dw::Textblock::sizeRequestImpl.
+
+For this, the size model has been extended (see \ref dw-widget-sizes,
+section *Rules for Methods Related to Resizing*): *sizeRequest* can be
+called within *sizeRequestImpl* for other widgets that children (with
+some caution). Namely, dw::Textblock::sizeRequestImpl calls
+dw::core::Widget::sizeRequest for the float, via
+dw::OutOfFlowMgr::getBorder and dw::OutOfFlowMgr::ensureFloatSize.
+
+**The size of a widget depends on the allocation of another widget.**
+In the example above, both list items (blue and green) must know the
+position of the float widget, within dw::Textblock::sizeRequestImpl,
+to calculate the borders. The position, however, is stored in the
+allocation, which is typically calculated later.
+
+Here, two cases must be distinguished. The position of a float is
+always relative to its generating block, so for calculating the
+borders for the generating block, the allocation needs not to be
+know. For other textblocks, it needs to be known, so the calculation
+of the borders will ignore floats generated by other textblocks, until
+all widgets are allocated. The latter will call (when neccessary)
+dw::core::Widget::queueResize, so that all border calculations are
+repeated.
+
+For details see:
+
+- dw::OutOfFlowMgr::getLeftBorder, dw::OutOfFlowMgr::getRightBorder,
+ dw::OutOfFlowMgr::getBorder (called by the first two), and
+ especially, dw::OutOfFlowMgr::getFloatsListForTextblock (called by
+ the latter), where these three cases are distinguished;
+- dw::OutOfFlowMgr::sizeAllocateStart,
+ dw::OutOfFlowMgr::sizeAllocateEnd which are called by the containing
+ block.
+
+Implementation overview
+=======================
+
+<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
+padding: 0.5em 1em; background-color: #fff0f0"><b>Warning:</b>
+This complete section must be reviewed.</div>
+
+Widget level
+------------
+The terms _generating block_ and _containing block_ have been raised
+to a higher level, the one of dw::core::Widget, and are here called
+_generating widget_ and _containing widget_. To represent the
+distinction, the type of dw::core::Content has been split into three
+parts:
+
+- If a widget is out of flow, the generating widget keeps a reference
+ with the type dw::core::Content::WIDGET_OOF_REF, while the
+ containing block refers to it as dw::core::Content::WIDGET_OOF_CONT.
+
+- For widgets within flow, dw::core::Content::WIDGET_IN_FLOW is used.
+
+Notice that in the first case, there are two pieces of content
+referring to the same widget.
+
+An application of this distinction is iterators. TODO: more. And still
+missing: DeepIterator may need the generating parent widget in some
+cases.
+
+
+Textblock level
+---------------
+Both dw::Textblock::notifySetAsTopLevel and
+dw::Textblock::notifySetParent set the member
+dw::Textblock::containingBlock appropriately, (according to rules
+which should be defined in this document).
+
+Handling widgets out of flow is partly the task of the new class
+dw::OutOfFlowMgr, which is stored by dw::Textblock::outOfFlowMgr, but
+only for containing blocks. Generating blocks should refer to
+_containingBlock->outOfFlowMgr_. (Perhaps dw::OutOfFlowMgr may become
+independent of dw::Textblock.)
+
+dw::Textblock::addWidget is extended, so that floats and absolutely
+positioned elements can be added. Notice that not this widget, but the
+containing block becomes the parent of the newly added child, if it is
+out of flow. dw::Textblock::addWidget decides this by calling
+dw::OutOfFlowMgr::isOutOfFlow. (See new content types above.)
+
+dw::core::Widget::parentRef has become a new representation. Before,
+it represented the line numer. Now (least signifant bit left):
+
+ +---+---+- - - - - - - - - - -+---+
+ | line number | 0 |
+ +---+---+- - - - - - - - - - -+---+
+
+ +---+---+- - - - - - - - -+---+---+
+ | number of left float | 0 | 1 |
+ +---+---+- - - - - - - - -+---+---+
+
+ +---+---+- - - - - - - - -+---+---+
+ | number of right float | 1 | 1 |
+ +---+---+- - - - - - - - -+---+---+
+
+The latter two must be changed, as soom as absolute positions are
+introduced. Details are hidden by static inline methods of
+dw::OutOfFlowMgr.
+
+
+See also
+========
+
+\ref dw-out-of-flow-2
+
+*/ \ No newline at end of file
diff --git a/doc/dw-widget-sizes.doc b/doc/dw-widget-sizes.doc
index 419a4a73..dcc74bfc 100644
--- a/doc/dw-widget-sizes.doc
+++ b/doc/dw-widget-sizes.doc
@@ -1,22 +1,20 @@
/** \page dw-widget-sizes Sizes of Dillo Widgets
-<h2>Allocation</h2>
+Allocation
+==========
Each widget has an \em allocation at a given time, this includes
-<ul>
-<li> the position (\em x, \em y) relative to the upper left corner of the
- canvas, and
-<li> the size (\em width, \em ascent, \em descent).
-</ul>
+- the position (\em x, \em y) relative to the upper left corner of the
+ canvas, and
+- the size (\em width, \em ascent, \em descent).
The \em canvas is the whole area available for the widgets, in most
-cases, only a part is seen in a viewport. The allocation of the toplevel widget is exactly the allocation of the canvas, i.e.
+cases, only a part is seen in a viewport. The allocation of the
+toplevel widget is exactly the allocation of the canvas, i.e.
-<ul>
-<li> the position of the toplevel widget is always (0, 0), and
-<li> the canvas size is defined by the size of the toplevel widget.
-</ul>
+- the position of the toplevel widget is always (0, 0), and
+- the canvas size is defined by the size of the toplevel widget.
The size of a widget is not simply defined by the width and the
height, instead, widgets may have a base line, and so are vertically
@@ -31,13 +29,11 @@ defined by the limits of the C++ type \em int.
In the example in the image, the widget has the following allocation:
-<ul>
-<li>\em x = 50
-<li>\em y = 50
-<li>\em width = 150
-<li>\em ascent = 150
-<li>\em descent = 100
-</ul>
+- \em x = 50
+- \em y = 50
+- \em width = 150
+- \em ascent = 150
+- \em descent = 100
The current allocation of a widget is hold in
dw::core::Widget::allocation. It can be set from outside by
@@ -54,7 +50,9 @@ appropriate child allocations. dw::core::Widget::allocation should not
be changed here, this is already done in
dw::core::Widget::sizeAllocate.
-<h2>Requisitions</h2>
+
+Requisitions
+============
A widget may prefer a given size for the allocation. This size, the
\em requisition, should be returned by the method
@@ -62,14 +60,12 @@ dw::core::Widget::sizeRequestImpl. In the simplest case, this is
independent of the context, e.g. for an
image. dw::Image::sizeRequestImpl returns the following size:
-<ul>
-<li> If no buffer has yet been assigned (see dw::Image for more details),
- the size necessary for the alternative text is returned. If no
- alternative text has been set, zero is returned.
+- If no buffer has yet been assigned (see dw::Image for more details),
+ the size necessary for the alternative text is returned. If no
+ alternative text has been set, zero is returned.
-<li> If a buffer has been assigned (by dw::Image::setBuffer), the root
- size is returned (i.e. the original size of the image to display).
-</ul>
+- If a buffer has been assigned (by dw::Image::setBuffer), the root
+ size is returned (i.e. the original size of the image to display).
This is a bit simplified, dw::Image::sizeRequestImpl should also deal
with margins, borders and paddings, see dw::core::style.
@@ -87,7 +83,9 @@ widget), may, but also may not consider the requisition. Instead, a
widget must deal with any allocation. (For example, dw::Image scales
the image buffer when allocated at another size.)
-<h2>Size Hints</h2>
+
+Size Hints
+==========
Some widgets do not have an inherent size, but depend on the context,
e.g. the viewport size. These widgets should adhere to <i>size hints</i>,
@@ -95,11 +93,9 @@ i.e. implement the methods dw::core::Widget::setWidth,
dw::core::Widget::setAscent and dw::core::Widget::setDescent. The values
passed to the callees are
-<ul>
-<li> the viewport size (ascent is the heigt here, while descent is 0) for
- the toplevel widget, and
-<li> determined by the parent for its child widgets.
-</ul>
+- the viewport size (ascent is the heigt here, while descent is 0) for
+ the toplevel widget, and
+- determined by the parent for its child widgets.
Generally, the values should define the available space for the
widget.
@@ -109,42 +105,41 @@ dw::core::Widget::queueResize, when apropriate.
\todo There should be a definition of "available space".
-<h2>Width Extremes</h2>
+
+Width Extremes
+==============
dw::Table uses width extremes for fast calculation of column
widths. The structure dw::core::Extremes represents the minimal and
maximal width of a widget, as defined by:
-<ul>
-<li> the minimal width is the smallest width, at which a widget can still
- display contents, and
-<li> the maximal width is the largest width, above which increasing the width
- does not make any sense.
-</ul>
+- the minimal width is the smallest width, at which a widget can still
+ display contents, and
+- the maximal width is the largest width, above which increasing the
+ width- does not make any sense.
Especially the latter is vaguely defined, here are some examples:
-<ul>
-<li> For those widgets, which do not depend on size hints, the minimal and
- the maximal width is the inherent width (the one returned by
- dw::core::Widget::sizeRequest).
+- For those widgets, which do not depend on size hints, the minimal
+ and the maximal width is the inherent width (the one returned by
+ dw::core::Widget::sizeRequest).
-<li> For a textblock, the minimal width is the width of the widest
- (unbreakable) word, the maximal width is the width of the total
- paragraph (stretching a paragraph further would only waste space).
- Actually, the implementation of dw::Textblock::getExtremesImpl is
- a bit more complex.
+- For a textblock, the minimal width is the width of the widest
+ (unbreakable) word, the maximal width is the width of the total
+ paragraph (stretching a paragraph further would only waste space).
+ Actually, the implementation of dw::Textblock::getExtremesImpl is a
+ bit more complex.
-<li> dw::Table is an example, where the width extremes are calculated
- from the width extremes of the children.
-</ul>
+- dw::Table is an example, where the width extremes are calculated
+ from the width extremes of the children.
Handling width extremes is similar to handling requisitions, a widget
must implement dw::core::Widget::getExtremesImpl, but a caller will
use dw::core::Widget::getExtremes.
-<h2>Resizing</h2>
+Resizing
+========
When the widget changes its size (requisition), it should call
dw::core::Widget::queueResize. The next call of
@@ -160,27 +155,127 @@ done before. In this case, a widget must exactly know the reasons, why
a call of dw::core::Widget::sizeRequestImpl is necessary. To make use
of this, a widget must implement the following:
-<ol>
-<li> There is a member dw::core::Widget::parentRef, which is
- totally under control of the parent widget (and so sometimes not
- used at all). It is necessary to define how parentRef is used
- by a specific parent widget, and it has to be set to the correct
- value whenever necessary.
-
-<li> The widget must implement dw::core::Widget::markSizeChange and
+1. There is a member dw::core::Widget::parentRef, which is totally
+ under control of the parent widget (and so sometimes not used at
+ all). It is necessary to define how parentRef is used by a specific
+ parent widget, and it has to be set to the correct value whenever
+ necessary.
+2. The widget must implement dw::core::Widget::markSizeChange and
dw::core::Widget::markExtremesChange, these methods are called in
two cases:
-
- <ol>
- <li> directly after dw::core::Widget::queueResize, with the argument
- ref was passed to dw::core::Widget::queueResize, and
- <li> if a child widget has called dw::core::Widget::queueResize,
- with the value of the parent_ref member of this child.
- </ol>
-</ol>
+ 1. directly after dw::core::Widget::queueResize, with the
+ argument ref was passed to dw::core::Widget::queueResize,
+ and
+ 2. if a child widget has called dw::core::Widget::queueResize,
+ with the value of the parent_ref member of this child.
This way, a widget can exactly keep track on size changes, and so
implement resizing in a faster way. A good example on how to use this
is dw::Textblock.
+
+Rules for Methods Related to Resizing
+=====================================
+
+Which method can be called, when the call of another method is not
+finished? These rules are important in two circumstances:
+
+1. To know which method can be called, and, especially, which methods
+ *must not* be called, within the implementation of
+ *sizeRequestImpl* (called by *sizeRequest*), *markSizeChange*, and
+ *markExtremesChange* (the latter two are called by *queueResize*).
+2. On the other hand, to make sure that the calls, which are allowed,
+ are handled correctly, especially in implementations of
+ *sizeRequestImpl*, *markSizeChange*, *markExtremesChange*
+
+Generally, the rules defined below are, in case of doubt, rather
+strict; when changing the rules, loosening is simpler than to tighten
+them, since this will make it neccessary to review old code for calls
+previously allowed but now forbidden.
+
+Short recap:
+
+- *QueueResize* directly calls *markSizeChange* and
+ *markExtremesChanges*, and queues an idle function for the actual
+ resizing (dw::core::Layout::resizeIdle). (The idle function is
+ called some time after *queueResize* is finished.)
+- The resize idle function first calls *sizeRequest*, then
+ *sizeAllocate*, for the toplevel widget.
+
+In the following table, the rules are defined in detail. "Within call
+of ..." includes all methods called from the original method: the
+first row (*queueResize*) defines also the rules for
+*markExtremesChanges* and *markExtremesChanges*, and in the second row
+(*sizeAllocate*), even *sizeRequest* has to be considered.
+
+<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
+padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
+Not up to date: *queueResize* can now be called recursively (so to
+speak). See code there.</div>
+
+<table>
+ <tr>
+ <th>Within call of ... ↓
+ <th>... is call allowed of ... ? →
+ <th>queueResize
+ <th>sizeAllocate
+ <th>sizeRequest
+ <th>getExtremes
+ <tr>
+ <th colspan=2>queueResize
+ <td>No
+ <td>No<sup>1</sup>
+ <td>No<sup>1</sup>
+ <td>No<sup>1</sup>
+ <tr>
+ <th colspan=2>sizeAllocate
+ <td>Yes
+ <td>Only for children<sup>2</sup>
+ <td>Yes(?)
+ <td>Yes(?)
+ <tr>
+ <th colspan=2>sizeRequest
+ <td>Yes<sup>3</sup>
+ <td>No
+ <td>Limited<sup>4</sup>
+ <td>Limited<sup>4</sup>
+ <tr>
+ <th colspan=2>getExtremes
+ <td>Yes<sup>3</sup>
+ <td>No
+ <td>Limited<sup>4</sup>
+ <td>Limited<sup>4</sup>
+ <tr>
+ <td colspan=6><sup>1</sup>) Otherwise, since these other methods
+may be call *queueResize*, the limitation that *queueResize* must not
+call *queueResize* can be violated.
+
+<sup>2</sup>) Could perhaps be loosened as for *sizeRequest* and
+*getExtremes*, but there is probably no need.
+
+<sup>3</sup>) Therefore the distinction between *RESIZE_QUEUED* and
+*NEEDS_RESIZE*, and *EXTREMES_QUEUED* and *EXTREMES_CHANGED*,
+respectively.
+
+<sup>4</sup>) Calls only for children are safe. In other cases, you
+take a large responsibility to prevent endless recursions by
+(typically indirectly) calling *sizeRequest* / *getExtremes* for
+direct ancestors.
+</table>
+
+Furthermore, *sizeAllocate* can only be called within a call of
+dw::core::Layout::resizeIdleId, so (if you do not touch dw::core) do
+not call it outside of *sizeAllocateImpl*. The other methods can be
+called outsize; e.&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..5306c5a5 100644
--- a/dw/Makefile.am
+++ b/dw/Makefile.am
@@ -1,7 +1,7 @@
AM_CPPFLAGS = \
-I$(top_srcdir) \
- -DDILLO_LIBDIR='"$(pkglibdir)/"'
-
+ -DDILLO_LIBDIR='"$(pkglibdir)/"' \
+ -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/dw"'
noinst_LIBRARIES = \
libDw-core.a \
@@ -67,6 +67,8 @@ libDw_widgets_a_SOURCES = \
image.hh \
listitem.cc \
listitem.hh \
+ outofflowmgr.cc \
+ outofflowmgr.hh \
ruler.cc \
ruler.hh \
table.cc \
diff --git a/dw/findtext.cc b/dw/findtext.cc
index cc57e991..e9384917 100644
--- a/dw/findtext.cc
+++ b/dw/findtext.cc
@@ -96,7 +96,7 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens,
if (iterator)
delete iterator;
- iterator = new CharIterator (widget);
+ iterator = new CharIterator (widget, true);
if (backwards) {
/* Go to end */
@@ -128,7 +128,7 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens,
} else {
// Nothing found anymore, reset the state for the next trial.
delete iterator;
- iterator = new CharIterator (widget);
+ iterator = new CharIterator (widget, true);
if (backwards) {
/* Go to end */
while (iterator->next ()) ;
diff --git a/dw/fltkui.cc b/dw/fltkui.cc
index 58bb2c6f..bfc07528 100644
--- a/dw/fltkui.cc
+++ b/dw/fltkui.cc
@@ -145,6 +145,8 @@ using namespace lout::container::typed;
FltkResource::FltkResource (FltkPlatform *platform)
{
+ DBG_OBJ_CREATE ("dw::fltk::ui::FltkResource");
+
this->platform = platform;
allocation.x = 0;
@@ -180,6 +182,8 @@ FltkResource::~FltkResource ()
}
if (style)
style->unref ();
+
+ DBG_OBJ_DELETE ();
}
void FltkResource::attachView (FltkView *view)
@@ -209,6 +213,10 @@ void FltkResource::detachView (FltkView *view)
void FltkResource::sizeAllocate (core::Allocation *allocation)
{
+ DBG_OBJ_MSGF ("resize", 0, "<b>sizeAllocate</b> (%d, %d; %d * (%d + %d))",
+ allocation->x, allocation->y, allocation->width,
+ allocation->ascent, allocation->descent);
+
this->allocation = *allocation;
view->allocateFltkWidget (widget, allocation);
}
@@ -293,6 +301,20 @@ void FltkResource::setEnabled (bool enabled)
// ----------------------------------------------------------------------
+template <class I> FltkSpecificResource<I>::FltkSpecificResource (FltkPlatform
+ *platform) :
+ FltkResource (platform)
+{
+ DBG_OBJ_CREATE ("dw::fltk::ui::FltkSpecificResource<>");
+ DBG_OBJ_BASECLASS (I);
+ DBG_OBJ_BASECLASS (FltkResource);
+}
+
+template <class I> FltkSpecificResource<I>::~FltkSpecificResource ()
+{
+ DBG_OBJ_DELETE ();
+}
+
template <class I> void FltkSpecificResource<I>::sizeAllocate (core::Allocation
*allocation)
{
@@ -367,6 +389,9 @@ Fl_Widget *FltkLabelButtonResource::createNewWidget (core::Allocation
void FltkLabelButtonResource::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>");
+ DBG_OBJ_MSG_START ();
+
if (style) {
FltkFont *font = (FltkFont*)style->font;
fl_font(font->font,font->size);
@@ -380,6 +405,10 @@ void FltkLabelButtonResource::sizeRequest (core::Requisition *requisition)
requisition->ascent = 1;
requisition->descent = 0;
}
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+ DBG_OBJ_MSG_END ();
}
/*
@@ -513,6 +542,17 @@ void FltkComplexButtonResource::sizeAllocate (core::Allocation *allocation)
{
FltkResource::sizeAllocate (allocation);
+ DBG_OBJ_MSGF_O ("resize", 0, flatView,
+ "<b>resize</b> (%d %d, <i>%d - 2 * %d =</i> %d, "
+ "<i>%d + %d - 2 * %d =</i> %d)",
+ reliefXThickness (), reliefYThickness (),
+ allocation->width, reliefXThickness (),
+ allocation->width - 2 * reliefXThickness (),
+ allocation->ascent, allocation->descent,
+ reliefYThickness (),
+ allocation->ascent + allocation->descent
+ - 2 * reliefYThickness ());
+
((FltkFlatView*)flatView)->resize (
reliefXThickness (), reliefYThickness (),
allocation->width - 2 * reliefXThickness (),
@@ -634,6 +674,9 @@ void FltkEntryResource::setDisplayed(bool displayed)
void FltkEntryResource::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>");
+ DBG_OBJ_MSG_START ();
+
if (displayed() && style) {
FltkFont *font = (FltkFont*)style->font;
fl_font(font->font,font->size);
@@ -650,6 +693,10 @@ void FltkEntryResource::sizeRequest (core::Requisition *requisition)
requisition->ascent = 0;
requisition->descent = 0;
}
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+ DBG_OBJ_MSG_END ();
}
void FltkEntryResource::sizeAllocate (core::Allocation *allocation)
@@ -657,6 +704,11 @@ void FltkEntryResource::sizeAllocate (core::Allocation *allocation)
if (!label) {
FltkResource::sizeAllocate(allocation);
} else {
+ DBG_OBJ_MSGF ("resize", 0,
+ "<b>sizeAllocate</b> (%d, %d; %d * (%d + %d))",
+ allocation->x, allocation->y, allocation->width,
+ allocation->ascent, allocation->descent);
+
this->allocation = *allocation;
/* push the Fl_Input over to the right of the label */
@@ -779,6 +831,9 @@ void FltkMultiLineTextResource::setWidgetStyle (Fl_Widget *widget,
void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>");
+ DBG_OBJ_MSG_START ();
+
if (style) {
FltkFont *font = (FltkFont*)style->font;
fl_font(font->font,font->size);
@@ -797,6 +852,10 @@ void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition)
requisition->ascent = 1;
requisition->descent = 0;
}
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+ DBG_OBJ_MSG_END ();
}
const char *FltkMultiLineTextResource::getText ()
@@ -864,6 +923,9 @@ void FltkToggleButtonResource<I>::setWidgetStyle (Fl_Widget *widget,
template <class I>
void FltkToggleButtonResource<I>::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>");
+ DBG_OBJ_MSG_START ();
+
FltkFont *font = (FltkFont *)
(this->FltkResource::style ? this->FltkResource::style->font : NULL);
@@ -877,6 +939,10 @@ void FltkToggleButtonResource<I>::sizeRequest (core::Requisition *requisition)
requisition->ascent = 1;
requisition->descent = 0;
}
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+ DBG_OBJ_MSG_END ();
}
@@ -1116,6 +1182,9 @@ int FltkOptionMenuResource::getMaxItemWidth()
void FltkOptionMenuResource::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>");
+ DBG_OBJ_MSG_START ();
+
if (style) {
FltkFont *font = (FltkFont*)style->font;
fl_font(font->font, font->size);
@@ -1130,6 +1199,10 @@ void FltkOptionMenuResource::sizeRequest (core::Requisition *requisition)
requisition->ascent = 1;
requisition->descent = 0;
}
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+ DBG_OBJ_MSG_END ();
}
void FltkOptionMenuResource::enlargeMenu ()
@@ -1405,6 +1478,9 @@ int FltkListResource::getMaxItemWidth()
void FltkListResource::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>");
+ DBG_OBJ_MSG_START ();
+
if (style) {
CustBrowser *b = (CustBrowser *) widget;
int height = b->full_height();
@@ -1425,6 +1501,10 @@ void FltkListResource::sizeRequest (core::Requisition *requisition)
requisition->ascent = 1;
requisition->descent = 0;
}
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+ DBG_OBJ_MSG_END ();
}
int FltkListResource::getNumberOfItems()
diff --git a/dw/fltkui.hh b/dw/fltkui.hh
index 8667a0cb..a0d2d7b7 100644
--- a/dw/fltkui.hh
+++ b/dw/fltkui.hh
@@ -223,8 +223,8 @@ public:
template <class I> class FltkSpecificResource: public I, public FltkResource
{
public:
- inline FltkSpecificResource (FltkPlatform *platform) :
- FltkResource (platform) { }
+ FltkSpecificResource (FltkPlatform *platform);
+ ~FltkSpecificResource ();
void sizeAllocate (core::Allocation *allocation);
void draw (core::View *view, core::Rectangle *area);
diff --git a/dw/iterator.cc b/dw/iterator.cc
index 18d7cd5a..18d62a49 100644
--- a/dw/iterator.cc
+++ b/dw/iterator.cc
@@ -55,6 +55,24 @@ bool Iterator::equals (Object *other)
(getWidget() == otherIt->getWidget() && compareTo(otherIt) == 0);
}
+void Iterator::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append ("{ widget = ");
+ //widget->intoStringBuffer (sb);
+ sb->appendPointer (widget);
+ sb->append (" (");
+ sb->append (widget->getClassName());
+ sb->append (")>");
+
+ sb->append (", mask = ");
+ Content::maskIntoStringBuffer (mask, sb);
+
+ sb->append (", content = ");
+ Content::intoStringBuffer (&content, sb);
+
+ sb->append (" }");
+}
+
/**
* \brief Delete the iterator.
*
@@ -186,6 +204,14 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end,
}
}
+
+void Iterator::print ()
+{
+ misc::StringBuffer sb;
+ intoStringBuffer (&sb);
+ printf ("%s", sb.getChars ());
+}
+
// -------------------
// EmptyIterator
// -------------------
@@ -343,7 +369,7 @@ Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask,
//DEBUG_MSG (1, "%*smoving down (%swards) from %s\n",
// indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
- assert (it->getContent()->type == Content::WIDGET);
+ assert (it->getContent()->type & Content::ANY_WIDGET);
it2 = it->getContent()->widget->iterator (mask, fromEnd);
if (it2 == NULL) {
@@ -356,7 +382,7 @@ Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask,
//DEBUG_MSG (1, "%*sexamining %s\n",
// indent, "", a_Dw_iterator_text (it2));
- if (it2->getContent()->type == Content::WIDGET) {
+ if (it2->getContent()->type & Content::ANY_WIDGET) {
// Another widget. Search in it downwards.
it3 = searchDownward (it2, mask, fromEnd);
if (it3 != NULL) {
@@ -390,11 +416,11 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
//DEBUG_MSG (1, "%*smoving %swards from %s\n",
// indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
- assert (it->getContent()->type == Content::WIDGET);
+ assert (it->getContent()->type & Content::ANY_WIDGET);
it2 = it->cloneIterator ();
while (fromEnd ? it2->prev () : it2->next ()) {
- if (it2->getContent()->type == Content::WIDGET) {
+ if (it2->getContent()->type & Content::ANY_WIDGET) {
// Search downwards in this widget.
it3 = searchDownward (it2, mask, fromEnd);
if (it3 != NULL) {
@@ -416,13 +442,14 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
/* Nothing found, go upwards in the tree (if possible). */
it2->unref ();
- if (it->getWidget()->getParent ()) {
- it2 = it->getWidget()->getParent()->iterator (mask, false);
+ Widget *respParent = getRespectiveParent (it->getWidget(), it->getMask());
+ if (respParent) {
+ it2 = respParent->iterator (mask, false);
while (true) {
if (!it2->next ())
misc::assertNotReached ();
- if (it2->getContent()->type == Content::WIDGET &&
+ if (it2->getContent()->type & Content::ANY_WIDGET &&
it2->getContent()->widget == it->getWidget ()) {
it3 = searchSideward (it2, mask, fromEnd);
it2->unref ();
@@ -440,6 +467,27 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
return NULL;
}
+Widget *DeepIterator::getRespectiveParent (Widget *widget, Content::Type mask)
+{
+ // Return, depending on which is requested indirectly (follow
+ // references or containments) the parent (container) or the
+ // generator. At this point, the type of the parent/generator is
+ // not known (since the parent/generator is not known), so we have
+ // to examine the mask. This is the reason why only one of
+ // WIDGET_OOF_CONT and WIDGET_OOF_REF is allowed.
+
+ return (mask & Content::WIDGET_OOF_REF) ?
+ widget->getGenerator() : widget->getParent();
+}
+
+int DeepIterator::getRespectiveLevel (Widget *widget, Content::Type mask)
+{
+ // Similar to getRespectiveParent.
+
+ return (mask & Content::WIDGET_OOF_REF) ?
+ widget->getGeneratorLevel() : widget->getLevel();
+}
+
/**
* \brief Create a new deep iterator from an existing dw::core::Iterator.
*
@@ -456,6 +504,19 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
*/
DeepIterator::DeepIterator (Iterator *it)
{
+ //printf ("Starting creating DeepIterator %p ...\n", this);
+ //printf ("Initial iterator: ");
+ //it->print ();
+ //printf ("\n");
+
+ // Widgets out of flow are either followed widtin containers, or
+ // generators. Both (and also nothing at all) is not allowed. See
+ // also comment in getRespectiveParent.
+ int oofMask =
+ it->getMask() & (Content::WIDGET_OOF_CONT | Content::WIDGET_OOF_REF);
+ assert (oofMask == Content::WIDGET_OOF_CONT ||
+ oofMask == Content::WIDGET_OOF_REF);
+
//DEBUG_MSG (1, "a_Dw_ext_iterator_new: %s\n", a_Dw_iterator_text (it));
// Clone input iterator, so the iterator passed as parameter
@@ -467,7 +528,7 @@ DeepIterator::DeepIterator (Iterator *it)
// If it points to a widget, find a near non-widget content,
// since an DeepIterator should never return widgets.
- if (it->getContent()->type == Content::WIDGET) {
+ if (it->getContent()->type & Content::ANY_WIDGET) {
Iterator *it2;
// The second argument of searchDownward is actually a matter of
@@ -494,31 +555,50 @@ DeepIterator::DeepIterator (Iterator *it)
// \todo There may be a faster way instead of iterating through the
// parent widgets.
+ //printf ("Starting with: ");
+ //it->print ();
+ //printf ("\n");
+
// Construct the iterators.
- int thisLevel = it->getWidget()->getLevel (), level;
+ int thisLevel = getRespectiveLevel (it->getWidget()), level;
Widget *w;
- for (w = it->getWidget (), level = thisLevel; w->getParent() != NULL;
- w = w->getParent (), level--) {
- Iterator *it = w->getParent()->iterator (mask, false);
+ for (w = it->getWidget (), level = thisLevel;
+ getRespectiveParent (w) != NULL;
+ w = getRespectiveParent (w), level--) {
+ Iterator *it = getRespectiveParent(w)->iterator (mask, false);
+
+ //printf (" parent: %s %p\n", w->getClassName (), w);
+
stack.put (it, level - 1);
while (true) {
+ //printf (" ");
+ //it->print ();
+ //printf ("\n");
+
bool hasNext = it->next();
assert (hasNext);
- if (it->getContent()->type == Content::WIDGET &&
+ if (it->getContent()->type & Content::ANY_WIDGET &&
it->getContent()->widget == w)
break;
}
+
+ //printf (" %d: ", level - 1);
+ //it->print ();
+ //printf ("\n");
}
stack.put (it, thisLevel);
content = *(it->getContent());
}
+
+ //printf ("... done creating DeepIterator %p.\n", this);
}
DeepIterator::~DeepIterator ()
{
+ //printf ("Deleting DeepIterator %p ...\n", this);
}
object::Object *DeepIterator::clone ()
@@ -539,9 +619,29 @@ int DeepIterator::compareTo (object::Comparable *other)
{
DeepIterator *otherDeepIterator = (DeepIterator*)other;
+ //printf ("Compare: %s\n", stack.toString ());
+ //printf (" to: %s\n", otherDeepIterator->stack.toString ());
+
// Search the highest level, where the widgets are the same.
int level = 0;
+ // The Comparable interface does not define "uncomparable". Deep
+ // iterators are only comparable if they belong to the same widget
+ // tree, so have the same widget at the bottom at the
+ // stack. If this is not the case, we abort.
+
+ assert (stack.size() > 0);
+ assert (otherDeepIterator->stack.size() > 0);
+
+ //printf ("Equal? The %s %p (of %p) and the %s %p (of %p)?\n",
+ // stack.get(0)->getWidget()->getClassName(),
+ // stack.get(0)->getWidget(), this,
+ // otherDeepIterator->stack.get(0)->getWidget()->getClassName(),
+ // otherDeepIterator->stack.get(0)->getWidget(), otherDeepIterator);
+
+ assert (stack.get(0)->getWidget()
+ == otherDeepIterator->stack.get(level)->getWidget());
+
while (stack.get(level)->getWidget ()
== otherDeepIterator->stack.get(level)->getWidget ()) {
if (level == stack.size() - 1 ||
@@ -550,10 +650,14 @@ int DeepIterator::compareTo (object::Comparable *other)
level++;
}
+ //printf (" => level = %d (temorally)\n", level);
+
while (stack.get(level)->getWidget ()
!= otherDeepIterator->stack.get(level)->getWidget ())
level--;
+ //printf (" => level = %d (finally)\n", level);
+
return stack.get(level)->compareTo (otherDeepIterator->stack.get(level));
}
@@ -577,7 +681,7 @@ bool DeepIterator::next ()
Iterator *it = stack.getTop ();
if (it->next ()) {
- if (it->getContent()->type == Content::WIDGET) {
+ if (it->getContent()->type & Content::ANY_WIDGET) {
// Widget: new iterator on stack, to search in this widget.
stack.push (it->getContent()->widget->iterator (mask, false));
return next ();
@@ -610,7 +714,7 @@ bool DeepIterator::prev ()
Iterator *it = stack.getTop ();
if (it->prev ()) {
- if (it->getContent()->type == Content::WIDGET) {
+ if (it->getContent()->type & Content::ANY_WIDGET) {
// Widget: new iterator on stack, to search in this widget.
stack.push (it->getContent()->widget->iterator (mask, true));
return prev ();
@@ -642,9 +746,17 @@ CharIterator::CharIterator ()
it = NULL;
}
-CharIterator::CharIterator (Widget *widget)
+/**
+ * \brief ...
+ *
+ * If followReferences is true, only the reference are followed, when
+ * the container and generator for a widget is different. If false,
+ * only the container is followed.
+ */
+CharIterator::CharIterator (Widget *widget, bool followReferences)
{
- Iterator *i = widget->iterator (Content::SELECTION_CONTENT, false);
+ Iterator *i =
+ widget->iterator (Content::maskForSelection (followReferences), false);
it = new DeepIterator (i);
i->unref ();
ch = START;
diff --git a/dw/iterator.hh b/dw/iterator.hh
index d086721c..abf31d0b 100644
--- a/dw/iterator.hh
+++ b/dw/iterator.hh
@@ -31,6 +31,7 @@ private:
public:
bool equals (Object *other);
+ void intoStringBuffer(lout::misc::StringBuffer *sb);
inline Widget *getWidget () { return widget; }
inline Content *getContent () { return &content; }
@@ -85,6 +86,8 @@ public:
static void scrollTo (Iterator *it1, Iterator *it2, int start, int end,
HPosition hpos, VPosition vpos);
+
+ virtual void print ();
};
@@ -168,6 +171,16 @@ private:
inline DeepIterator () { }
+ static Widget *getRespectiveParent (Widget *widget, Content::Type mask);
+ inline Widget *getRespectiveParent (Widget *widget) {
+ return getRespectiveParent (widget, mask);
+ }
+
+ static int getRespectiveLevel (Widget *widget, Content::Type mask);
+ inline int getRespectiveLevel (Widget *widget) {
+ return getRespectiveLevel (widget, mask);
+ }
+
public:
DeepIterator(Iterator *it);
~DeepIterator();
@@ -230,7 +243,7 @@ private:
CharIterator ();
public:
- CharIterator (Widget *widget);
+ CharIterator (Widget *widget, bool followReferences);
~CharIterator ();
lout::object::Object *clone();
diff --git a/dw/layout.cc b/dw/layout.cc
index 6f2e8d8b..289b204b 100644
--- a/dw/layout.cc
+++ b/dw/layout.cc
@@ -245,6 +245,9 @@ Layout::Layout (Platform *platform)
topLevel = NULL;
widgetAtPoint = NULL;
+ queueQueueResizeList = new typed::Vector<QueueResizeItem> (4, true);
+ queueResizeList = new typed::Vector<Widget> (4, false);
+
DBG_OBJ_CREATE ("dw::core::Layout");
bgColor = NULL;
@@ -277,7 +280,13 @@ Layout::Layout (Platform *platform)
selectionState.setLayout(this);
+ queueResizeCounter = sizeAllocateCounter = sizeRequestCounter =
+ getExtremesCounter = 0;
+
layoutImgRenderer = NULL;
+
+ resizeIdleCounter = queueResizeCounter = sizeAllocateCounter
+ = sizeRequestCounter = getExtremesCounter = 0;
}
Layout::~Layout ()
@@ -299,10 +308,14 @@ Layout::~Layout ()
if (bgImage)
bgImage->unref ();
if (topLevel) {
+ detachWidget (topLevel);
Widget *w = topLevel;
topLevel = NULL;
delete w;
}
+
+ delete queueQueueResizeList;
+ delete queueResizeList;
delete platform;
delete view;
delete anchorsTable;
@@ -311,6 +324,26 @@ Layout::~Layout ()
DBG_OBJ_DELETE ();
}
+void Layout::detachWidget (Widget *widget)
+{
+ // Called form ~Layout. Sometimes, the widgets (not only the toplevel widget)
+ // do some stuff after the layout has been deleted, so *all* widgets have to
+ // be detached, and check "layout != NULL" at relevant points.
+
+ // Could be replaced by a virtual method in Widget, like getWidgetAtPoint,
+ // if performace were really a problem.
+
+ widget->layout = NULL;
+ Iterator *it =
+ widget->iterator ((Content::Type)
+ (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT),
+ false);
+ while (it->next ())
+ detachWidget (it->getContent()->widget);
+
+ it->unref ();
+}
+
void Layout::addWidget (Widget *widget)
{
if (topLevel) {
@@ -320,12 +353,18 @@ void Layout::addWidget (Widget *widget)
topLevel = widget;
widget->layout = this;
+ queueResizeList->clear ();
+ widget->notifySetAsTopLevel();
findtextState.setWidget (widget);
canvasHeightGreater = false;
setSizeHints ();
- queueResize ();
+
+ // Do not directly call Layout::queueResize(), but
+ // Widget::queueResize(), so that all flags are set properly,
+ // queueResizeList is filled, etc.
+ topLevel->queueResize (-1, false);
}
void Layout::removeWidget ()
@@ -334,6 +373,7 @@ void Layout::removeWidget ()
* \bug Some more attributes must be reset here.
*/
topLevel = NULL;
+ queueResizeList->clear ();
widgetAtPoint = NULL;
canvasWidth = canvasAscent = canvasDescent = 0;
scrollX = scrollY = 0;
@@ -776,11 +816,41 @@ void Layout::setBgImage (style::StyleImage *bgImage,
void Layout::resizeIdle ()
{
+ DBG_OBJ_MSG ("resize", 0, "<b>resizeIdle</b>");
+ DBG_OBJ_MSG_START ();
+
+ enterResizeIdle ();
+
//static int calls = 0;
- //MSG(" Layout::resizeIdle calls = %d\n", ++calls);
+ //printf ("Layout::resizeIdle calls = %d\n", ++calls);
assert (resizeIdleId != -1);
+ for (typed::Iterator <Widget> it = queueResizeList->iterator();
+ it.hasNext (); ) {
+ Widget *widget = it.getNext ();
+
+ //printf (" the %stop-level %s %p was queued (extremes changed: %s)\n",
+ // widget->parent ? "non-" : "", widget->getClassName(), widget,
+ // widget->extremesQueued () ? "yes" : "no");
+
+ if (widget->resizeQueued ()) {
+ widget->setFlags (Widget::NEEDS_RESIZE);
+ widget->unsetFlags (Widget::RESIZE_QUEUED);
+ }
+
+ if (widget->allocateQueued ()) {
+ widget->setFlags (Widget::NEEDS_ALLOCATE);
+ widget->unsetFlags (Widget::ALLOCATE_QUEUED);
+ }
+
+ if (widget->extremesQueued ()) {
+ widget->setFlags (Widget::EXTREMES_CHANGED);
+ widget->unsetFlags (Widget::EXTREMES_QUEUED);
+ }
+ }
+ queueResizeList->clear ();
+
// Reset already here, since in this function, queueResize() may be
// called again.
resizeIdleId = -1;
@@ -790,7 +860,15 @@ void Layout::resizeIdle ()
Allocation allocation;
topLevel->sizeRequest (&requisition);
-
+ DBG_OBJ_MSGF ("resize", 1, "toplevel size: %d * (%d + %d)",
+ requisition.width, requisition.ascent, requisition.descent);
+
+ // This method is triggered by Widget::queueResize, which will,
+ // in any case, set NEEDS_ALLOCATE (indirectly, as
+ // ALLOCATE_QUEUED). This assertion helps to find
+ // inconsistences.
+ assert (topLevel->needsAllocate ());
+
allocation.x = allocation.y = 0;
allocation.width = requisition.width;
allocation.ascent = requisition.ascent;
@@ -825,10 +903,15 @@ void Layout::resizeIdle ()
}
// views are redrawn via Widget::resizeDrawImpl ()
-
}
updateAnchor ();
+
+ DBG_OBJ_MSGF ("resize", 1,
+ "after resizeIdle: resizeIdleId = %d", resizeIdleId);
+ DBG_OBJ_MSG_END ();
+
+ leaveResizeIdle ();
}
void Layout::setSizeHints ()
@@ -879,11 +962,17 @@ void Layout::queueDrawExcept (int x, int y, int width, int height,
void Layout::queueResize ()
{
+ DBG_OBJ_MSG ("resize", 0, "<b>queueResize</b>");
+ DBG_OBJ_MSG_START ();
+
if (resizeIdleId == -1) {
view->cancelQueueDraw ();
resizeIdleId = platform->addIdle (&Layout::resizeIdle);
+ DBG_OBJ_MSGF ("resize", 1, "setting resizeIdleId = %d", resizeIdleId);
}
+
+ DBG_OBJ_MSG_END ();
}
@@ -1164,8 +1253,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..6b6ceadb 100644
--- a/dw/layout.hh
+++ b/dw/layout.hh
@@ -150,9 +150,26 @@ private:
~Anchor ();
};
+ class QueueResizeItem: public lout::object::Object
+ {
+ public:
+ Widget *widget;
+ int ref;
+ bool extremesChanged;
+
+ inline QueueResizeItem (Widget *widget, int ref, bool extremesChanged)
+ {
+ this->widget = widget;
+ this->ref = ref;
+ this->extremesChanged = extremesChanged;
+ }
+ };
+
Platform *platform;
View *view;
Widget *topLevel, *widgetAtPoint;
+ lout::container::typed::Vector<QueueResizeItem> *queueQueueResizeList;
+ lout::container::typed::Vector<Widget> *queueResizeList;
/* The state, which must be projected into the view. */
style::Color *bgColor;
@@ -186,6 +203,8 @@ private:
enum ButtonEventType { BUTTON_PRESS, BUTTON_RELEASE, MOTION_NOTIFY };
+ void detachWidget (Widget *widget);
+
Widget *getWidgetAtPoint (int x, int y);
void moveToWidget (Widget *newWidgetAtPoint, ButtonState state);
@@ -236,6 +255,16 @@ private:
void queueResize ();
void removeWidget ();
+ /* For tests regarding the respective Layout and (mostly) Widget
+ methods. Accessed by respective methods (enter..., leave...,
+ ...Entered) defined here and in Widget. */
+
+ int resizeIdleCounter, queueResizeCounter, sizeAllocateCounter,
+ sizeRequestCounter, getExtremesCounter;
+
+ void enterResizeIdle () { resizeIdleCounter++; }
+ void leaveResizeIdle () { resizeIdleCounter--; }
+
public:
Layout (Platform *platform);
~Layout ();
diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc
new file mode 100644
index 00000000..0c9a92be
--- /dev/null
+++ b/dw/outofflowmgr.cc
@@ -0,0 +1,2114 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2013 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "outofflowmgr.hh"
+#include "textblock.hh"
+#include "../lout/debug.hh"
+
+using namespace lout::object;
+using namespace lout::container::typed;
+using namespace lout::misc;
+using namespace dw::core;
+using namespace dw::core::style;
+
+namespace dw {
+
+OutOfFlowMgr::WidgetInfo::WidgetInfo (OutOfFlowMgr *oofm, Widget *widget)
+{
+ this->oofm = oofm;
+ this->widget = widget;
+ wasAllocated = false;
+ xCB = yCB = width = height = -1;
+}
+
+void OutOfFlowMgr::WidgetInfo::update (bool wasAllocated, int xCB, int yCB,
+ int width, int height)
+{
+ DBG_OBJ_MSGF_O ("resize.oofm", 0, widget,
+ "<b>update</b> (%s, %d, %d, %d, %d)",
+ wasAllocated ? "true" : "false", xCB, yCB, width, height);
+
+ this->wasAllocated = wasAllocated;
+ this->xCB = xCB;
+ this->yCB = yCB;
+ this->width = width;
+ this->height = height;
+
+ DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.xCB", xCB);
+ DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.yCB", yCB);
+ DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.width", width);
+ DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.height", height);
+}
+
+void OutOfFlowMgr::WidgetInfo::updateAllocation ()
+{
+ DBG_OBJ_MSG_O ("resize.oofm", 0, widget, "<b>updateAllocation</b> ()");
+ DBG_OBJ_MSG_START_O (widget);
+
+ update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (),
+ getNewHeight ());
+
+ DBG_OBJ_MSG_END_O (widget);
+}
+
+// ----------------------------------------------------------------------
+
+OutOfFlowMgr::Float::Float (OutOfFlowMgr *oofm, Widget *widget,
+ Textblock *generatingBlock, int externalIndex) :
+ WidgetInfo (oofm, widget)
+{
+ this->generatingBlock = generatingBlock;
+ this->externalIndex = externalIndex;
+
+ yReq = yReal = size.width = size.ascent = size.descent = 0;
+ dirty = sizeChangedSinceLastAllocation = true;
+ inCBList = false;
+
+ // Sometimes a float with widget = NULL is created as a key; this
+ // is not interesting for RTFL.
+ if (widget) {
+ DBG_OBJ_SET_NUM_O (widget, "<Float>.yReq", yReq);
+ DBG_OBJ_SET_NUM_O (widget, "<Float>.yReal", yReal);
+ DBG_OBJ_SET_NUM_O (widget, "<Float>.size.width", size.width);
+ DBG_OBJ_SET_NUM_O (widget, "<Float>.size.ascent", size.ascent);
+ DBG_OBJ_SET_NUM_O (widget, "<Float>.size.descent", size.descent);
+ }
+}
+
+void OutOfFlowMgr::Float::intoStringBuffer(StringBuffer *sb)
+{
+ sb->append ("{ widget = ");
+ sb->appendPointer (getWidget ());
+
+ if (getWidget ()) {
+ sb->append (" (");
+ sb->append (getWidget()->getClassName ());
+ sb->append (")");
+ }
+
+ sb->append (", index = ");
+ sb->appendInt (index);
+ sb->append (", sideSpanningIndex = ");
+ sb->appendInt (sideSpanningIndex);
+ sb->append (", generatingBlock = ");
+ sb->appendPointer (generatingBlock);
+ sb->append (", yReq = ");
+ sb->appendInt (yReq);
+ sb->append (", yReal = ");
+ sb->appendInt (yReal);
+ sb->append (", size = { ");
+ sb->appendInt (size.width);
+ sb->append (" * ");
+ sb->appendInt (size.ascent);
+ sb->append (" + ");
+ sb->appendInt (size.descent);
+ sb->append (" }, dirty = ");
+ sb->appendBool (dirty);
+ sb->append (", sizeChangedSinceLastAllocation = ");
+ sb->appendBool (sizeChangedSinceLastAllocation);
+ sb->append (", inCBList = ");
+ sb->appendBool (inCBList);
+ sb->append (" }");
+}
+
+bool OutOfFlowMgr::Float::covers (Textblock *textblock, int y, int h)
+{
+ DBG_OBJ_MSGF_O ("border", 0, getOutOfFlowMgr (),
+ "<b>covers</b> (%p, %d, %d) [vloat: %p]",
+ textblock, y, h, getWidget ());
+ DBG_OBJ_MSG_START_O (getOutOfFlowMgr ());
+
+ bool b;
+
+ if (textblock == generatingBlock) {
+ int reqyGB = y;
+ int flyGB = yReal;
+ getOutOfFlowMgr()->ensureFloatSize (this);
+ int flh = size.ascent + size.descent;
+ b = flyGB + flh > reqyGB && flyGB < reqyGB + h;
+
+ DBG_OBJ_MSGF_O ("border", 1, getOutOfFlowMgr (),
+ "for generator: reqyGB = %d, flyGB = %d, "
+ "flh = %d + %d = %d => %s",
+ reqyGB, flyGB, size.ascent, size.descent, flh,
+ b ? "true" : "false");
+ } else {
+ assert (getOutOfFlowMgr()->wasAllocated (generatingBlock));
+ assert (getOutOfFlowMgr()->wasAllocated (textblock));
+
+ if (!getWidget()->wasAllocated ()) {
+ DBG_OBJ_MSG_O ("border", 1, getOutOfFlowMgr (),
+ "not generator (not allocated) => false");
+ b = false;
+ } else {
+ Allocation *tba = getOutOfFlowMgr()->getAllocation(textblock),
+ //*gba = getOutOfFlowMgr()->getAllocation(generatingBlock),
+ *fla = getWidget()->getAllocation ();
+ int reqyCanv = tba->y + y;
+ int flyCanv = fla->y;
+ int flh = fla->ascent + fla->descent;
+ b = flyCanv + flh > reqyCanv && flyCanv < reqyCanv + h;
+
+ DBG_OBJ_MSGF_O ("border", 1, getOutOfFlowMgr (),
+ "not generator (allocated): reqyCanv = %d + %d = %d, "
+ "flyCanv = %d, flh = %d + %d = %d => %s",
+ tba->y, y, reqyCanv, flyCanv,
+ fla->ascent, fla->descent, flh, b ? "true" : "false");
+ }
+ }
+
+ DBG_OBJ_MSG_END_O (getOutOfFlowMgr ());
+
+ return b;
+}
+
+int OutOfFlowMgr::Float::ComparePosition::compare (Object *o1, Object *o2)
+{
+ Float *fl1 = (Float*)o1, *fl2 = (Float*)o2;
+ int r;
+
+ DBG_OBJ_MSGF_O ("border", 1, oofm,
+ "<b>ComparePosition::compare</b> (#%d, #%d) [refTB = %p]",
+ fl1->index, fl2->index, refTB);
+ DBG_OBJ_MSG_START_O (oofm);
+
+ if (refTB == fl1->generatingBlock && refTB == fl2->generatingBlock) {
+ DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is generating both floats");
+ r = fl1->yReal - fl2->yReal;
+ } else {
+ DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is not generating both floats");
+ DBG_OBJ_MSG_START_O (oofm);
+
+ assert (oofm->wasAllocated (fl1->generatingBlock));
+ assert (oofm->wasAllocated (fl2->generatingBlock));
+
+ DBG_OBJ_MSGF_O ("border", 2, oofm, "generators are %p and %p",
+ fl1->generatingBlock, fl2->generatingBlock);
+
+ // (i) Floats may not yet been allocated (although the
+ // generators are). Non-allocated floats do not have an effect
+ // yet, they are considered "at the end" of the list.
+
+ // (ii) Float::widget for the key used for binary search. In
+ // this case, Float::yReal is used instead (which is set in
+ // SortedFloatsVector::find).
+
+ bool a1 = fl1->getWidget () ? fl1->getWidget()->wasAllocated () : true;
+ bool a2 = fl2->getWidget () ? fl2->getWidget()->wasAllocated () : true;
+
+ DBG_OBJ_MSGF_O ("border", 2, oofm,
+ "float 1 allocated: %s; float 2 allocated: %s",
+ a1 ? "yes" : "no", a2 ? "yes" : "no");
+
+ if (a1 && a2) {
+ int fly1 = fl1->getWidget() ? fl1->getWidget()->getAllocation()->y :
+ oofm->getAllocation(fl1->generatingBlock)->y + fl1->yReal;
+ int fly2 = fl2->getWidget() ? fl2->getWidget()->getAllocation()->y :
+ oofm->getAllocation(fl2->generatingBlock)->y + fl2->yReal;
+ DBG_OBJ_MSGF_O ("border", 2, oofm, "y diff = %d - %d", fly1, fly2);
+ r = fly1 - fly2;
+ } else if (a1 && !a2)
+ r = -1;
+ else if (!a1 && a2)
+ r = +1;
+ else // if (!a1 && !a2)
+ return 0;
+
+ DBG_OBJ_MSG_END_O (oofm);
+ }
+
+ DBG_OBJ_MSGF_O ("border", 1, oofm, "result: %d", r);
+ DBG_OBJ_MSG_END_O (oofm);
+ return r;
+}
+
+int OutOfFlowMgr::Float::CompareSideSpanningIndex::compare (Object *o1,
+ Object *o2)
+{
+ return ((Float*)o1)->sideSpanningIndex - ((Float*)o2)->sideSpanningIndex;
+}
+
+int OutOfFlowMgr::Float::CompareGBAndExtIndex::compare (Object *o1, Object *o2)
+{
+ Float *f1 = (Float*)o1, *f2 = (Float*)o2;
+ int r = -123; // Compiler happiness: GCC 4.7 does not handle this?;
+
+ DBG_OBJ_MSGF_O ("border", 1, oofm,
+ "<b>CompareGBAndExtIndex::compare</b> (#%d -> %p/%d, "
+ "#%d -> %p/#%d)",
+ f1->index, f1->generatingBlock, f1->externalIndex,
+ f2->index, f2->generatingBlock, f2->externalIndex);
+ DBG_OBJ_MSG_START_O (oofm);
+
+ if (f1->generatingBlock == f2->generatingBlock) {
+ r = f1->externalIndex - f2->externalIndex;
+ DBG_OBJ_MSGF_O ("border", 2, oofm,
+ "(a) generating blocks equal => %d - %d = %d",
+ f1->externalIndex, f2->externalIndex, r);
+ } else {
+ TBInfo *t1 = oofm->getTextblock (f1->generatingBlock),
+ *t2 = oofm->getTextblock (f2->generatingBlock);
+ bool rdef = false;
+
+ for (TBInfo *t = t1; t != NULL; t = t->parent)
+ if (t->parent == t2) {
+ rdef = true;
+ r = t->parentExtIndex - f2->externalIndex;
+ DBG_OBJ_MSGF_O ("border", 2, oofm,
+ "(b) %p is an achestor of %p; direct child is "
+ "%p (%d) => %d - %d = %d\n",
+ t2->getTextblock (), t1->getTextblock (),
+ t->getTextblock (), t->parentExtIndex,
+ t->parentExtIndex, f2->externalIndex, r);
+ }
+
+ for (TBInfo *t = t2; !rdef && t != NULL; t = t->parent)
+ if (t->parent == t1) {
+ r = f1->externalIndex - t->parentExtIndex;
+ rdef = true;
+ DBG_OBJ_MSGF_O ("border", 2, oofm,
+ "(c) %p is an achestor of %p; direct child is %p "
+ "(%d) => %d - %d = %d\n",
+ t1->getTextblock (), t2->getTextblock (),
+ t->getTextblock (), t->parentExtIndex,
+ f1->externalIndex, t->parentExtIndex, r);
+ }
+
+ if (!rdef) {
+ r = t1->index - t2->index;
+ DBG_OBJ_MSGF_O ("border", 2, oofm, "(d) other => %d - %d = %d",
+ t1->index, t2->index, r);
+ }
+ }
+
+ DBG_OBJ_MSGF_O ("border", 2, oofm, "result: %d", r);
+ DBG_OBJ_MSG_END_O (oofm);
+ return r;
+}
+
+int OutOfFlowMgr::SortedFloatsVector::findFloatIndex (Textblock *lastGB,
+ int lastExtIndex)
+{
+ DBG_OBJ_MSGF_O ("border", 0, oofm, "<b>findFloatIndex</b> (%p, %d)",
+ lastGB, lastExtIndex);
+ DBG_OBJ_MSG_START_O (oofm);
+
+ Float key (oofm, NULL, lastGB, lastExtIndex);
+ key.index = -1; // for debugging
+ Float::CompareGBAndExtIndex comparator (oofm);
+ int i = bsearch (&key, false, &comparator);
+
+ // At position i is the next larger element, so element i should
+ // not included, but i - 1 returned; except if the exact element is
+ // found: then include it and so return i.
+ int r;
+ if (i == size())
+ r = i - 1;
+ else {
+ Float *f = get (i);
+ if (comparator.compare (f, &key) == 0)
+ r = i;
+ else
+ r = i - 1;
+ }
+
+ //printf ("[%p] findFloatIndex (%p, %d) => i = %d, r = %d (size = %d); "
+ // "in %s list %p on the %s side\n",
+ // oofm->containingBlock, lastGB, lastExtIndex, i, r, size (),
+ // type == GB ? "GB" : "CB", this, side == LEFT ? "left" : "right");
+
+ //for (int i = 0; i < size (); i++) {
+ // Float *f = get(i);
+ // TBInfo *t = oofm->getTextblock(f->generatingBlock);
+ // printf (" %d: (%p [%d, %p], %d)\n", i, f->generatingBlock,
+ // t->index, t->parent ? t->parent->textblock : NULL,
+ // get(i)->externalIndex);
+ //}
+
+ DBG_OBJ_MSGF_O ("border", 1, oofm, "=> r = %d", r);
+ DBG_OBJ_MSG_END_O (oofm);
+ return r;
+}
+
+int OutOfFlowMgr::SortedFloatsVector::find (Textblock *textblock, int y,
+ int start, int end)
+{
+ DBG_OBJ_MSGF_O ("border", 0, oofm, "<b>find</b> (%p, %d, %d, %d)",
+ textblock, y, start, end);
+ DBG_OBJ_MSG_START_O (oofm);
+
+ Float key (oofm, NULL, NULL, 0);
+ key.generatingBlock = textblock;
+ key.yReal = y;
+ key.index = -1; // for debugging
+ Float::ComparePosition comparator (oofm, textblock);
+ int result = bsearch (&key, false, start, end, &comparator);
+
+ DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result);
+ DBG_OBJ_MSG_END_O (oofm);
+ return result;
+}
+
+int OutOfFlowMgr::SortedFloatsVector::findFirst (Textblock *textblock,
+ int y, int h,
+ Textblock *lastGB,
+ int lastExtIndex)
+{
+ DBG_OBJ_MSGF_O ("border", 0, oofm, "<b>findFirst</b> (%p, %d, %d, %p, %d)",
+ textblock, y, h, lastGB, lastExtIndex);
+ DBG_OBJ_MSG_START_O (oofm);
+
+ DBG_IF_RTFL {
+ DBG_OBJ_MSG_O ("border", 2, oofm, "searching in list:");
+ DBG_OBJ_MSG_START_O (oofm);
+
+ for (int i = 0; i < size(); i++) {
+ Float *f = get(i);
+ DBG_OBJ_MSGF_O ("border", 2, oofm,
+ "%d: (%p, i = %d/%d, y = %d/%d, s = (%d * (%d + %d)), "
+ "%s, %s, ext = %d, GB = %p); widget at (%d, %d)",
+ i, f->getWidget (), f->index, f->sideSpanningIndex,
+ f->yReq, f->yReal, f->size.width, f->size.ascent,
+ f->size.descent, f->dirty ? "dirty" : "clean",
+ f->sizeChangedSinceLastAllocation ? "scsla" : "sNcsla",
+ f->externalIndex, f->generatingBlock,
+ f->getWidget()->getAllocation()->x,
+ f->getWidget()->getAllocation()->y);
+ }
+
+ DBG_OBJ_MSG_END_O (oofm);
+ }
+
+ int last = findFloatIndex (lastGB, lastExtIndex);
+ DBG_OBJ_MSGF_O ("border", 1, oofm, "last = %d", last);
+ assert (last < size());
+ int i = find (textblock, y, 0, last), result;
+ DBG_OBJ_MSGF_O ("border", 1, oofm, "i = %d", i);
+
+ // Note: The smallest value of "i" is 0, which means that "y" is before or
+ // equal to the first float. The largest value is "last + 1", which means
+ // that "y" is after the last float. In both cases, the first or last,
+ // respectively, float is a candidate. Generally, both floats, before and
+ // at the search position, are candidates.
+
+ if (i > 0 && get(i - 1)->covers (textblock, y, h))
+ result = i - 1;
+ else if (i <= last && get(i)->covers (textblock, y, h))
+ result = i;
+ else
+ result = -1;
+
+ DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result);
+ DBG_OBJ_MSG_END_O (oofm);
+ return result;
+}
+
+int OutOfFlowMgr::SortedFloatsVector::findLastBeforeSideSpanningIndex
+ (int sideSpanningIndex)
+{
+ OutOfFlowMgr::Float::CompareSideSpanningIndex comparator;
+ Float key (NULL, NULL, NULL, 0);
+ key.sideSpanningIndex = sideSpanningIndex;
+ return bsearch (&key, false, &comparator) - 1;
+}
+
+void OutOfFlowMgr::SortedFloatsVector::put (Float *vloat)
+{
+ lout::container::typed::Vector<Float>::put (vloat);
+ vloat->index = size() - 1;
+ vloat->inCBList = type == CB;
+}
+
+OutOfFlowMgr::TBInfo::TBInfo (OutOfFlowMgr *oofm, Textblock *textblock,
+ TBInfo *parent, int parentExtIndex) :
+ WidgetInfo (oofm, textblock)
+{
+ this->parent = parent;
+ this->parentExtIndex = parentExtIndex;
+
+ leftFloatsGB = new SortedFloatsVector (oofm, LEFT, SortedFloatsVector::GB);
+ rightFloatsGB = new SortedFloatsVector (oofm, RIGHT, SortedFloatsVector::GB);
+}
+
+OutOfFlowMgr::TBInfo::~TBInfo ()
+{
+ delete leftFloatsGB;
+ delete rightFloatsGB;
+}
+
+OutOfFlowMgr::AbsolutelyPositioned::AbsolutelyPositioned (OutOfFlowMgr *oofm,
+ Widget *widget,
+ Textblock
+ *generatingBlock,
+ int externalIndex)
+{
+ this->widget = widget;
+ dirty = true;
+}
+
+OutOfFlowMgr::OutOfFlowMgr (Textblock *containingBlock)
+{
+ DBG_OBJ_CREATE ("dw::OutOfFlowMgr");
+
+ this->containingBlock = containingBlock;
+
+ leftFloatsCB = new SortedFloatsVector (this, LEFT, SortedFloatsVector::CB);
+ rightFloatsCB = new SortedFloatsVector (this, RIGHT, SortedFloatsVector::CB);
+
+ leftFloatsAll = new Vector<Float> (1, true);
+ rightFloatsAll = new Vector<Float> (1, true);
+
+ DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size());
+ DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size());
+
+ 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;
+
+ DBG_OBJ_DELETE ();
+}
+
+void OutOfFlowMgr::sizeAllocateStart (Allocation *containingBlockAllocation)
+{
+ DBG_OBJ_MSG ("resize.oofm", 0, "<b>sizeAllocateStart</b>");
+ this->containingBlockAllocation = *containingBlockAllocation;
+ containingBlockWasAllocated = true;
+}
+
+void OutOfFlowMgr::sizeAllocateEnd ()
+{
+ DBG_OBJ_MSG ("resize.oofm", 0, "<b>sizeAllocateEnd</b>");
+ DBG_OBJ_MSG_START ();
+
+ // Move floats from GB lists to the one CB list.
+ moveFromGBToCB (LEFT);
+ moveFromGBToCB (RIGHT);
+
+ // Floats and absolutely positioned blocks have to be allocated
+ sizeAllocateFloats (LEFT);
+ sizeAllocateFloats (RIGHT);
+ sizeAllocateAbsolutelyPositioned ();
+
+ // Textblocks have already been allocated here.
+
+ // Check changes of both textblocks and floats allocation. (All is checked
+ // by hasRelationChanged (...).)
+ for (lout::container::typed::Iterator<TypedPointer <Textblock> > it =
+ tbInfosByTextblock->iterator ();
+ it.hasNext (); ) {
+ TypedPointer <Textblock> *key = it.getNext ();
+ TBInfo *tbInfo = tbInfosByTextblock->get (key);
+ Textblock *tb = key->getTypedValue();
+
+ int minFloatPos;
+ Widget *minFloat;
+ if (hasRelationChanged (tbInfo, &minFloatPos, &minFloat))
+ tb->borderChanged (minFloatPos, minFloat);
+ }
+
+ // Store some information for later use.
+ for (lout::container::typed::Iterator<TypedPointer <Textblock> > it =
+ tbInfosByTextblock->iterator ();
+ it.hasNext (); ) {
+ TypedPointer <Textblock> *key = it.getNext ();
+ TBInfo *tbInfo = tbInfosByTextblock->get (key);
+ Textblock *tb = key->getTypedValue();
+
+ tbInfo->updateAllocation ();
+ tbInfo->availWidth = tb->getAvailWidth ();
+ }
+
+ // There are cases where some allocated floats (TODO: later also absolutely
+ // positioned elements?) exceed the CB allocation.
+ bool sizeChanged = doFloatsExceedCB (LEFT) || doFloatsExceedCB (RIGHT);
+
+ // Similar for extremes. (TODO: here also absolutely positioned elements?)
+ bool extremesChanged =
+ haveExtremesChanged (LEFT) || haveExtremesChanged (RIGHT);
+
+ for (int i = 0; i < leftFloatsCB->size(); i++)
+ leftFloatsCB->get(i)->updateAllocation ();
+
+ for (int i = 0; i < rightFloatsCB->size(); i++)
+ rightFloatsCB->get(i)->updateAllocation ();
+
+ if (sizeChanged || extremesChanged)
+ containingBlock->oofSizeChanged (extremesChanged);
+
+ DBG_OBJ_MSG_END ();
+}
+
+bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, int *minFloatPos,
+ Widget **minFloat)
+{
+ DBG_OBJ_MSGF ("resize.oofm", 0,
+ "<b>hasRelationChanged</b> (<i>widget:</i> %p, ...)",
+ tbInfo->getWidget ());
+ DBG_OBJ_MSG_START ();
+
+ int leftMinPos, rightMinPos;
+ Widget *leftMinFloat, *rightMinFloat;
+ bool c1 =
+ hasRelationChanged (tbInfo, LEFT, &leftMinPos, &leftMinFloat);
+ bool c2 =
+ hasRelationChanged (tbInfo, RIGHT, &rightMinPos, &rightMinFloat);
+ if (c1 || c2) {
+ if (!c1) {
+ *minFloatPos = rightMinPos;
+ *minFloat = rightMinFloat;
+ } else if (!c2) {
+ *minFloatPos = leftMinPos;
+ *minFloat = leftMinFloat;
+ } else {
+ if (leftMinPos < rightMinPos) {
+ *minFloatPos = leftMinPos;
+ *minFloat = leftMinFloat;
+ } else{
+ *minFloatPos = rightMinPos;
+ *minFloat = rightMinFloat;
+ }
+ }
+ }
+
+ if (c1 || c2)
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "has changed: minFloatPos = %d, minFloat = %p",
+ *minFloatPos, *minFloat);
+ else
+ DBG_OBJ_MSG ("resize.oofm", 1, "has not changed");
+
+ DBG_OBJ_MSG_END ();
+ return c1 || c2;
+}
+
+bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, Side side,
+ int *minFloatPos, Widget **minFloat)
+{
+ DBG_OBJ_MSGF ("resize.oofm", 0,
+ "<b>hasRelationChanged</b> (<i>widget:</i> %p, %s, ...)",
+ tbInfo->getWidget (), side == LEFT ? "LEFT" : "RIGHT");
+ DBG_OBJ_MSG_START ();
+
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+ bool changed = false;
+
+ for (int i = 0; i < list->size(); i++) {
+ // TODO binary search?
+ Float *vloat = list->get(i);
+ int floatPos;
+
+ if (tbInfo->getTextblock () == vloat->generatingBlock)
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "not checking (generating!) textblock %p against float "
+ "%p", tbInfo->getWidget (), vloat->getWidget ());
+ else {
+ Allocation *gba = getAllocation (vloat->generatingBlock);
+
+ int newFlx =
+ calcFloatX (vloat, side,
+ gba->x - containingBlockAllocation.x,
+ gba->width, vloat->generatingBlock->getAvailWidth ());
+ int newFly = vloat->generatingBlock->getAllocation()->y
+ - containingBlockAllocation.y + vloat->yReal;
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "checking textblock %p against float %p",
+ tbInfo->getWidget (), vloat->getWidget ());
+ DBG_OBJ_MSG_START ();
+
+ if (hasRelationChanged (tbInfo->wasThenAllocated (),
+ tbInfo->getOldXCB (), tbInfo->getOldYCB (),
+ tbInfo->getOldWidth (),
+ tbInfo->getOldHeight (),
+ tbInfo->getNewXCB (), tbInfo->getNewYCB (),
+ tbInfo->getNewWidth (),
+ tbInfo->getNewHeight (),
+ vloat->wasThenAllocated (),
+ // When not allocated before, these values
+ // are undefined, but this does not matter,
+ // since they are neither used.
+ vloat->getOldXCB (), vloat->getOldYCB (),
+ vloat->getOldWidth (), vloat->getOldHeight (),
+ newFlx, newFly, vloat->size.width,
+ vloat->size.ascent + vloat->size.descent,
+ side, &floatPos)) {
+ if (!changed || floatPos < *minFloatPos) {
+ *minFloatPos = floatPos;
+ *minFloat = vloat->getWidget ();
+ }
+ changed = true;
+ } else
+ DBG_OBJ_MSG ("resize.oofm", 0, "No.");
+
+ DBG_OBJ_MSG_END ();
+ }
+
+ // All floarts are searched, to find the minimum. TODO: Are
+ // floats sorted, so this can be shortened? (The first is the
+ // minimum?)
+ }
+
+ if (changed)
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "has changed: minFloatPos = %d, minFloat = %p",
+ *minFloatPos, *minFloat);
+ else
+ DBG_OBJ_MSG ("resize.oofm", 1, "has not changed");
+
+ DBG_OBJ_MSG_END ();
+ return changed;
+}
+
+/**
+ * \brief ...
+ *
+ * All coordinates are given relative to the CB. *floatPos is relative
+ * to the TB, and may be negative.
+ */
+bool OutOfFlowMgr::hasRelationChanged (bool oldTBAlloc,
+ int oldTBx, int oldTBy, int oldTBw,
+ int oldTBh, int newTBx, int newTBy,
+ int newTBw, int newTBh,
+ bool oldFlAlloc,
+ int oldFlx, int oldFly, int oldFlw,
+ int oldFlh, int newFlx, int newFly,
+ int newFlw, int newFlh,
+ Side side, int *floatPos)
+{
+ DBG_OBJ_MSGF ("resize.oofm", 0,
+ "<b>hasRelationChanged</b> (<i>see below</i>, %s, ...)",
+ side == LEFT ? "LEFT" : "RIGHT");
+ DBG_OBJ_MSG_START ();
+
+ if (oldTBAlloc)
+ DBG_OBJ_MSGF ("resize.oofm", 1, "old TB: %d, %d; %d * %d",
+ oldTBx, oldTBy, oldTBw, oldTBh);
+ else
+ DBG_OBJ_MSG ("resize.oofm", 1, "old TB: undefined");
+ DBG_OBJ_MSGF ("resize.oofm", 1, "new TB: %d, %d; %d * %d",
+ newTBx, newTBy, newTBw, newTBh);
+
+ if (oldFlAlloc)
+ DBG_OBJ_MSGF ("resize.oofm", 1, "old Fl: %d, %d; %d * %d",
+ oldFlx, oldFly, oldFlw, oldFlh);
+ else
+ DBG_OBJ_MSG ("resize.oofm", 1, "old Fl: undefined");
+ DBG_OBJ_MSGF ("resize.oofm", 1, "new Fl: %d, %d; %d * %d",
+ newFlx, newFly, newFlw, newFlh);
+
+ bool result;
+ if (oldTBAlloc && oldFlAlloc) {
+ bool oldCov = oldFly + oldFlh > oldTBy && oldFly < oldTBy + oldTBh;
+ bool newCov = newFly + newFlh > newTBy && newFly < newTBy + newTBh;
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "covered? then: %s, now: %s.",
+ oldCov ? "yes" : "no", newCov ? "yes" : "no");
+ DBG_OBJ_MSG_START ();
+
+ if (oldCov && newCov) {
+ int yOld = oldFly - oldTBy, yNew = newFly - newTBy;
+ if (yOld == yNew) {
+ DBG_OBJ_MSGF ("resize.oofm", 2,
+ "old (%d - %d) and new (%d - %d) position equal: %d",
+ oldFly, oldTBy, newFly, newTBy, yOld);
+
+ // Float position has not changed, but perhaps the amout
+ // how far the float reaches into the TB. (TODO:
+ // Generally, not only here, it could be tested whether
+ // the float reaches into the TB at all.)
+ int wOld, wNew;
+ if (side == LEFT) {
+ wOld = oldFlx + oldFlw - oldTBx;
+ wNew = newFlx + newFlw - newTBx;
+ } else {
+ wOld = oldTBx + oldTBw - oldFlx;
+ wNew = newTBx + newTBw - newFlx;
+ }
+
+ DBG_OBJ_MSGF ("resize.oofm", 2, "wOld = %d, wNew = %d\n",
+ wOld, wNew);
+
+ if (wOld == wNew) {
+ if (oldFlh == newFlh)
+ result = false;
+ else {
+ // Only heights of floats changed. Relevant only
+ // from bottoms of float.
+ *floatPos = min (yOld + oldFlh, yNew + newFlh);
+ result = true;
+ }
+ } else {
+ *floatPos = yOld;
+ result = true;
+ }
+ } else {
+ DBG_OBJ_MSGF ("resize.oofm", 2,
+ "old (%d - %d = %d) and new (%d - %d = %d) position "
+ "different",
+ oldFly, oldTBy, yOld, newFly, newTBy, yNew);
+ *floatPos = min (yOld, yNew);
+ result = true;
+ }
+ } else if (oldCov) {
+ *floatPos = oldFly - oldTBy;
+ result = true;
+ DBG_OBJ_MSGF ("resize.oofm", 2,
+ "returning old position: %d - %d = %d", oldFly, oldTBy,
+ *floatPos);
+ } else if (newCov) {
+ *floatPos = newFly - newTBy;
+ result = true;
+ DBG_OBJ_MSGF ("resize.oofm", 2,
+ "returning new position: %d - %d = %d", newFly, newTBy,
+ *floatPos);
+ } else
+ result = false;
+
+ DBG_OBJ_MSG_END ();
+ } else {
+ // Not allocated before: ignore all old values, only check whether
+ // TB is covered by Float.
+ if (newFly + newFlh > newTBy && newFly < newTBy + newTBh) {
+ *floatPos = newFly - newTBy;
+ result = true;
+ } else
+ result = false;
+ }
+
+ if (result)
+ DBG_OBJ_MSGF ("resize.oofm", 1, "has changed: floatPos = %d",
+ *floatPos);
+ else
+ DBG_OBJ_MSG ("resize.oofm", 1, "has not changed");
+
+ DBG_OBJ_MSG_END ();
+
+ return result;
+}
+
+bool OutOfFlowMgr::doFloatsExceedCB (Side side)
+{
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+ bool exceeds = false;
+
+ for (int i = 0; i < list->size () && !exceeds; i++) {
+ Float *vloat = list->get (i);
+ if (vloat->getWidget()->wasAllocated ()) {
+ Allocation *fla = vloat->getWidget()->getAllocation ();
+ if (fla->x + fla->width >
+ containingBlockAllocation.x + containingBlockAllocation.width ||
+ fla->y + fla->ascent + fla->descent >
+ containingBlockAllocation.y + containingBlockAllocation.ascent
+ + containingBlockAllocation.descent)
+ exceeds = true;
+ }
+ }
+
+ return exceeds;
+}
+
+bool OutOfFlowMgr::haveExtremesChanged (Side side)
+{
+ // This is quite different from doFloatsExceedCB, since there is no
+ // counterpart to getExtremes, as sizeAllocate is a counterpart to
+ // sizeRequest. So we have to determine whether the allocation has
+ // changed the extremes, which is done by examining the part of the
+ // allocation which is part of the extremes calculation (see
+ // getFloatsExtremes). Changes of the extremes are handled by the
+ // normal queueResize mechanism.
+
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+ bool changed = false;
+
+ for (int i = 0; i < list->size () && !changed; i++) {
+ Float *vloat = list->get (i);
+ // When the GB is the CB, an allocation change does not play a
+ // role here.
+ if (vloat->generatingBlock != containingBlock) {
+ if (!vloat->wasThenAllocated () && vloat->isNowAllocated ())
+ changed = true;
+ else {
+ // This method is called within sizeAllocateEnd, where
+ // containinBlock->getAllocation() (old value) and
+ // containinBlockAllocation (new value) are different.
+
+ Allocation *oldCBA = containingBlock->getAllocation ();
+ Allocation *newCBA = &containingBlockAllocation;
+
+ // Compare also to getFloatsExtremes. The box difference
+ // of the GB (from style) has not changed in this context,
+ // so it is ignored.
+
+ int oldDiffLeft = vloat->getOldXCB ();
+ int newDiffLeft = vloat->getNewXCB ();
+ int oldDiffRight =
+ oldCBA->width - (vloat->getOldXCB () + vloat->getOldWidth ());
+ int newDiffRight =
+ newCBA->width - (vloat->getNewXCB () + vloat->getNewWidth ());
+
+ if (// regarding minimum
+ (side == LEFT && oldDiffLeft != newDiffLeft) ||
+ (side == RIGHT && oldDiffRight != newDiffRight) ||
+ // regarding maximum
+ oldDiffLeft + oldDiffRight != newDiffLeft + newDiffRight)
+ changed = true;
+ }
+ }
+ }
+
+ return changed;
+}
+
+void OutOfFlowMgr::moveFromGBToCB (Side side)
+{
+ SortedFloatsVector *dest = side == LEFT ? leftFloatsCB : rightFloatsCB;
+ int *floatsMark = side == LEFT ? &leftFloatsMark : &rightFloatsMark;
+
+ for (int mark = 0; mark <= *floatsMark; mark++)
+ for (lout::container::typed::Iterator<TBInfo> it = tbInfos->iterator ();
+ it.hasNext (); ) {
+ TBInfo *tbInfo = it.getNext ();
+ SortedFloatsVector *src =
+ side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB;
+ for (int i = 0; i < src->size (); i++) {
+ Float *vloat = src->get (i);
+ if (!vloat->inCBList && vloat->mark == mark) {
+ dest->put (vloat);
+ //printf("[%p] moving %s float %p (%s %p, mark %d) to CB list\n",
+ // containingBlock, side == LEFT ? "left" : "right",
+ // vloat, vloat->widget->getClassName(), vloat->widget,
+ // vloat->mark);
+ }
+ }
+ }
+
+ *floatsMark = 0;
+
+ /* Old code: GB lists do not have to be cleared, but their contents
+ are still useful after allocation. Soon to be deleted, not only
+ uncommented.
+
+ for (lout::container::typed::Iterator<TBInfo> it = tbInfos->iterator ();
+ it.hasNext (); ) {
+ TBInfo *tbInfo = it.getNext ();
+ SortedFloatsVector *src =
+ side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB;
+ src->clear ();
+ }
+ */
+
+ //printf ("[%p] new %s list:\n",
+ // containingBlock, side == LEFT ? "left" : "right");
+ //for (int i = 0; i < dest->size(); i++)
+ // printf (" %d: %s\n", i, dest->get(i)->toString());
+}
+
+void OutOfFlowMgr::sizeAllocateFloats (Side side)
+{
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ ensureFloatSize (vloat);
+
+ Allocation *gbAllocation = getAllocation(vloat->generatingBlock);
+ Allocation *cbAllocation = getAllocation(containingBlock);
+
+ Allocation childAllocation;
+ childAllocation.x = cbAllocation->x +
+ calcFloatX (vloat, side, gbAllocation->x - cbAllocation->x,
+ gbAllocation->width,
+ vloat->generatingBlock->getAvailWidth());
+ childAllocation.y = gbAllocation->y + vloat->yReal;
+ childAllocation.width = vloat->size.width;
+ childAllocation.ascent = vloat->size.ascent;
+ childAllocation.descent = vloat->size.descent;
+
+ vloat->getWidget()->sizeAllocate (&childAllocation);
+ }
+}
+
+
+/**
+ * \brief ...
+ *
+ * gbX is given relative to the CB, as is the return value.
+ */
+int OutOfFlowMgr::calcFloatX (Float *vloat, Side side, int gbX, int gbWidth,
+ int gbAvailWidth)
+{
+ int gbActualWidth;
+
+ switch (side) {
+ case LEFT:
+ // Left floats are always aligned on the left side of the
+ // generator (content, not allocation).
+ return gbX + vloat->generatingBlock->getStyle()->boxOffsetX();
+ break;
+
+ case RIGHT:
+ // In some cases, the actual (allocated) width is too large; we
+ // use the "available" width here.
+ gbActualWidth = min (gbWidth, gbAvailWidth);
+
+ // Similar for right floats, but in this case, floats are
+ // shifted to the right when they are too big (instead of
+ // shifting the generator to the right).
+ return max (gbX + gbActualWidth - vloat->size.width
+ - vloat->generatingBlock->getStyle()->boxRestWidth(),
+ // Do not exceed CB allocation:
+ 0);
+
+ default:
+ assertNotReached ();
+ return 0;
+ }
+}
+
+
+void OutOfFlowMgr::draw (View *view, Rectangle *area)
+{
+ drawFloats (leftFloatsCB, view, area);
+ drawFloats (rightFloatsCB, view, area);
+ drawAbsolutelyPositioned (view, area);
+}
+
+void OutOfFlowMgr::drawFloats (SortedFloatsVector *list, View *view,
+ Rectangle *area)
+{
+ // This could be improved, since the list is sorted: search the
+ // first float fitting into the area, and iterate until one is
+ // found below the area.
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ Rectangle childArea;
+ if (vloat->getWidget()->intersects (area, &childArea))
+ vloat->getWidget()->draw (view, &childArea);
+ }
+}
+
+void OutOfFlowMgr::drawAbsolutelyPositioned (View *view, Rectangle *area)
+{
+ for (int i = 0; i < absolutelyPositioned->size(); i++) {
+ AbsolutelyPositioned *abspos = absolutelyPositioned->get(i);
+ 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 (Widget *widget)
+{
+ return
+ widget->getStyle()->vloat != FLOAT_NONE ||
+ widget->getStyle()->position == POSITION_ABSOLUTE ||
+ widget->getStyle()->position == POSITION_FIXED;
+}
+
+bool OutOfFlowMgr::isWidgetHandledByOOFM (Widget *widget)
+{
+ // May be extended for fixed (and relative?) positions.
+ return isWidgetFloat (widget);
+ // TODO temporary disabled: || isWidgetAbsolutelyPositioned (widget);
+}
+
+void OutOfFlowMgr::addWidgetInFlow (Textblock *textblock,
+ Textblock *parentBlock, int externalIndex)
+{
+ //printf ("[%p] addWidgetInFlow (%p, %p, %d)\n",
+ // containingBlock, textblock, parentBlock, externalIndex);
+
+ TBInfo *tbInfo =
+ new TBInfo (this, textblock,
+ parentBlock ? getTextblock (parentBlock) : NULL,
+ externalIndex);
+ tbInfo->index = tbInfos->size();
+
+ tbInfos->put (tbInfo);
+ tbInfosByTextblock->put (new TypedPointer<Textblock> (textblock), tbInfo);
+}
+
+void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock,
+ int externalIndex)
+{
+ DBG_OBJ_MSGF ("construct.oofm", 0, "<b>addWidgetOOF</b> (%p, %p, %d)",
+ widget, generatingBlock, externalIndex);
+
+ if (isWidgetFloat (widget)) {
+ TBInfo *tbInfo = getTextblock (generatingBlock);
+
+ Float *vloat = new Float (this, widget, generatingBlock, externalIndex);
+
+ // Note: Putting the float first in the GB list, and then,
+ // possibly into the CB list (in that order) will trigger
+ // setting Float::inCBList to the right value.
+
+ switch (widget->getStyle()->vloat) {
+ case FLOAT_LEFT:
+ leftFloatsAll->put (vloat);
+ DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size());
+ 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);
+ DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size());
+ widget->parentRef = createRefRightFloat (rightFloatsAll->size() - 1);
+ tbInfo->rightFloatsGB->put (vloat);
+
+ if (wasAllocated (generatingBlock)) {
+ rightFloatsCB->put (vloat);
+ //printf ("[%p] adding right float %p (%s %p) to CB list\n",
+ // containingBlock, vloat, widget->getClassName(), widget);
+ } else {
+ if (tbInfo->index < lastRightTBIndex)
+ rightFloatsMark++;
+
+ vloat->mark = rightFloatsMark;
+ //printf ("[%p] adding right float %p (%s %p, mark %d) to GB list "
+ // "(index %d, last = %d)\n",
+ // containingBlock, vloat, widget->getClassName(), widget,
+ // vloat->mark, tbInfo->index, lastRightTBIndex);
+
+ lastRightTBIndex = tbInfo->index;
+ }
+
+ break;
+
+ default:
+ assertNotReached();
+ }
+
+ // "sideSpanningIndex" is only compared, so this simple
+ // assignment is sufficient; differenciation between GB and CB
+ // lists is not neccessary. TODO: Can this also be applied to
+ // "index", to simplify the current code? Check: where is
+ // "index" used.
+ vloat->sideSpanningIndex =
+ leftFloatsAll->size() + rightFloatsAll->size() - 1;
+
+ floatsByWidget->put (new TypedPointer<Widget> (widget), vloat);
+ } else if (isWidgetAbsolutelyPositioned (widget)) {
+ AbsolutelyPositioned *abspos =
+ new AbsolutelyPositioned (this, widget, generatingBlock,
+ externalIndex);
+ absolutelyPositioned->put (abspos);
+ widget->parentRef =
+ createRefAbsolutelyPositioned (absolutelyPositioned->size() - 1);
+ } else
+ // May be extended.
+ assertNotReached();
+}
+
+void OutOfFlowMgr::moveExternalIndices (Textblock *generatingBlock,
+ int oldStartIndex, int diff)
+{
+ TBInfo *tbInfo = getTextblock (generatingBlock);
+ moveExternalIndices (tbInfo->leftFloatsGB, oldStartIndex, diff);
+ moveExternalIndices (tbInfo->rightFloatsGB, oldStartIndex, diff);
+}
+
+void OutOfFlowMgr::moveExternalIndices (SortedFloatsVector *list,
+ int oldStartIndex, int diff)
+{
+ // Could be faster with binary search, but the GB (not CB!) lists
+ // should be rather small.
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ if (vloat->externalIndex >= oldStartIndex)
+ vloat->externalIndex += diff;
+ }
+}
+
+OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget)
+{
+ TypedPointer <Widget> key (widget);
+ Float *vloat = floatsByWidget->get (&key);
+ assert (vloat != NULL);
+ return vloat;
+}
+
+void OutOfFlowMgr::markSizeChange (int ref)
+{
+ DBG_OBJ_MSGF ("resize", 0, "<b>markSizeChange</b> (%d)", ref);
+ DBG_OBJ_MSG_START ();
+
+ if (isRefFloat (ref)) {
+ Float *vloat;
+
+ if (isRefLeftFloat (ref)) {
+ int i = getFloatIndexFromRef (ref);
+ vloat = leftFloatsAll->get (i);
+ //printf (" => left float %d\n", i);
+ } else if (isRefRightFloat (ref)) {
+ int i = getFloatIndexFromRef (ref);
+ vloat = rightFloatsAll->get (i);
+ //printf (" => right float %d\n", i);
+ } else {
+ assertNotReached();
+ vloat = NULL; // compiler happiness
+ }
+
+ vloat->dirty = vloat->sizeChangedSinceLastAllocation = true;
+
+ // The generating block is told directly about this. (Others later, in
+ // sizeAllocateEnd.) Could be faster (cf. hasRelationChanged, which
+ // differentiates many special cases), but the size is not known yet,
+ vloat->generatingBlock->borderChanged (vloat->yReal, vloat->getWidget ());
+ } else if (isRefAbsolutelyPositioned (ref)) {
+ int i = getAbsolutelyPositionedIndexFromRef (ref);
+ absolutelyPositioned->get(i)->dirty = true;
+ } else
+ assertNotReached();
+
+ DBG_OBJ_MSG_END ();
+}
+
+
+void OutOfFlowMgr::markExtremesChange (int ref)
+{
+ // Nothing to do here.
+}
+
+Widget *OutOfFlowMgr::getWidgetAtPoint (int x, int y, int level)
+{
+ Widget *childAtPoint = getFloatWidgetAtPoint (leftFloatsCB, x, y, level);
+ if (childAtPoint == NULL)
+ childAtPoint = getFloatWidgetAtPoint (rightFloatsCB, x, y, level);
+ if (childAtPoint == NULL)
+ childAtPoint = getAbsolutelyPositionedWidgetAtPoint (x, y, level);
+ return childAtPoint;
+}
+
+Widget *OutOfFlowMgr::getFloatWidgetAtPoint (SortedFloatsVector *list,
+ int x, int y, int level)
+{
+ for (int i = 0; i < list->size(); i++) {
+ // Could use binary search to be faster.
+ Float *vloat = list->get(i);
+ Widget *childAtPoint =
+ vloat->getWidget()->getWidgetAtPoint (x, y, level + 1);
+ if (childAtPoint)
+ return childAtPoint;
+ }
+
+ return NULL;
+}
+
+Widget *OutOfFlowMgr::getAbsolutelyPositionedWidgetAtPoint (int x, int y,
+ int level)
+{
+ for (int i = 0; i < absolutelyPositioned->size(); i++) {
+ AbsolutelyPositioned *abspos = absolutelyPositioned->get(i);
+ Widget *childAtPoint = abspos->widget->getWidgetAtPoint (x, y, level + 1);
+ if (childAtPoint)
+ return childAtPoint;
+ }
+
+ return NULL;
+}
+
+void OutOfFlowMgr::tellPosition (Widget *widget, int yReq)
+{
+ if (isWidgetFloat (widget))
+ tellFloatPosition (widget, yReq);
+
+ // Nothing to do for absolutely positioned blocks.
+}
+
+
+void OutOfFlowMgr::tellFloatPosition (Widget *widget, int yReq)
+{
+ DBG_OBJ_MSGF ("resize.oofm", 0, "<b>tellFloatPosition</b> (%p, %d)",
+ widget, yReq);
+ DBG_OBJ_MSG_START ();
+
+ assert (yReq >= 0);
+
+ Float *vloat = findFloatByWidget(widget);
+
+ SortedFloatsVector *listSame, *listOpp;
+ Side side;
+ getFloatsListsAndSide (vloat, &listSame, &listOpp, &side);
+ ensureFloatSize (vloat);
+
+ // "yReal" may change due to collisions (see below).
+ vloat->yReq = vloat->yReal = yReq;
+
+ DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReq", vloat->yReq);
+ DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal);
+
+ // Test collisions (on this side). Only previous float is relevant.
+ int yRealNew;
+ if (vloat->index >= 1 &&
+ collidesV (vloat, listSame->get (vloat->index - 1), &yRealNew)) {
+ vloat->yReal = yRealNew;
+ DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", 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 &&
+ collidesH (vloat, listOpp->get (lastOppFloat), &yRealNew)) {
+ vloat->yReal = yRealNew;
+ DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal);
+ }
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "vloat->yReq = %d, vloat->yReal = %d",
+ vloat->yReq, vloat->yReal);
+
+ DBG_OBJ_MSG_END ();
+}
+
+bool OutOfFlowMgr::collidesV (Float *vloat, Float *other, int *yReal)
+{
+ // Only checks vertical (possible) collisions, and only refers to
+ // vloat->yReal; never to vloat->allocation->y, even when the GBs are
+ // different. Used only in tellPosition.
+
+ DBG_OBJ_MSGF ("resize.oofm", 0,
+ "<b>collides</b> (#%d [%p], #%d [%p], ...)",
+ vloat->index, vloat->getWidget (), other->index,
+ other->getWidget ());
+ DBG_OBJ_MSG_START ();
+
+ bool result;
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "initial yReal = %d", vloat->yReal);
+
+ if (vloat->generatingBlock == other->generatingBlock) {
+ ensureFloatSize (other);
+ int otherBottomGB =
+ other->yReal + other->size.ascent + other->size.descent;
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "same generators: otherBottomGB = %d + (%d + %d) = %d",
+ other->yReal, other->size.ascent, other->size.descent,
+ otherBottomGB);
+
+ if (vloat->yReal < otherBottomGB) {
+ *yReal = otherBottomGB;
+ result = true;
+ } else
+ result = false;
+ } else {
+ assert (wasAllocated (vloat->generatingBlock));
+ assert (wasAllocated (other->generatingBlock));
+
+ // If the other float is not allocated, there is no collision. The
+ // allocation of this float (vloat) is not used at all.
+ if (!other->getWidget()->wasAllocated ())
+ result = false;
+ else {
+ Allocation *gba = getAllocation (vloat->generatingBlock),
+ *flaOther = other->getWidget()->getAllocation ();
+ int otherBottomGB =
+ flaOther->y + flaOther->ascent + flaOther->descent - gba->y;
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "different generators: "
+ "otherBottomGB = %d + (%d + %d) - %d = %d",
+ flaOther->y, flaOther->ascent, flaOther->descent, gba->y,
+ otherBottomGB);
+
+ if (vloat->yReal < otherBottomGB) {
+ *yReal = otherBottomGB;
+ result = true;
+ } else
+ result = false;
+ }
+ }
+
+ if (result)
+ DBG_OBJ_MSGF ("resize.oofm", 1, "collides: new yReal = %d", *yReal);
+ else
+ DBG_OBJ_MSG ("resize.oofm", 1, "does not collide");
+
+ DBG_OBJ_MSG_END ();
+ return result;
+}
+
+
+bool OutOfFlowMgr::collidesH (Float *vloat, Float *other, int *yReal)
+{
+ bool collidesH;
+
+ if (!collidesV (vloat, other, yReal))
+ collidesH = false;
+ else {
+ if (vloat->generatingBlock == other->generatingBlock)
+ collidesH = vloat->size.width + other->size.width
+ + vloat->generatingBlock->getStyle()->boxDiffWidth()
+ > vloat->generatingBlock->getAvailWidth();
+ else {
+ assert (wasAllocated (vloat->generatingBlock));
+ assert (wasAllocated (other->generatingBlock));
+
+ // Again, if the other float is not allocated, there is no
+ // collision. Compare to collidesV. (But vloat->size is used
+ // here.)
+ if (!other->getWidget()->wasAllocated ())
+ collidesH = false;
+ else {
+ Allocation *gba = getAllocation (vloat->generatingBlock);
+ int vloatX =
+ calcFloatX (vloat,
+ vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT ?
+ LEFT : RIGHT,
+ gba->x, gba->width,
+ vloat->generatingBlock->getAvailWidth ());
+
+ // Generally: right border of the left float > left border
+ // of the right float (all in canvas coordinates).
+ if (vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT)
+ // "vloat" is left, "other" is right
+ collidesH = vloatX + vloat->size.width
+ > other->getWidget()->getAllocation()->x;
+ else
+ // "other" is left, "vloat" is right
+ collidesH = other->getWidget()->getAllocation()->x
+ + other->getWidget()->getAllocation()->width
+ > vloatX;
+ }
+ }
+ }
+
+ return collidesH;
+}
+
+void OutOfFlowMgr::getFloatsListsAndSide (Float *vloat,
+ SortedFloatsVector **listSame,
+ SortedFloatsVector **listOpp,
+ Side *side)
+{
+ TBInfo *tbInfo = getTextblock (vloat->generatingBlock);
+
+ switch (vloat->getWidget()->getStyle()->vloat) {
+ case FLOAT_LEFT:
+ if (wasAllocated (vloat->generatingBlock)) {
+ if (listSame) *listSame = leftFloatsCB;
+ if (listOpp) *listOpp = rightFloatsCB;
+ } else {
+ if (listSame) *listSame = tbInfo->leftFloatsGB;
+ if (listOpp) *listOpp = tbInfo->rightFloatsGB;
+ }
+ if (side) *side = LEFT;
+ break;
+
+ case FLOAT_RIGHT:
+ if (wasAllocated (vloat->generatingBlock)) {
+ if (listSame) *listSame = rightFloatsCB;
+ if (listOpp) *listOpp = leftFloatsCB;
+ } else {
+ if (listSame) *listSame = tbInfo->rightFloatsGB;
+ if (listOpp) *listOpp = tbInfo->leftFloatsGB;
+ }
+ if (side) *side = RIGHT;
+ break;
+
+ default:
+ assertNotReached();
+ }
+}
+
+void OutOfFlowMgr::getSize (Requisition *cbReq, int *oofWidth, int *oofHeight)
+{
+ DBG_OBJ_MSG ("resize.oofm", 0, "<b>getSize</b> ()");
+ DBG_OBJ_MSG_START ();
+
+ int oofWidthAbsPos, oofHeightAbsPos;
+ getAbsolutelyPositionedSize (cbReq, &oofWidthAbsPos, &oofHeightAbsPos);
+
+ int oofWidthtLeft, oofWidthRight, oofHeightLeft, oofHeightRight;
+ getFloatsSize (cbReq, LEFT, &oofWidthtLeft, &oofHeightLeft);
+ getFloatsSize (cbReq, RIGHT, &oofWidthRight, &oofHeightRight);
+
+ *oofWidth = max (oofWidthtLeft, oofWidthRight, oofWidthAbsPos);
+ *oofHeight = max (oofHeightLeft, oofHeightRight, oofHeightAbsPos);
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "=> (a: %d, l: %d, r: %d => %d) * (a: %d, l: %d, r: %d => %d)",
+ oofWidthAbsPos, oofWidthtLeft, oofWidthRight, *oofWidth,
+ oofHeightAbsPos, oofHeightLeft, oofHeightRight, *oofHeight);
+ DBG_OBJ_MSG_END ();
+}
+
+void OutOfFlowMgr::getFloatsSize (Requisition *cbReq, Side side, int *width,
+ int *height)
+{
+ DBG_OBJ_MSGF ("resize.oofm", 0,
+ "<b>getFloatsSize</b> ((%d * (%d + %d), %s, ...)",
+ cbReq->width, cbReq->ascent, cbReq->descent,
+ side == LEFT ? "LEFT" : "RIGHT");
+ DBG_OBJ_MSG_START ();
+
+ SortedFloatsVector *list = getFloatsListForTextblock (containingBlock, side);
+
+ *width = *height = 0;
+
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+
+ if (vloat->generatingBlock == containingBlock ||
+ wasAllocated (vloat->generatingBlock)) {
+ ensureFloatSize (vloat);
+ int x, y;
+
+ if (vloat->generatingBlock == containingBlock) {
+ x = calcFloatX (vloat, side, 0, cbReq->width,
+ vloat->generatingBlock->getAvailWidth ());
+ y = vloat->yReal;
+ } else {
+ Allocation *gba = getAllocation(vloat->generatingBlock);
+ x = calcFloatX (vloat, side,
+ gba->x - containingBlockAllocation.x, gba->width,
+ vloat->generatingBlock->getAvailWidth ());
+ y = gba->y - containingBlockAllocation.y + vloat->yReal;
+ }
+
+ *width = max (*width, x + vloat->size.width);
+ *height = max (*height, y + vloat->size.ascent + vloat->size.descent);
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "considering float %p generated by %p: (%d + %d) * "
+ "(%d + (%d + %d)) => %d * %d",
+ vloat->getWidget (), vloat->generatingBlock,
+ x, vloat->size.width,
+ y, vloat->size.ascent, vloat->size.descent,
+ *width, *height);
+ } else
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "considering float %p generated by %p: not allocated",
+ vloat->getWidget (), vloat->generatingBlock);
+ }
+
+ DBG_OBJ_MSG_END ();
+}
+
+void OutOfFlowMgr::getExtremes (Extremes *cbExtr, int *oofMinWidth,
+ int *oofMaxWidth)
+{
+ DBG_OBJ_MSG ("resize.oofm", 0, "<b>getExtremes</b> ()");
+ DBG_OBJ_MSG_START ();
+
+ int oofMinWidthAbsPos, oofMaxWidthAbsPos;
+ getAbsolutelyPositionedExtremes (cbExtr, &oofMinWidthAbsPos,
+ &oofMaxWidthAbsPos);
+
+ int oofMinWidthtLeft, oofMinWidthRight, oofMaxWidthLeft, oofMaxWidthRight;
+ getFloatsExtremes (cbExtr, LEFT, &oofMinWidthtLeft, &oofMaxWidthLeft);
+ getFloatsExtremes (cbExtr, RIGHT, &oofMinWidthRight, &oofMaxWidthRight);
+
+ *oofMinWidth = max (oofMinWidthtLeft, oofMinWidthRight, oofMinWidthAbsPos);
+ *oofMaxWidth = max (oofMaxWidthLeft, oofMaxWidthRight, oofMaxWidthAbsPos);
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "=> (a: %d, l: %d, r: %d => %d) * (a: %d, l: %d, r: %d => %d)",
+ oofMinWidthAbsPos, oofMinWidthtLeft, oofMinWidthRight,
+ *oofMinWidth, oofMaxWidthAbsPos, oofMaxWidthLeft,
+ oofMaxWidthRight, *oofMaxWidth);
+ DBG_OBJ_MSG_END ();
+}
+
+void OutOfFlowMgr::getFloatsExtremes (Extremes *cbExtr, Side side,
+ int *minWidth, int *maxWidth)
+{
+ *minWidth = *maxWidth = 0;
+
+ SortedFloatsVector *list = getFloatsListForTextblock (containingBlock, side);
+
+ DBG_OBJ_MSGF ("resize", 0, "<b>getFloatsExtremes</b> ((%d / %d), %s, ...)",
+ cbExtr->minWidth, cbExtr->maxWidth,
+ side == LEFT ? "LEFT" : "RIGHT");
+ DBG_OBJ_MSG_START ();
+
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+
+ if (vloat->generatingBlock == containingBlock ||
+ wasAllocated (vloat->generatingBlock)) {
+ Extremes extr;
+ vloat->getWidget()->getExtremes (&extr);
+ int leftDiff, rightDiff;
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "considering float %p generated by %p: %d / %d",
+ vloat->getWidget (), vloat->generatingBlock,
+ extr.minWidth, extr.maxWidth);
+
+ if (vloat->generatingBlock == containingBlock) {
+ leftDiff = vloat->generatingBlock->getStyle()->boxOffsetX();
+ rightDiff = vloat->generatingBlock->getStyle()->boxRestWidth();
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "GB == CB => leftDiff = %d, rightDiff = %d",
+ leftDiff, rightDiff);
+ } else {
+ Allocation *gba = getAllocation(vloat->generatingBlock);
+ leftDiff = gba->x - containingBlockAllocation.x
+ + vloat->generatingBlock->getStyle()->boxOffsetX();
+ rightDiff =
+ (containingBlockAllocation.x + containingBlockAllocation.width)
+ - (gba->x + gba->width)
+ + vloat->generatingBlock->getStyle()->boxRestWidth();
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "GB != CB => leftDiff = %d - %d + %d = %d, "
+ "rightDiff = (%d + %d) - (%d + %d) + %d = %d",
+ gba->x, containingBlockAllocation.x,
+ vloat->generatingBlock->getStyle()->boxOffsetX(),
+ leftDiff, containingBlockAllocation.x,
+ containingBlockAllocation.width, gba->x, gba->width,
+ vloat->generatingBlock->getStyle()->boxRestWidth(),
+ rightDiff);
+ }
+
+ // TODO: Or zero (instead of rightDiff) for right floats?
+ *minWidth = max (*minWidth,
+ extr.minWidth + side == LEFT ? leftDiff : rightDiff);
+ *maxWidth = max (*maxWidth, extr.maxWidth + leftDiff + rightDiff);
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, " => %d / %d", *minWidth, *maxWidth);
+ } else
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "considering float %p generated by %p: not allocated",
+ vloat->getWidget (), vloat->generatingBlock);
+ }
+
+ DBG_OBJ_MSG_END ();
+}
+
+OutOfFlowMgr::TBInfo *OutOfFlowMgr::getTextblock (Textblock *textblock)
+{
+ TypedPointer<Textblock> key (textblock);
+ TBInfo *tbInfo = tbInfosByTextblock->get (&key);
+ assert (tbInfo);
+ return tbInfo;
+}
+
+/**
+ * Get the left border for the vertical position of *y*, for a height
+ * of *h", based on floats; relative to the allocation of the calling
+ * textblock.
+ *
+ * The border includes marging/border/padding of the calling textblock
+ * but is 0 if there is no float, so a caller should also consider
+ * other borders.
+ */
+int OutOfFlowMgr::getLeftBorder (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ int b = getBorder (textblock, LEFT, y, h, lastGB, lastExtIndex);
+ DBG_OBJ_MSGF ("border", 0, "left border (%p, %d, %d, %p, %d) => %d",
+ textblock, y, h, lastGB, lastExtIndex, b);
+ return b;
+}
+
+/**
+ * Get the right border for the vertical position of *y*, for a height
+ * of *h*, based on floats.
+ *
+ * See also getLeftBorder(int, int);
+ */
+int OutOfFlowMgr::getRightBorder (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ int b = getBorder (textblock, RIGHT, y, h, lastGB, lastExtIndex);
+ DBG_OBJ_MSGF ("border", 0, "right border (%p, %d, %d, %p, %d) => %d",
+ textblock, y, h, lastGB, lastExtIndex, b);
+ return b;
+}
+
+int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ DBG_OBJ_MSGF ("border", 0, "<b>getBorder</b> (%p, %s, %d, %d, %p, %d)",
+ textblock, side == LEFT ? "LEFT" : "RIGHT", y, h,
+ lastGB, lastExtIndex);
+ DBG_OBJ_MSG_START ();
+
+ SortedFloatsVector *list = getFloatsListForTextblock (textblock, side);
+ int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex);
+
+ DBG_OBJ_MSGF ("border", 1, "first = %d", first);
+
+ if (first == -1) {
+ // No float.
+ DBG_OBJ_MSG_END ();
+ return 0;
+ } else {
+ // It is not sufficient to find the first float, since a line
+ // (with height h) may cover the region of multiple float, of
+ // which the widest has to be choosen.
+ int border = 0;
+ bool covers = true;
+ // TODO Also check against lastGB and lastExtIndex
+ for (int i = first; covers && i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ covers = vloat->covers (textblock, y, h);
+ DBG_OBJ_MSGF ("border", 1, "float %d (%p) covers? %s.",
+ i, vloat->getWidget(), covers ? "<b>yes</b>" : "no");
+
+ if (covers) {
+ int thisBorder;
+ if (vloat->generatingBlock == textblock) {
+ int borderIn = side == LEFT ?
+ vloat->generatingBlock->getStyle()->boxOffsetX() :
+ vloat->generatingBlock->getStyle()->boxRestWidth();
+ thisBorder = vloat->size.width + borderIn;
+ DBG_OBJ_MSGF ("border", 1, "GB: thisBorder = %d + %d = %d",
+ vloat->size.width, borderIn, thisBorder);
+ } else {
+ assert (wasAllocated (vloat->generatingBlock));
+ assert (vloat->getWidget()->wasAllocated ());
+
+ Allocation *tba = getAllocation(textblock),
+ *fla = vloat->getWidget()->getAllocation ();
+ if (side == LEFT) {
+ thisBorder = fla->x + fla->width - tba->x;
+ DBG_OBJ_MSGF ("border", 1,
+ "not GB: thisBorder = %d + %d - %d = %d",
+ fla->x, fla->width, tba->x, thisBorder);
+ } else {
+ // See also calcFloatX.
+ int tbAvWidth = textblock->getAvailWidth ();
+ thisBorder = tba->x + min (tba->width, tbAvWidth) - fla->x;
+ DBG_OBJ_MSGF ("border", 1,
+ "not GB: thisBorder = %d + min (%d, %d) - %d "
+ "= %d",
+ tba->x, tba->width, tbAvWidth, fla->x,
+ thisBorder);
+ }
+ }
+
+ border = max (border, thisBorder);
+ DBG_OBJ_MSGF ("border", 1, "=> border = %d", border);
+ }
+ }
+
+ DBG_OBJ_MSG_END ();
+ return border;
+ }
+}
+
+
+OutOfFlowMgr::SortedFloatsVector *OutOfFlowMgr::getFloatsListForTextblock
+ (Textblock *textblock, Side side)
+{
+ if (wasAllocated (textblock))
+ return side == LEFT ? leftFloatsCB : rightFloatsCB;
+ else {
+ TBInfo *tbInfo = getTextblock (textblock);
+ return side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB;
+ }
+}
+
+
+bool OutOfFlowMgr::hasFloatLeft (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ bool b = hasFloat (textblock, LEFT, y, h, lastGB, lastExtIndex);
+ DBG_OBJ_MSGF ("border", 0, "has float left (%p, %d, %d, %p, %d) => %s",
+ textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false");
+ return b;
+}
+
+bool OutOfFlowMgr::hasFloatRight (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ bool b = hasFloat (textblock, RIGHT, y, h, lastGB, lastExtIndex);
+ DBG_OBJ_MSGF ("border", 0, "has float right (%p, %d, %d, %p, %d) => %s",
+ textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false");
+ return b;
+}
+
+bool OutOfFlowMgr::hasFloat (Textblock *textblock, Side side, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ DBG_OBJ_MSGF ("border", 0, "<b>hasFloat</b> (%p, %s, %d, %d, %p, %d)",
+ textblock, side == LEFT ? "LEFT" : "RIGHT", y, h,
+ lastGB, lastExtIndex);
+ DBG_OBJ_MSG_START ();
+
+ SortedFloatsVector *list = getFloatsListForTextblock (textblock, side);
+ int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex);
+
+ DBG_OBJ_MSGF ("border", 1, "first = %d", first);
+ DBG_OBJ_MSG_END ();
+ return first != -1;
+}
+
+/**
+ * Returns position relative to the textblock "tb".
+ */
+int OutOfFlowMgr::getClearPosition (Textblock *tb)
+{
+ if (tb->getStyle()) {
+ bool left = false, right = false;
+ switch (tb->getStyle()->clear) {
+ case CLEAR_NONE: break;
+ case CLEAR_LEFT: left = true; break;
+ case CLEAR_RIGHT: right = true; break;
+ case CLEAR_BOTH: left = right = true; break;
+ default: assertNotReached ();
+ }
+
+ return max (left ? getClearPosition (tb, LEFT) : 0,
+ right ? getClearPosition (tb, RIGHT) : 0);
+ } else
+ return 0;
+}
+
+int OutOfFlowMgr::getClearPosition (Textblock *tb, Side side)
+{
+ if (!wasAllocated (tb))
+ // There is no relation yet to floats generated by other
+ // textblocks, and this textblocks floats are unimportant for
+ // the "clear" property.
+ return 0;
+
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+
+ int i = list->findFloatIndex (tb, 0);
+ if (i < 0)
+ return 0;
+
+ Float *vloat = list->get(i);
+ // We pass this texblock and 0 (first word, or smallest external
+ // index, respectively), but search for the last float before this
+ // position. Therefore this check.
+ if (vloat->generatingBlock== tb)
+ i--;
+ if (i < 0)
+ return 0;
+
+ vloat = list->get(i);
+ ensureFloatSize (vloat);
+ return
+ vloat->yReal + vloat->size.ascent + vloat->size.descent -
+ getAllocation(tb)->y;
+}
+
+void OutOfFlowMgr::ensureFloatSize (Float *vloat)
+{
+ if (vloat->dirty ||
+ // If the size of the containing block has changed (represented
+ // currently by the available width), a recalculation of a
+ // relative float width may also be necessary.
+ (isPerLength (vloat->getWidget()->getStyle()->width) &&
+ vloat->cbAvailWidth != containingBlock->getAvailWidth ())) {
+ DBG_OBJ_MSGF ("resize.oofm", 0,
+ "<b>ensureFloatSize</b> (%p): recalculation",
+ vloat->getWidget ());
+ DBG_OBJ_MSG_START ();
+
+ Extremes extremes;
+ vloat->getWidget()->getExtremes (&extremes);
+ DBG_OBJ_MSGF ("resize.oofm", 1, "getExtremes => %d / %d",
+ extremes.minWidth, extremes.maxWidth);
+
+ // TODO Ugly. Soon to be replaced by cleaner code? See also
+ // comment in Textblock::calcWidgetSize.
+
+ if (vloat->getWidget()->usesHints ()) {
+ // For widths defined by CSS, similar adjustments (extremes
+ // etc.) like below are necessary, to prevent CPU hogging.
+ if (isAbsLength (vloat->getWidget()->getStyle()->width)) {
+ int width = absLengthVal (vloat->getWidget()->getStyle()->width);
+ DBG_OBJ_MSGF ("resize.oofm", 1, "about to set absolute width: %d",
+ width);
+ width = adjustFloatWidth (width, &extremes);
+ vloat->getWidget()->setWidth (width);
+ } else if (isPerLength (vloat->getWidget()->getStyle()->width)) {
+ int width =
+ multiplyWithPerLength (containingBlock->getAvailWidth(),
+ vloat->getWidget()->getStyle()->width);
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "about to set percentage width: %d * %g = %d",
+ containingBlock->getAvailWidth(),
+ perLengthVal (vloat->getWidget()->getStyle()->width),
+ width);
+ width = adjustFloatWidth (width, &extremes);
+ vloat->getWidget()->setWidth (width);
+ } else
+ DBG_OBJ_MSG ("resize.oofm", 1, "setting no width: not defined");
+ } else
+ DBG_OBJ_MSG ("resize.oofm", 1, "setting no width: uses no hints");
+
+ // This is a bit hackish: We first request the size, then set
+ // the available width (also considering the one of the
+ // containing block, and the extremes of the float), then
+ // request the size again, which may of course have a different
+ // result. This is a fix for the bug:
+ //
+ // Text in floats, which are wider because of an image, are
+ // broken at a too narrow width. Reproduce:
+ // test/floats2.html. After the image has been loaded, the
+ // text "Some text in a float." should not be broken
+ // anymore.
+ //
+ // If the call of setWidth not is neccessary, the second call
+ // will read the size from the cache, so no redundant
+ // calculation is necessary.
+ //
+ // Furthermore, extremes are considered; especially, floats are too
+ // wide, sometimes.
+
+ vloat->getWidget()->sizeRequest (&vloat->size);
+ DBG_OBJ_MSGF ("resize.oofm", 1, "sizeRequest (1) => %d * (%d + %d)",
+ vloat->size.width, vloat->size.ascent, vloat->size.descent);
+
+ // Set width ...
+ int width = vloat->size.width;
+ DBG_OBJ_MSGF ("resize.oofm", 1, "new width: %d", width);
+ width = adjustFloatWidth (width, &extremes);
+ vloat->getWidget()->setWidth (width);
+ vloat->getWidget()->sizeRequest (&vloat->size);
+ DBG_OBJ_MSGF ("resize.oofm", 1, "sizeRequest (2) => %d * (%d + %d)",
+ vloat->size.width, vloat->size.ascent, vloat->size.descent);
+
+ vloat->cbAvailWidth = containingBlock->getAvailWidth ();
+ vloat->dirty = false;
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "final size: %d * (%d + %d)",
+ vloat->size.width, vloat->size.ascent, vloat->size.descent);
+
+ DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.width",
+ vloat->size.width);
+ DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.ascent",
+ vloat->size.ascent);
+ DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.descent",
+ vloat->size.descent);
+
+ // "sizeChangedSinceLastAllocation" is reset in sizeAllocateEnd()
+
+ DBG_OBJ_MSG_END ();
+ }
+}
+
+int OutOfFlowMgr::adjustFloatWidth (int width, Extremes *extremes)
+{
+ DBG_OBJ_MSGF ("resize.oofm", 0,
+ "<b>adjustFloatWidth</b> (%d, (%d, %d)) [CB->availWidth = %d]",
+ width, extremes->minWidth, extremes->maxWidth,
+ containingBlock->getAvailWidth());
+ DBG_OBJ_MSG_START ();
+
+ // Consider the available width of the containing block (when set):
+ if (width > containingBlock->getAvailWidth()) {
+ width = containingBlock->getAvailWidth();
+ DBG_OBJ_MSGF ("resize.oofm", 1, "adjusted to availWidth: %d", width);
+ }
+ // Finally, consider extremes (as described above).
+ if (width < extremes->minWidth) {
+ width = extremes->minWidth;
+ DBG_OBJ_MSGF ("resize.oofm", 1, "adjusted to minWidth: %d", width);
+ }
+ if (width > extremes->maxWidth) {
+ width = extremes->maxWidth;
+ DBG_OBJ_MSGF ("resize.oofm", 1, "adjusted to maxWidth: %d", width);
+ }
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", width);
+ DBG_OBJ_MSG_END ();
+
+ return width;
+}
+
+void OutOfFlowMgr::getAbsolutelyPositionedSize (Requisition *cbReq, int *width,
+ int *height)
+{
+ // TODO
+ *width = *height = 0;
+}
+
+void OutOfFlowMgr::getAbsolutelyPositionedExtremes (Extremes *cbExtr,
+ int *minWidth,
+ int *maxWidth)
+{
+ // TODO
+ *minWidth = *maxWidth = 0;
+}
+
+void OutOfFlowMgr::ensureAbsolutelyPositionedSizeAndPosition
+ (AbsolutelyPositioned *abspos)
+{
+ // No work is done anymore on this, since widget sizes will be
+ // redesigned before absolute positions are finished.
+
+ if (abspos->dirty) {
+ Style *style = abspos->widget->getStyle();
+ int availWidth = containingBlock->getAvailWidth();
+ int availHeight =
+ containingBlock->getAvailAscent() + containingBlock->getAvailDescent();
+
+ if (style->left == LENGTH_AUTO)
+ abspos->xCB = 0;
+ else
+ abspos->xCB =
+ calcValueForAbsolutelyPositioned (abspos, style->left, availWidth);
+
+ if (style->top == LENGTH_AUTO)
+ abspos->yCB = 0;
+ else
+ abspos->yCB =
+ calcValueForAbsolutelyPositioned (abspos, style->top, availHeight);
+
+ abspos->width = -1; // undefined
+ if (style->width != LENGTH_AUTO)
+ abspos->width = calcValueForAbsolutelyPositioned (abspos, style->width,
+ availWidth);
+ else if (style->right != LENGTH_AUTO) {
+ int right = calcValueForAbsolutelyPositioned (abspos, style->right,
+ availWidth);
+ abspos->width = max (0, availWidth - (abspos->xCB + right));
+ }
+
+ abspos->height = -1; // undefined
+ if (style->height != LENGTH_AUTO)
+ abspos->height = calcValueForAbsolutelyPositioned (abspos,
+ style->height,
+ availHeight);
+ else if (style->bottom != LENGTH_AUTO) {
+ int bottom = calcValueForAbsolutelyPositioned (abspos, style->bottom,
+ availHeight);
+ abspos->height = max (0, availHeight - (abspos->yCB + bottom));
+ }
+
+ if (abspos->width != -1)
+ abspos->widget->setWidth (abspos->width);
+
+ if (abspos->height != -1) {
+ abspos->widget->setAscent (abspos->height);
+ abspos->widget->setDescent (0); // TODO
+ }
+
+ if (abspos->width == -1 || abspos->height == -1) {
+ Requisition req;
+ abspos->widget->sizeRequest (&req);
+
+ if (abspos->width == -1)
+ abspos->width = req.width;
+
+ if (abspos->height == -1)
+ abspos->height = req.ascent + req.descent;
+ }
+
+ abspos->dirty = false;
+ }
+}
+
+int OutOfFlowMgr::calcValueForAbsolutelyPositioned
+ (AbsolutelyPositioned *abspos, Length styleLen, int refLen)
+{
+ assert (styleLen != LENGTH_AUTO);
+ if (isAbsLength (styleLen))
+ return absLengthVal (styleLen);
+ else if (isPerLength (styleLen))
+ return multiplyWithPerLength (refLen, styleLen);
+ else {
+ assertNotReached ();
+ return 0; // compiler happiness
+ }
+}
+
+void OutOfFlowMgr::sizeAllocateAbsolutelyPositioned ()
+{
+ for (int i = 0; i < absolutelyPositioned->size(); i++) {
+ Allocation *cbAllocation = getAllocation(containingBlock);
+ AbsolutelyPositioned *abspos = absolutelyPositioned->get (i);
+ ensureAbsolutelyPositionedSizeAndPosition (abspos);
+
+ Allocation childAllocation;
+ childAllocation.x = cbAllocation->x + abspos->xCB;
+ childAllocation.y = cbAllocation->y + abspos->yCB;
+ childAllocation.width = abspos->width;
+ childAllocation.ascent = abspos->height;
+ childAllocation.descent = 0; // TODO
+
+ abspos->widget->sizeAllocate (&childAllocation);
+
+ printf ("[%p] allocating child %p at: (%d, %d), %d x (%d + %d)\n",
+ containingBlock, abspos->widget, childAllocation.x,
+ childAllocation.y, childAllocation.width, childAllocation.ascent,
+ childAllocation.descent);
+ }
+}
+
+} // namespace dw
diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh
new file mode 100644
index 00000000..c5c560c7
--- /dev/null
+++ b/dw/outofflowmgr.hh
@@ -0,0 +1,429 @@
+#ifndef __DW_OUTOFFLOWMGR_HH__
+#define __DW_OUTOFFLOWMGR_HH__
+
+#include "core.hh"
+
+namespace dw {
+
+class Textblock;
+
+/**
+ * \brief Represents additional data for containing blocks.
+ */
+class OutOfFlowMgr
+{
+ friend class WidgetInfo;
+
+private:
+ enum Side { LEFT, RIGHT };
+
+ Textblock *containingBlock;
+
+ // These two values are set by sizeAllocateStart(), and they are
+ // accessable also within sizeAllocateEnd(), and also for the
+ // containing block, for which allocation and WAS_ALLOCATED is set
+ // *after* sizeAllocateEnd(). See the two inline functions
+ // wasAllocated(Widget*) and getAllocation(Widget*) (further down)
+ // for usage.
+ core::Allocation containingBlockAllocation;
+ bool containingBlockWasAllocated;
+
+ class WidgetInfo: public lout::object::Object
+ {
+ private:
+ bool wasAllocated;
+ int xCB, yCB; // relative to the containing block
+ int width, height;
+
+ OutOfFlowMgr *oofm;
+ core::Widget *widget;
+
+ protected:
+ OutOfFlowMgr *getOutOfFlowMgr () { return oofm; }
+
+ public:
+ WidgetInfo (OutOfFlowMgr *oofm, core::Widget *widget);
+
+ inline bool wasThenAllocated () { return wasAllocated; }
+ inline int getOldXCB () { return xCB; }
+ inline int getOldYCB () { return yCB; }
+ inline int getOldWidth () { return width; }
+ inline int getOldHeight () { return height; }
+
+ inline bool isNowAllocated () { return widget->wasAllocated (); }
+ inline int getNewXCB () { return widget->getAllocation()->x -
+ oofm->containingBlockAllocation.x; }
+ inline int getNewYCB () { return widget->getAllocation()->y -
+ oofm->containingBlockAllocation.y; }
+ inline int getNewWidth () { return widget->getAllocation()->width; }
+ inline int getNewHeight () { return widget->getAllocation()->ascent +
+ widget->getAllocation()->descent; }
+
+ void update (bool wasAllocated, int xCB, int yCB, int width, int height);
+ void updateAllocation ();
+
+ inline core::Widget *getWidget () { return widget; }
+ };
+
+ class Float: public WidgetInfo
+ {
+ public:
+ class ComparePosition: public lout::object::Comparator
+ {
+ private:
+ OutOfFlowMgr *oofm;
+ Textblock *refTB;
+
+ public:
+ ComparePosition (OutOfFlowMgr *oofm, Textblock *refTB)
+ { this->oofm = oofm; this->refTB = refTB; }
+ int compare(Object *o1, Object *o2);
+ };
+
+ class CompareSideSpanningIndex: public lout::object::Comparator
+ {
+ public:
+ int compare(Object *o1, Object *o2);
+ };
+
+ class CompareGBAndExtIndex: public lout::object::Comparator
+ {
+ private:
+ OutOfFlowMgr *oofm;
+
+ public:
+ CompareGBAndExtIndex (OutOfFlowMgr *oofm) { this->oofm = oofm; }
+ int compare(Object *o1, Object *o2);
+ };
+
+ Textblock *generatingBlock;
+ int externalIndex;
+ int yReq, yReal; // relative to generator, not container
+ int index; /* When GB is not yet allocated: position
+ within TBInfo::leftFloatsGB or
+ TBInfo::rightFloatsGB, respectively. When GB
+ is allocated: position within leftFloatsCB
+ or rightFloatsCB, respectively, even when
+ the floats are still elements of
+ TBInfo::*FloatsGB. */
+ int sideSpanningIndex, mark;
+ core::Requisition size;
+ int cbAvailWidth; /* On which the calculation of relative sizes
+ is based. Height not yet used, and probably
+ not added before size redesign. */
+ bool dirty, sizeChangedSinceLastAllocation;
+ bool inCBList; /* Neccessary to prevent floats from being moved
+ twice from GB to CB list. */
+
+ Float (OutOfFlowMgr *oofm, core::Widget *widget,
+ Textblock *generatingBlock, int externalIndex);
+
+ void intoStringBuffer(lout::misc::StringBuffer *sb);
+
+ bool covers (Textblock *textblock, int y, int h);
+ };
+
+ /**
+ * This list is kept sorted.
+ *
+ * To prevent accessing methods of the base class in an
+ * uncontrolled way, the inheritance is private, not public; this
+ * means that all methods must be delegated (see iterator(), size()
+ * etc. below.)
+ *
+ * TODO Update comment: still sorted, but ...
+ *
+ * More: add() and change() may check order again.
+ */
+ class SortedFloatsVector: private lout::container::typed::Vector<Float>
+ {
+ public:
+ enum Type { GB, CB } type;
+
+ private:
+ OutOfFlowMgr *oofm;
+ Side side;
+
+ public:
+ inline SortedFloatsVector (OutOfFlowMgr *oofm, Side side, Type type) :
+ lout::container::typed::Vector<Float> (1, false)
+ { this->oofm = oofm; this->side = side; this->type = type; }
+
+ int findFloatIndex (Textblock *lastGB, int lastExtIndex);
+ int find (Textblock *textblock, int y, int start, int end);
+ int findFirst (Textblock *textblock, int y, int h, Textblock *lastGB,
+ int lastExtIndex);
+ int findLastBeforeSideSpanningIndex (int sideSpanningIndex);
+ void put (Float *vloat);
+
+ inline lout::container::typed::Iterator<Float> iterator()
+ { return lout::container::typed::Vector<Float>::iterator (); }
+ inline int size ()
+ { return lout::container::typed::Vector<Float>::size (); }
+ inline Float *get (int pos)
+ { return lout::container::typed::Vector<Float>::get (pos); }
+ inline void clear ()
+ { lout::container::typed::Vector<Float>::clear (); }
+ };
+
+ class TBInfo: public WidgetInfo
+ {
+ public:
+ int availWidth;
+ int index; // position within "tbInfos"
+
+ TBInfo *parent;
+ int parentExtIndex;
+
+ // These two lists store all floats generated by this textblock,
+ // as long as this textblock is not allocates.
+ SortedFloatsVector *leftFloatsGB, *rightFloatsGB;
+
+ TBInfo (OutOfFlowMgr *oofm, Textblock *textblock,
+ TBInfo *parent, int parentExtIndex);
+ ~TBInfo ();
+
+ inline Textblock *getTextblock () { return (Textblock*)getWidget (); }
+ };
+
+ class AbsolutelyPositioned: public lout::object::Object
+ {
+ public:
+ core::Widget *widget;
+ int xCB, yCB; // relative to the containing block
+ int width, height;
+ bool dirty;
+
+ AbsolutelyPositioned (OutOfFlowMgr *oofm, core::Widget *widget,
+ Textblock *generatingBlock, int externalIndex);
+ };
+
+ // These two lists store all floats, in the order in which they are
+ // defined. Only used for iterators.
+ lout::container::typed::Vector<Float> *leftFloatsAll, *rightFloatsAll;
+
+ // These two lists store all floats whose generators are already
+ // allocated.
+ SortedFloatsVector *leftFloatsCB, *rightFloatsCB;
+
+ lout::container::typed::HashTable<lout::object::TypedPointer
+ <dw::core::Widget>, Float> *floatsByWidget;
+
+ lout::container::typed::Vector<TBInfo> *tbInfos;
+ lout::container::typed::HashTable<lout::object::TypedPointer <Textblock>,
+ TBInfo> *tbInfosByTextblock;
+
+ lout::container::typed::Vector<AbsolutelyPositioned> *absolutelyPositioned;
+
+ int lastLeftTBIndex, lastRightTBIndex, leftFloatsMark, rightFloatsMark;
+
+ /**
+ * Variant of Widget::wasAllocated(), which can also be used within
+ * OOFM::sizeAllocateEnd(), and also for the generating block.
+ */
+ inline bool wasAllocated (core::Widget *widget) {
+ return widget->wasAllocated () ||
+ (widget == (core::Widget*)containingBlock &&
+ containingBlockWasAllocated); }
+
+ /**
+ * Variant of Widget::getAllocation(), which can also be used
+ * within OOFM::sizeAllocateEnd(), and also for the generating
+ * block.
+ */
+ inline core::Allocation *getAllocation (core::Widget *widget) {
+ return widget == (core::Widget*)containingBlock ?
+ &containingBlockAllocation : widget->getAllocation (); }
+
+ void moveExternalIndices (SortedFloatsVector *list, int oldStartIndex,
+ int diff);
+ Float *findFloatByWidget (core::Widget *widget);
+
+ void moveFromGBToCB (Side side);
+ void sizeAllocateFloats (Side side);
+ int calcFloatX (Float *vloat, Side side, int gbX, int gbWidth,
+ int gbAvailWidth);
+
+ bool hasRelationChanged (TBInfo *tbInfo,int *minFloatPos,
+ core::Widget **minFloat);
+ bool hasRelationChanged (TBInfo *tbInfo, Side side, int *minFloatPos,
+ core::Widget **minFloat);
+ bool hasRelationChanged (bool oldTBAlloc,
+ int oldTBx, int oldTBy, int oldTBw, int oldTBh,
+ int newTBx, int newTBy, int newTBw, int newTBh,
+ bool oldFlAlloc,
+ int oldFlx, int oldFly, int oldFlw, int oldFlh,
+ int newFlx, int newFly, int newFlw, int newFlh,
+ Side side, int *floatPos);
+
+ bool doFloatsExceedCB (Side side);
+ bool haveExtremesChanged (Side side);
+
+ void drawFloats (SortedFloatsVector *list, core::View *view,
+ core::Rectangle *area);
+ void drawAbsolutelyPositioned (core::View *view, core::Rectangle *area);
+ core::Widget *getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y,
+ int level);
+ core::Widget *getAbsolutelyPositionedWidgetAtPoint (int x, int y, int level);
+
+ bool collidesV (Float *vloat, Float *other, int *yReal);
+ bool collidesH (Float *vloat, Float *other, int *yReal);
+
+ void getFloatsListsAndSide (Float *vloat, SortedFloatsVector **listSame,
+ SortedFloatsVector **listOpp, Side *side);
+
+ void getFloatsSize (core::Requisition *cbReq, Side side, int *width,
+ int *height);
+ void getFloatsExtremes (core::Extremes *cbExtr, Side side, int *minWidth,
+ int *maxWidth);
+
+ 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 adjustFloatWidth (int width, core::Extremes *extremes);
+
+ void tellFloatPosition (core::Widget *widget, int yReq);
+
+ void getAbsolutelyPositionedSize (core::Requisition *cbReq, int *width,
+ int *height);
+ void getAbsolutelyPositionedExtremes (core::Extremes *cbExtr, int *minWidth,
+ int *maxWidth);
+ 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 (core::Requisition *cbReq, int *oofWidth, int *oofHeight);
+ void getExtremes (core::Extremes *cbExtr,
+ int *oofMinWidth, int *oofMaxWidth);
+
+ int getLeftBorder (Textblock *textblock, int y, int h, Textblock *lastGB,
+ int lastExtIndex);
+ int getRightBorder (Textblock *textblock, int y, int h, Textblock *lastGB,
+ int lastExtIndex);
+
+ bool hasFloatLeft (Textblock *textblock, int y, int h, Textblock *lastGB,
+ int lastExtIndex);
+ bool hasFloatRight (Textblock *textblock, int y, int h, Textblock *lastGB,
+ int lastExtIndex);
+
+ int getClearPosition (Textblock *tb);
+
+ inline static bool isRefOutOfFlow (int ref)
+ { return ref != -1 && (ref & 1) != 0; }
+ inline static int createRefNormalFlow (int lineNo) { return lineNo << 1; }
+ inline static int getLineNoFromRef (int ref)
+ { return ref == -1 ? ref : (ref >> 1); }
+
+ // for iterators
+ inline int getNumWidgets () {
+ return leftFloatsAll->size() + rightFloatsAll->size() +
+ absolutelyPositioned->size(); }
+
+ inline core::Widget *getWidget (int i) {
+ if (i < leftFloatsAll->size())
+ return leftFloatsAll->get(i)->getWidget ();
+ else if (i < leftFloatsAll->size() + rightFloatsAll->size())
+ return rightFloatsAll->get(i - leftFloatsAll->size())->getWidget ();
+ else
+ return absolutelyPositioned->get(i - (leftFloatsAll->size() +
+ rightFloatsAll->size()))->widget;
+ }
+
+ inline bool affectsLeftBorder (core::Widget *widget) {
+ return widget->getStyle()->vloat == core::style::FLOAT_LEFT; }
+ inline bool affectsRightBorder (core::Widget *widget) {
+ return widget->getStyle()->vloat == core::style::FLOAT_RIGHT; }
+};
+
+} // namespace dw
+
+#endif // __DW_OUTOFFLOWMGR_HH__
diff --git a/dw/ruler.cc b/dw/ruler.cc
index 115dfaa5..2b5288c2 100644
--- a/dw/ruler.cc
+++ b/dw/ruler.cc
@@ -28,17 +28,28 @@ namespace dw {
Ruler::Ruler ()
{
+ setFlags (USES_HINTS);
setFlags (BLOCK_LEVEL);
unsetFlags (HAS_CONTENTS);
+ availWidth = 0;
}
void Ruler::sizeRequestImpl (core::Requisition *requisition)
{
- requisition->width = getStyle()->boxDiffWidth ();
+ requisition->width =
+ lout::misc::max (availWidth, getStyle()->boxDiffWidth ());
requisition->ascent = getStyle()->boxOffsetY ();
requisition->descent = getStyle()->boxRestHeight ();
}
+void Ruler::setWidth (int width)
+{
+ if (availWidth != width) {
+ availWidth = width;
+ queueResize (0, false);
+ }
+}
+
void Ruler::draw (core::View *view, core::Rectangle *area)
{
drawWidgetBox (view, area, false);
diff --git a/dw/ruler.hh b/dw/ruler.hh
index 32e859a1..863792dd 100644
--- a/dw/ruler.hh
+++ b/dw/ruler.hh
@@ -15,8 +15,12 @@ namespace dw {
*/
class Ruler: public core::Widget
{
+private:
+ int availWidth;
+
protected:
void sizeRequestImpl (core::Requisition *requisition);
+ void setWidth (int width);
void draw (core::View *view, core::Rectangle *area);
public:
diff --git a/dw/style.cc b/dw/style.cc
index 8ec230a1..5edb7047 100644
--- a/dw/style.cc
+++ b/dw/style.cc
@@ -17,8 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-
#include <stdio.h>
#include <string.h>
#include <unistd.h>
@@ -75,6 +73,10 @@ void StyleAttrs::initValues ()
backgroundPositionX = createPerLength (0);
backgroundPositionY = createPerLength (0);
width = height = lineHeight = LENGTH_AUTO;
+ vloat = FLOAT_NONE;
+ clear = CLEAR_NONE;
+ position = POSITION_STATIC;
+ top = bottom = left = right = LENGTH_AUTO;
textIndent = 0;
margin.setVal (0);
borderWidth.setVal (0);
@@ -101,6 +103,11 @@ void StyleAttrs::resetValues ()
valign = VALIGN_BASELINE;
textAlignChar = '.';
+ vloat = FLOAT_NONE; /** \todo Correct? Check specification. */
+ clear = CLEAR_NONE; /** \todo Correct? Check specification. */
+ position = POSITION_STATIC; /** \todo Correct? Check specification. */
+ top = bottom = left = right = LENGTH_AUTO; /** \todo Correct? Check
+ specification. */
backgroundColor = NULL;
backgroundImage = NULL;
backgroundRepeat = BACKGROUND_REPEAT;
@@ -156,6 +163,13 @@ bool StyleAttrs::equals (object::Object *other) {
valign == otherAttrs->valign &&
textAlignChar == otherAttrs->textAlignChar &&
textTransform == otherAttrs->textTransform &&
+ vloat == otherAttrs->vloat &&
+ clear == otherAttrs->clear &&
+ position == otherAttrs->position &&
+ top == otherAttrs->top &&
+ bottom == otherAttrs->bottom &&
+ left == otherAttrs->left &&
+ right == otherAttrs->right &&
hBorderSpacing == otherAttrs->hBorderSpacing &&
vBorderSpacing == otherAttrs->vBorderSpacing &&
wordSpacing == otherAttrs->wordSpacing &&
@@ -201,6 +215,13 @@ int StyleAttrs::hashValue () {
valign +
textAlignChar +
textTransform +
+ vloat +
+ clear +
+ position +
+ top +
+ bottom +
+ left +
+ right +
hBorderSpacing +
vBorderSpacing +
wordSpacing +
@@ -316,6 +337,13 @@ void Style::copyAttrs (StyleAttrs *attrs)
valign = attrs->valign;
textAlignChar = attrs->textAlignChar;
textTransform = attrs->textTransform;
+ vloat = attrs->vloat;
+ clear = attrs->clear;
+ position = attrs->position;
+ top = attrs->top;
+ bottom = attrs->bottom;
+ left = attrs->left;
+ right = attrs->right;
hBorderSpacing = attrs->hBorderSpacing;
vBorderSpacing = attrs->vBorderSpacing;
wordSpacing = attrs->wordSpacing;
diff --git a/dw/style.hh b/dw/style.hh
index e0ce9d89..2cc258bf 100644
--- a/dw/style.hh
+++ b/dw/style.hh
@@ -296,7 +296,6 @@ enum ListStylePosition {
LIST_STYLE_POSITION_INSIDE,
LIST_STYLE_POSITION_OUTSIDE
};
-
enum ListStyleType {
LIST_STYLE_TYPE_DISC,
LIST_STYLE_TYPE_CIRCLE,
@@ -332,6 +331,13 @@ enum FontVariant {
FONT_VARIANT_SMALL_CAPS
};
+enum Position {
+ POSITION_STATIC,
+ POSITION_RELATIVE,
+ POSITION_ABSOLUTE,
+ POSITION_FIXED,
+};
+
enum TextDecoration {
TEXT_DECORATION_NONE = 0,
TEXT_DECORATION_UNDERLINE = 1 << 0,
@@ -348,6 +354,19 @@ enum WhiteSpace {
WHITE_SPACE_PRE_LINE,
};
+enum FloatType {
+ FLOAT_NONE,
+ FLOAT_LEFT,
+ FLOAT_RIGHT
+};
+
+enum ClearType {
+ CLEAR_LEFT,
+ CLEAR_RIGHT,
+ CLEAR_BOTH,
+ CLEAR_NONE
+};
+
/**
* \brief Type for representing all lengths within dw::core::style.
*
@@ -503,6 +522,12 @@ public:
VAlignType valign;
char textAlignChar; /* In future, strings will be supported. */
TextTransform textTransform;
+
+ FloatType vloat; /* "float" is a keyword. */
+ ClearType clear;
+
+ Position position;
+ Length top, bottom, left, right;
int hBorderSpacing, vBorderSpacing, wordSpacing;
Length width, height, lineHeight, textIndent;
diff --git a/dw/table.cc b/dw/table.cc
index b6f7209b..565dfc9e 100644
--- a/dw/table.cc
+++ b/dw/table.cc
@@ -482,7 +482,7 @@ void Table::reallocChildren (int newNumCols, int newNumRows)
void Table::calcCellSizes ()
{
- if (needsResize ())
+ if (needsResize () || resizeQueued ())
forceCalcCellSizes ();
}
@@ -640,7 +640,7 @@ void Table::apportionRowSpan ()
*/
void Table::calcColumnExtremes ()
{
- if (extremesChanged ())
+ if (extremesChanged () || extremesQueued ())
forceCalcColumnExtremes ();
}
@@ -1130,7 +1130,7 @@ Table::TableIterator::TableIterator (Table *table,
else if (index >= table->children->size ())
content.type = core::Content::END;
else {
- content.type = core::Content::WIDGET;
+ content.type = core::Content::WIDGET_IN_FLOW;
content.widget = table->children->get(index)->cell.widget;
}
}
@@ -1152,8 +1152,8 @@ bool Table::TableIterator::next ()
if (content.type == core::Content::END)
return false;
- // tables only contain widgets:
- if ((getMask() & core::Content::WIDGET) == 0) {
+ // tables only contain widgets (in flow):
+ if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) {
content.type = core::Content::END;
return false;
}
@@ -1167,7 +1167,7 @@ bool Table::TableIterator::next ()
} while (table->children->get(index) == NULL ||
table->children->get(index)->type != Child::CELL);
- content.type = core::Content::WIDGET;
+ content.type = core::Content::WIDGET_IN_FLOW;
content.widget = table->children->get(index)->cell.widget;
return true;
}
@@ -1179,8 +1179,8 @@ bool Table::TableIterator::prev ()
if (content.type == core::Content::START)
return false;
- // tables only contain widgets:
- if ((getMask() & core::Content::WIDGET) == 0) {
+ // tables only contain widgets (in flow):
+ if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) {
content.type = core::Content::START;
return false;
}
@@ -1194,7 +1194,7 @@ bool Table::TableIterator::prev ()
} while (table->children->get(index) == NULL ||
table->children->get(index)->type != Child::CELL);
- content.type = core::Content::WIDGET;
+ content.type = core::Content::WIDGET_IN_FLOW;
content.widget = table->children->get(index)->cell.widget;
return true;
}
diff --git a/dw/table.hh b/dw/table.hh
index 1d14ec07..6966a163 100644
--- a/dw/table.hh
+++ b/dw/table.hh
@@ -71,7 +71,7 @@ namespace dw {
* is the case.
*
* [C] Whether this function is called, depends on NEEDS_RESIZE /
- * EXTREMES_CHANGED.
+ * RESIZE_QUEUED / EXTREMES_CHANGED / EXTREMES_QUEUED.
*
*
* <h4>Apportionment</h4>
diff --git a/dw/tablecell.cc b/dw/tablecell.cc
index 8612f620..67d6cf2d 100644
--- a/dw/tablecell.cc
+++ b/dw/tablecell.cc
@@ -107,7 +107,7 @@ int TableCell::getValue ()
void TableCell::setMaxValue (int maxValue, int value)
{
line1Offset = maxValue - value;
- queueResize (0, true);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), true);
}
} // namespace dw
diff --git a/dw/textblock.cc b/dw/textblock.cc
index 2c4ca20b..49030d26 100644
--- a/dw/textblock.cc
+++ b/dw/textblock.cc
@@ -25,13 +25,14 @@
#include "../lout/debug.hh"
#include <stdio.h>
-#include <math.h>
+#include <math.h> // remove again?
+#include <limits.h>
/*
* Local variables
*/
- /* The tooltip under mouse pointer in current textblock. No ref. hold.
- * (having one per view looks not worth the extra clutter). */
+/* The tooltip under mouse pointer in current textblock. No ref. hold.
+ * (having one per view looks not worth the extra clutter). */
static dw::core::style::Tooltip *hoverTooltip = NULL;
@@ -228,6 +229,7 @@ Textblock::Textblock (bool limitTextWidth)
setFlags (USES_HINTS);
setButtonSensitive(true);
+ containingBlock = NULL;
hasListitemValue = false;
innerPadding = 0;
line1Offset = 0;
@@ -250,15 +252,14 @@ Textblock::Textblock (bool limitTextWidth)
nonTemporaryLines = 0;
words = new misc::NotSoSimpleVector <Word> (1);
anchors = new misc::SimpleVector <Anchor> (1);
-
- //DBG_OBJ_SET_NUM(this, "num_lines", num_lines);
+ outOfFlowMgr = NULL;
wrapRefLines = wrapRefParagraphs = -1;
- //DBG_OBJ_SET_NUM(this, "last_line_width", last_line_width);
- //DBG_OBJ_SET_NUM(this, "last_line_par_min", last_line_par_min);
- //DBG_OBJ_SET_NUM(this, "last_line_par_max", last_line_par_max);
- //DBG_OBJ_SET_NUM(this, "wrap_ref", wrap_ref);
+ DBG_OBJ_SET_NUM ("lines.size", lines->size ());
+ DBG_OBJ_SET_NUM ("words.size", words->size ());
+ DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines);
+ DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs);
hoverLink = -1;
@@ -267,6 +268,12 @@ Textblock::Textblock (bool limitTextWidth)
availAscent = 100;
availDescent = 0;
+ DBG_OBJ_SET_NUM ("availWidth", availWidth);
+ DBG_OBJ_SET_NUM ("availAscent", availAscent);
+ DBG_OBJ_SET_NUM ("availDescent", availDescent);
+
+ verticalOffset = 0;
+
this->limitTextWidth = limitTextWidth;
for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
@@ -276,6 +283,8 @@ Textblock::Textblock (bool limitTextWidth)
hlEnd[layer].index = 0;
hlEnd[layer].nChar = 0;
}
+
+ initNewLine ();
}
Textblock::~Textblock ()
@@ -285,18 +294,8 @@ Textblock::~Textblock ()
/* make sure not to call a free'd tooltip (very fast overkill) */
hoverTooltip = NULL;
- for (int i = 0; i < words->size(); i++) {
- Word *word = words->getRef (i);
-
- if (word->content.type == core::Content::WIDGET)
- delete word->content.widget;
-
- removeWordImgRenderer (i);
- removeSpaceImgRenderer (i);
-
- word->style->unref ();
- word->spaceStyle->unref ();
- }
+ for (int i = 0; i < words->size(); i++)
+ cleanupWord (i);
for (int i = 0; i < anchors->size(); i++) {
Anchor *anchor = anchors->getRef (i);
@@ -309,6 +308,16 @@ Textblock::~Textblock ()
delete words;
delete anchors;
+ if(outOfFlowMgr) {
+ // I feel more comfortable by letting the textblock delete these
+ // widgets, instead of doing this in ~OutOfFlowMgr.
+
+ for (int i = 0; i < outOfFlowMgr->getNumWidgets (); i++)
+ delete outOfFlowMgr->getWidget (i);
+
+ delete outOfFlowMgr;
+ }
+
/* Make sure we don't own widgets anymore. Necessary before call of
parent class destructor. (???) */
words = NULL;
@@ -323,7 +332,8 @@ Textblock::~Textblock ()
*/
void Textblock::sizeRequestImpl (core::Requisition *requisition)
{
- PRINTF ("[%p] SIZE_REQUEST: ...\n", this);
+ DBG_OBJ_MSG ("resize", 0, "<b>sizeRequestImpl</b> ()");
+ DBG_OBJ_MSG_START ();
rewrap ();
showMissingLines ();
@@ -331,18 +341,18 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
if (lines->size () > 0) {
Line *lastLine = lines->getRef (lines->size () - 1);
requisition->width = lastLine->maxLineWidth;
-
- PRINTF ("[%p] SIZE_REQUEST: lastLine->maxLineWidth = %d\n",
- this, lastLine->maxLineWidth);
-
- PRINTF ("[%p] SIZE_REQUEST: lines[0]->boxAscent = %d\n",
- this, lines->getRef(0)->boxAscent);
- PRINTF ("[%p] SIZE_REQUEST: lines[%d]->top = %d\n",
- this, lines->size () - 1, lastLine->top);
- PRINTF ("[%p] SIZE_REQUEST: lines[%d]->boxAscent = %d\n",
- this, lines->size () - 1, lastLine->boxAscent);
- PRINTF ("[%p] SIZE_REQUEST: lines[%d]->boxDescent = %d\n",
- this, lines->size () - 1, lastLine->boxDescent);
+
+ DBG_OBJ_MSGF ("resize", 1, "lines[%d]->maxLineWidth = %d",
+ lines->size () - 1, lastLine->maxLineWidth);
+
+ DBG_OBJ_MSGF ("resize", 1, "lines[0]->boxAscent = %d",
+ lines->getRef(0)->boxAscent);
+ DBG_OBJ_MSGF ("resize", 1, "lines[%d]->top = %d",
+ lines->size () - 1, lastLine->top);
+ DBG_OBJ_MSGF ("resize", 1, "lines[%d]->boxAscent = %d",
+ lines->size () - 1, lastLine->boxAscent);
+ DBG_OBJ_MSGF ("resize", 1, "lines[%d]->boxDescent = %d",
+ lines->size () - 1, lastLine->boxDescent);
/* Note: the breakSpace of the last line is ignored, so breaks
at the end of a textblock are not visible. */
@@ -356,18 +366,44 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
requisition->descent = 0;
}
- PRINTF ("[%p] SIZE_REQUEST: inner padding = %d, boxDiffWidth = %d\n",
- this, innerPadding, getStyle()->boxDiffWidth ());
+ DBG_OBJ_MSGF ("resize", 1, "inner padding = %d, boxDiffWidth = %d",
+ innerPadding, getStyle()->boxDiffWidth ());
requisition->width += innerPadding + getStyle()->boxDiffWidth ();
- requisition->ascent += getStyle()->boxOffsetY ();
+ requisition->ascent += verticalOffset + getStyle()->boxOffsetY ();
requisition->descent += getStyle()->boxRestHeight ();
- if (requisition->width < availWidth)
+ // Dealing with parts out of flow, which may overlap the borders of
+ // the text block. Base lines are ignored here: they do not play a
+ // role (currently) and caring about them (for the future) would
+ // cause too much problems.
+
+ DBG_OBJ_MSGF ("resize", 1, "before considering OOF widgets: %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+
+ if (outOfFlowMgr) {
+ int oofWidth, oofHeight;
+ outOfFlowMgr->getSize (requisition, &oofWidth, &oofHeight);
+ requisition->width = misc::max (requisition->width, oofWidth);
+ if (oofHeight > requisition->ascent + requisition->descent)
+ requisition->descent = oofHeight - requisition->ascent;
+ }
+
+ DBG_OBJ_MSGF ("resize", 1,
+ "before considering availWidth (= %d): %d * (%d + %d)",
+ availWidth, requisition->width, requisition->ascent,
+ requisition->descent);
+
+ if (requisition->width < availWidth) {
requisition->width = availWidth;
+ DBG_OBJ_MSGF ("resize", 1, "adjusting to availWidth => %d",
+ requisition->width);
+ }
- PRINTF ("[%p] SIZE_REQUEST: %d x %d + %d\n", this, requisition->width,
- requisition->ascent, requisition->descent);
+ DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+
+ DBG_OBJ_MSG_END ();
}
/**
@@ -375,7 +411,7 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
*/
void Textblock::getWordExtremes (Word *word, core::Extremes *extremes)
{
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
if (word->content.widget->usesHints ())
word->content.widget->getExtremes (extremes);
else {
@@ -405,7 +441,8 @@ void Textblock::getWordExtremes (Word *word, core::Extremes *extremes)
void Textblock::getExtremesImpl (core::Extremes *extremes)
{
- PRINTF ("[%p] GET_EXTREMES ...\n", this);
+ DBG_OBJ_MSG ("resize", 0, "<b>getExtremesImpl</b>");
+ DBG_OBJ_MSG_START ();
fillParagraphs ();
@@ -417,14 +454,33 @@ void Textblock::getExtremesImpl (core::Extremes *extremes)
Paragraph *lastPar = paragraphs->getLastRef ();
extremes->minWidth = lastPar->maxParMin;
extremes->maxWidth = lastPar->maxParMax;
+
+ DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMin = %d",
+ paragraphs->size () - 1, lastPar->maxParMin);
+ DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMax = %d",
+ paragraphs->size () - 1, lastPar->maxParMax);
}
int diff = innerPadding + getStyle()->boxDiffWidth ();
extremes->minWidth += diff;
extremes->maxWidth += diff;
- PRINTF ("[%p] GET_EXTREMES => %d / %d\n",
- this, extremes->minWidth, extremes->maxWidth);
+ if (outOfFlowMgr) {
+ int oofMinWidth, oofMaxWidth;
+ outOfFlowMgr->getExtremes (extremes, &oofMinWidth, &oofMaxWidth);
+
+ DBG_OBJ_MSGF ("resize", 1, "extremes: %d / %d, corrected: %d / %d",
+ extremes->minWidth, extremes->maxWidth,
+ oofMinWidth, oofMaxWidth);
+
+ extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth);
+ extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth);
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d / %d",
+ extremes->minWidth, extremes->maxWidth);
+
+ DBG_OBJ_MSG_END ();
}
@@ -436,6 +492,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
showMissingLines ();
+ if(outOfFlowMgr)
+ outOfFlowMgr->sizeAllocateStart (allocation);
+
int lineIndex, wordIndex;
Line *line;
Word *word;
@@ -449,7 +508,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 +518,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 +532,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 +591,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 +627,122 @@ void Textblock::resizeDrawImpl ()
void Textblock::markSizeChange (int ref)
{
- PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", this, ref, wrapRefLines);
+ DBG_OBJ_MSGF ("resize", 0, "<b>markSizeChange</b> (%d)", ref);
+ DBG_OBJ_MSG_START ();
- /* By the way: ref == -1 may have two different causes: (i) flush()
- calls "queueResize (-1, true)", when no rewrapping is necessary;
- and (ii) a word may have parentRef == -1 , when it is not yet
- added to a line. In the latter case, nothing has to be done
- now, but addLine(...) will do everything necessary. */
- if (ref != -1) {
- if (wrapRefLines == -1)
- wrapRefLines = ref;
- else
- wrapRefLines = misc::min (wrapRefLines, ref);
- }
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markSizeChange (ref);
+ } else {
+ PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n",
+ this, ref, wrapRefLines);
+
+ /* By the way: ref == -1 may have two different causes: (i) flush()
+ calls "queueResize (-1, true)", when no rewrapping is necessary;
+ and (ii) a word may have parentRef == -1 , when it is not yet
+ added to a line. In the latter case, nothing has to be done
+ now, but addLine(...) will do everything necessary. */
+ if (ref != -1) {
+ if (wrapRefLines == -1)
+ wrapRefLines = OutOfFlowMgr::getLineNoFromRef (ref);
+ else
+ wrapRefLines = misc::min (wrapRefLines,
+ OutOfFlowMgr::getLineNoFromRef (ref));
+ }
- PRINTF (" ... => %d\n", wrapRefLine);
+ DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines);
+
+ // It seems that sometimes (even without floats) the lines
+ // structure is changed, so that wrapRefLines may refers to a
+ // line which does not exist anymore. Should be examined
+ // again. Until then, setting wrapRefLines to the same value is
+ // a workaround.
+ markExtremesChange (ref);
+ }
- // It seems that sometimes the lines structure is changed, so that
- // wrapRefLines may refers to a line which does not exist
- // anymore. Should be examined again. Until then, setting
- // wrapRefLines to the same value is a workaround.
- markExtremesChange (ref);
+ DBG_OBJ_MSG_END ();
}
void Textblock::markExtremesChange (int ref)
{
- PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n",
- this, ref, wrapRefParagraphs);
-
- /* By the way: ref == -1 may have two different causes: (i) flush()
- calls "queueResize (-1, true)", when no rewrapping is necessary;
- and (ii) a word may have parentRef == -1 , when it is not yet
- added to a line. In the latter case, nothing has to be done
- now, but addLine(...) will do everything necessary. */
- if (ref != -1) {
- if (wrapRefParagraphs == -1)
- wrapRefParagraphs = ref;
- else
- wrapRefParagraphs = misc::min (wrapRefParagraphs, ref);
+ DBG_OBJ_MSGF ("resize", 1, "<b>markExtremesChange</b> (%d)", ref);
+ DBG_OBJ_MSG_START ();
+
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markExtremesChange (ref);
+ } else {
+ PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n",
+ this, ref, wrapRefParagraphs);
+
+ /* By the way: ref == -1 may have two different causes: (i) flush()
+ calls "queueResize (-1, true)", when no rewrapping is necessary;
+ and (ii) a word may have parentRef == -1 , when it is not yet
+ added to a line. In the latter case, nothing has to be done
+ now, but addLine(...) will do everything necessary. */
+ if (ref != -1) {
+ if (wrapRefParagraphs == -1)
+ wrapRefParagraphs = OutOfFlowMgr::getLineNoFromRef (ref);
+ else
+ wrapRefParagraphs =
+ misc::min (wrapRefParagraphs,
+ OutOfFlowMgr::getLineNoFromRef (ref));
+ }
+
+ DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs);
}
- PRINTF (" ... => %d\n", wrapRefParagraphs);
+ DBG_OBJ_MSG_END ();
+}
+
+void Textblock::notifySetAsTopLevel()
+{
+ PRINTF ("%p becomes toplevel\n", this);
+ containingBlock = this;
+ PRINTF ("-> %p is its own containing block\n", this);
+}
+
+bool Textblock::isContainingBlock (Widget *widget)
+{
+ return
+ // Of course, only textblocks are considered as containing
+ // blocks.
+ widget->instanceOf (Textblock::CLASS_ID) &&
+ // The second condition: that this block is "out of flow", in a
+ // wider sense.
+ (// The toplevel widget is "out of flow", since there is no
+ // parent, and so no context.
+ widget->getParent() == NULL ||
+ // A similar reasoning applies to a widget with another parent
+ // than a textblock (typical example: a table cell (this is
+ // also a text block) within a table widget).
+ !widget->getParent()->instanceOf (Textblock::CLASS_ID) ||
+ // Finally, "out of flow" in a narrower sense: floats and
+ // absolute positions.
+ OutOfFlowMgr::isWidgetOutOfFlow (widget));
+}
+
+void Textblock::notifySetParent ()
+{
+ PRINTF ("%p becomes a child of %p\n", this, getParent());
+
+ // Search for containing Box.
+ containingBlock = NULL;
+
+ for (Widget *widget = this; widget != NULL && containingBlock == NULL;
+ widget = widget->getParent())
+ if (isContainingBlock (widget)) {
+ containingBlock = (Textblock*)widget;
+
+ if (containingBlock == this) {
+ PRINTF ("-> %p is its own containing block\n", this);
+ } else {
+ PRINTF ("-> %p becomes containing block of %p\n",
+ containingBlock, this);
+ }
+ }
+
+ assert (containingBlock != NULL);
}
void Textblock::setWidth (int width)
@@ -609,43 +750,46 @@ void Textblock::setWidth (int width)
/* If limitTextWidth is set to YES, a queueResize() may also be
* necessary. */
if (availWidth != width || limitTextWidth) {
- //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
- // "setWidth: Calling queueResize, "
- // "in page with %d word(s)\n",
- // words->size());
+ DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width);
+ DBG_OBJ_MSG_START ();
availWidth = width;
- queueResize (0, false);
+ DBG_OBJ_SET_NUM ("availWidth", availWidth);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
redrawY = 0;
+
+ DBG_OBJ_MSG_END ();
}
}
void Textblock::setAscent (int ascent)
{
if (availAscent != ascent) {
- //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
- // "setAscent: Calling queueResize, "
- // "in page with %d word(s)\n",
- // words->size());
+ DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent);
+ DBG_OBJ_MSG_START ();
availAscent = ascent;
- queueResize (0, false);
+ DBG_OBJ_SET_NUM ("availAscent", availAscent);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
+
+ DBG_OBJ_MSG_END ();
}
}
void Textblock::setDescent (int descent)
{
if (availDescent != descent) {
- //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
- // "setDescent: Calling queueResize, "
- // "in page with %d word(s)\n",
- // words->size());
+ DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent);
+ DBG_OBJ_MSG_START ();
availDescent = descent;
- queueResize (0, false);
+ DBG_OBJ_SET_NUM ("availDescent", availDescent);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
+
+ DBG_OBJ_MSG_END ();
}
}
@@ -753,7 +897,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 +906,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 +1005,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;
@@ -884,6 +1030,8 @@ core::Iterator *Textblock::iterator (core::Content::Type mask, bool atEnd)
*/
void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size)
{
+ DBG_OBJ_MSGF ("resize", 0, "<b>calcWidgetSize</b> (%p, ...)", widget);
+
core::Requisition requisition;
int availWidth, availAscent, availDescent;
core::style::Style *wstyle = widget->getStyle();
@@ -894,14 +1042,38 @@ 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);
+
+ DBG_OBJ_MSGF ("resize", 1, "setting hints: %d, %d, %d",
+ corrAvailWidth, availAscent, availDescent);
+ widget->setWidth (corrAvailWidth);
widget->setAscent (availAscent);
widget->setDescent (availDescent);
widget->sizeRequest (size);
+ DBG_OBJ_MSGF ("resize", 1, "sizeRequest => %d * (%d + %d)",
+ size->width, size->ascent, size->descent);
} else {
if (wstyle->width == core::style::LENGTH_AUTO ||
- wstyle->height == core::style::LENGTH_AUTO)
+ wstyle->height == core::style::LENGTH_AUTO) {
widget->sizeRequest (&requisition);
+ DBG_OBJ_MSGF ("resize", 1, "AUTO; sizeRequest => %d * (%d + %d)",
+ requisition.width, requisition.ascent,
+ requisition.descent);
+ }
if (wstyle->width == core::style::LENGTH_AUTO)
size->width = requisition.width;
@@ -934,6 +1106,10 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size)
/* ascent and descent in words do not contain margins. */
size->ascent -= wstyle->margin.top;
size->descent -= wstyle->margin.bottom;
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)",
+ size->width, size->ascent, size->descent);
+ DBG_OBJ_MSG_END ();
}
/*
@@ -1245,7 +1421,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 +1432,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 +1479,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 +1516,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 +1542,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 +1599,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 +1641,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 +1660,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area)
drawLine (line, view, area);
}
+
+ if(outOfFlowMgr)
+ outOfFlowMgr->draw(view, area);
}
/**
@@ -1457,11 +1671,18 @@ void Textblock::draw (core::View *view, core::Rectangle *area)
Textblock::Word *Textblock::addWord (int width, int ascent, int descent,
short flags, core::style::Style *style)
{
+ DBG_OBJ_MSGF ("construct.word", 0, "<b>addWord</b> (%d * (%d + %d), %d, %p)",
+ width, ascent, descent, flags, style);
+ DBG_OBJ_MSG_START ();
+
words->increase ();
+ DBG_OBJ_SET_NUM ("words.size", words->size ());
int wordNo = words->size () - 1;
initWord (wordNo);
fillWord (wordNo, width, ascent, descent, flags, style);
- return words->getRef (wordNo);;
+
+ DBG_OBJ_MSG_END ();
+ return words->getRef (wordNo);
}
/**
@@ -1476,6 +1697,21 @@ void Textblock::initWord (int wordNo)
word->spaceImgRenderer = NULL;
}
+void Textblock::cleanupWord (int wordNo)
+{
+ Word *word = words->getRef (wordNo);
+
+ if (word->content.type == core::Content::WIDGET_IN_FLOW)
+ delete word->content.widget;
+ /** \todo Widget references? What about texts? */
+
+ removeWordImgRenderer (wordNo);
+ removeSpaceImgRenderer (wordNo);
+
+ word->style->unref ();
+ word->spaceStyle->unref ();
+}
+
void Textblock::removeWordImgRenderer (int wordNo)
{
Word *word = words->getRef (wordNo);
@@ -1936,6 +2172,16 @@ void Textblock::addText0 (const char *text, size_t len, short flags,
word->content.type = core::Content::TEXT;
word->content.text = layout->textZone->strndup(text, len);
+ DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", "TEXT");
+ DBG_OBJ_ARRATTRSET_STR ("words", words->size () - 1,
+ "text/widget/breakSpace", word->content.text);
+
+ // The following debug message may be useful to identify the
+ // different textblocks.
+
+ //if (words->size() == 1)
+ // printf ("[%p] first word: '%s'\n", this, text);
+
processWord (words->size () - 1);
}
@@ -1944,28 +2190,62 @@ void Textblock::addText0 (const char *text, size_t len, short flags,
*/
void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
{
- Word *word;
- core::Requisition size;
-
/* We first assign -1 as parent_ref, since the call of widget->size_request
* will otherwise let this Textblock be rewrapped from the beginning.
* (parent_ref is actually undefined, but likely has the value 0.) At the,
* end of this function, the correct value is assigned. */
widget->parentRef = -1;
- PRINTF ("%p becomes child of %p\n", widget, this);
-
- widget->setParent (this);
widget->setStyle (style);
- calcWidgetSize (widget, &size);
- word = addWord (size.width, size.ascent, size.descent, 0, style);
+ PRINTF ("adding the %s %p to %p (word %d) ...\n",
+ widget->getClassName(), widget, this, words->size());
- word->content.type = core::Content::WIDGET;
- word->content.widget = widget;
+ if (containingBlock->outOfFlowMgr == NULL) {
+ containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock);
+ DBG_OBJ_ASSOC (containingBlock, containingBlock->outOfFlowMgr);
+ }
+
+ if (OutOfFlowMgr::isWidgetHandledByOOFM (widget)) {
+ PRINTF (" -> out of flow.\n");
+
+ widget->setParent (containingBlock);
+ widget->setGenerator (this);
+ containingBlock->outOfFlowMgr->addWidgetOOF (widget, this,
+ words->size ());
+ Word *word = addWord (0, 0, 0, 0, style);
+ word->content.type = core::Content::WIDGET_OOF_REF;
+ word->content.widget = widget;
+
+ // After a out-of-flow reference, breaking is allowed. (This avoids some
+ // problems with breaking near float definitions.)
+ setBreakOption (word, style, 0, 0, false);
- //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1,
- // word->content.widget);
+ DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type",
+ "WIDGET_OOF_REF");
+ DBG_OBJ_ARRATTRSET_PTR ("words", words->size () - 1,
+ "text/widget/breakSpace", word->content.widget);
+ } else {
+ PRINTF (" -> within flow.\n");
+
+ widget->setParent (this);
+
+ // TODO Replace (perhaps) later "textblock" by "OOF aware widget".
+ if (widget->instanceOf (Textblock::CLASS_ID))
+ containingBlock->outOfFlowMgr->addWidgetInFlow ((Textblock*)widget,
+ this, words->size ());
+
+ core::Requisition size;
+ calcWidgetSize (widget, &size);
+ Word *word = addWord (size.width, size.ascent, size.descent, 0, style);
+ word->content.type = core::Content::WIDGET_IN_FLOW;
+ word->content.widget = widget;
+
+ DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type",
+ "WIDGET_IN_FLOW");
+ DBG_OBJ_ARRATTRSET_PTR ("words", words->size () - 1,
+ "text/widget/breakSpace", word->content.widget);
+ }
processWord (words->size () - 1);
//DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
@@ -2033,7 +2313,7 @@ void Textblock::addSpace (core::style::Style *style)
/**
* Add a break option (see setBreakOption() for details). Used instead
- * of addStyle for ideographic characters.
+ * of addSpace for ideographic characters.
*
* When "forceBreak" is true, a break is even possible within PRE etc.
*/
@@ -2067,8 +2347,10 @@ void Textblock::fillSpace (int wordNo, core::style::Style *style)
// TODO: This line does not work: addBreakOption (word, style);
- // Do not override a previously set break penalty.
- if (!word->content.space) {
+ if (// Do not override a previously set break penalty:
+ !word->content.space &&
+ // OOF references are considered specially, and must not have a space:
+ word->content.type != core::Content::WIDGET_OOF_REF) {
setBreakOption (word, style, 0, 0, false);
word->content.space = true;
@@ -2140,7 +2422,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
/* A break may not be the first word of a page, or directly after
the bullet/number (which is the first word) in a list item. (See
- also comment in Dw_page_size_request.) */
+ also comment in sizeRequest.) */
if (words->size () == 0 ||
(hasListitemValue && words->size () == 1)) {
/* This is a bit hackish: If a break is added as the
@@ -2149,23 +2431,23 @@ void Textblock::addParbreak (int space, core::style::Style *style)
a widget is used as a text box (lists, blockquotes, list
items etc) -- then we simply adjust the break before, in a
way that the space is in any case visible. */
- Widget *widget;
-
- /* Find the widget where to adjust the breakSpace. */
- for (widget = this;
- widget->getParent() &&
- widget->getParent()->instanceOf (Textblock::CLASS_ID);
+ /* Find the widget where to adjust the breakSpace. (Only
+ consider normal flow, no floats etc.) */
+ for (Widget *widget = this;
+ widget->getParent() != NULL &&
+ widget->getParent()->instanceOf (Textblock::CLASS_ID) &&
+ !OutOfFlowMgr::isRefOutOfFlow (widget->parentRef);
widget = widget->getParent ()) {
Textblock *textblock2 = (Textblock*)widget->getParent ();
int index = textblock2->hasListitemValue ? 1 : 0;
bool isfirst = (textblock2->words->getRef(index)->content.type
- == core::Content::WIDGET
+ == core::Content::WIDGET_IN_FLOW
&& textblock2->words->getRef(index)->content.widget
== widget);
if (!isfirst) {
- /* The page we searched for has been found. */
+ /* The text block we searched for has been found. */
Word *word2;
- int lineno = widget->parentRef;
+ int lineno = OutOfFlowMgr::getLineNoFromRef (widget->parentRef);
if (lineno > 0 &&
(word2 =
@@ -2174,7 +2456,8 @@ void Textblock::addParbreak (int space, core::style::Style *style)
word2->content.type == core::Content::BREAK) {
if (word2->content.breakSpace < space) {
word2->content.breakSpace = space;
- textblock2->queueResize (lineno, false);
+ textblock2->queueResize
+ (OutOfFlowMgr::createRefNormalFlow (lineno), false);
textblock2->mustQueueResize = false;
}
}
@@ -2182,6 +2465,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
}
/* Otherwise continue to examine parents. */
}
+
/* Return in any case. */
return;
}
@@ -2205,6 +2489,12 @@ void Textblock::addParbreak (int space, core::style::Style *style)
word->content.type = core::Content::BREAK;
word->badnessAndPenalty.setPenalty (PENALTY_FORCE_BREAK);
word->content.breakSpace = space;
+
+ DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", "BREAK");
+ DBG_OBJ_ARRATTRSET_NUM ("words", words->size () - 1,
+ "text/widget/breakSpace", word->content.breakSpace);
+
+ breakAdded ();
processWord (words->size () - 1);
}
@@ -2230,9 +2520,41 @@ void Textblock::addLinebreak (core::style::Style *style)
word->content.type = core::Content::BREAK;
word->badnessAndPenalty.setPenalty (PENALTY_FORCE_BREAK);
word->content.breakSpace = 0;
+
+ DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", "BREAK");
+ DBG_OBJ_ARRATTRSET_NUM ("words", words->size () - 1,
+ "text/widget/breakSpace", word->content.breakSpace);
+
+ breakAdded ();
processWord (words->size () - 1);
}
+/**
+ * Called directly after a (line or paragraph) break has been added.
+ */
+void Textblock::breakAdded ()
+{
+ assert (words->size () >= 1);
+ assert (words->getRef(words->size () - 1)->content.type
+ == core::Content::BREAK);
+
+ // Any space before is removed. It is not used; on the other hand,
+ // this snippet (an example from a real-world debugging session)
+ // would cause problems:
+ //
+ // <input style="width: 100%" .../>
+ // <button ...>...</button>
+ //
+ // (Notice the space between <input> and <button>, and also that
+ // the HTML parser will insert a BREAK between them.) The <input>
+ // would be given the available width ("width: 100%"), but the
+ // actual width (Word::totalWidth) would include the space, so that
+ // the width of the line is larger than the available width.
+
+ if (words->size () >= 2)
+ words->getRef(words->size () - 2)->origSpace =
+ words->getRef(words->size () - 2)->effSpace = 0;
+}
/**
* \brief Search recursively through widget.
@@ -2242,6 +2564,10 @@ void Textblock::addLinebreak (core::style::Style *style)
*/
core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
{
+ //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n",
+ // 3 * level, "", getClassName (), this, allocation.x, allocation.y,
+ // allocation.width, allocation.ascent, allocation.descent);
+
int lineIndex, wordIndex;
Line *line;
@@ -2252,7 +2578,15 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
return NULL;
}
- lineIndex = findLineIndex (y - allocation.y);
+ // First, search for widgets out of flow, notably floats, since
+ // there are cases where they overlap child textblocks. Should
+ // later be refined using z-index.
+ Widget *oofWidget =
+ outOfFlowMgr ? outOfFlowMgr->getWidgetAtPoint (x, y, level) : NULL;
+ if (oofWidget)
+ return oofWidget;
+
+ lineIndex = findLineIndexWhenAllocated (y - allocation.y);
if (lineIndex < 0 || lineIndex >= lines->size ()) {
return this;
@@ -2263,7 +2597,7 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
Word *word = words->getRef (wordIndex);
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
core::Widget * childAtPoint;
childAtPoint = word->content.widget->getWidgetAtPoint (x, y,
level + 1);
@@ -2303,12 +2637,14 @@ void Textblock::handOverBreak (core::style::Style *style)
*/
void Textblock::flush ()
{
- PRINTF ("[%p] FLUSH => %s (parentRef = %d)\n",
- this, mustQueueResize ? "true" : "false", parentRef);
-
if (mustQueueResize) {
+ DBG_OBJ_MSG ("resize", 0, "<b>flush</b> (mustQueueResize set)");
+ DBG_OBJ_MSG_START ();
+
queueResize (-1, true);
mustQueueResize = false;
+
+ DBG_OBJ_MSG_END ();
}
}
@@ -2344,7 +2680,7 @@ void Textblock::changeLinkColor (int link, int newColor)
old_style->unref();
break;
}
- case core::Content::WIDGET:
+ case core::Content::WIDGET_IN_FLOW:
{ core::Widget *widget = word->content.widget;
styleAttrs = *widget->getStyle();
styleAttrs.color = core::style::Color::create (layout,
@@ -2396,4 +2732,315 @@ void Textblock::queueDrawRange (int index1, int index2)
}
}
+void Textblock::setVerticalOffset (int verticalOffset)
+{
+ if (this->verticalOffset != verticalOffset) {
+ this->verticalOffset = verticalOffset;
+ mustQueueResize = true;
+ queueDraw (); // Could perhaps be optimized.
+ }
+}
+
+/**
+ * Called by dw::OutOfFlowMgr when the border has changed due to a
+ * float (or some floats).
+ *
+ * "y", which given in widget coordinates, denotes the minimal
+ * position (when more than one float caused this), "vloat" the
+ * floating widget belonging to "y".
+ */
+void Textblock::borderChanged (int y, Widget *vloat)
+{
+ DBG_OBJ_MSGF ("resize", 0, "<b>borderChanged</b> (%d, %p)", y, vloat);
+ DBG_OBJ_MSG_START ();
+
+ int lineIndex = findLineIndex (y);
+ DBG_OBJ_MSGF ("resize", 1, "Line index: %d (of %d).",
+ lineIndex, lines->size ());
+
+ // Nothing to do at all, when lineIndex >= lines->size (),
+ // i. e. the change is below the bottom of this widget.
+ if (lineIndex < lines->size ()) {
+ int wrapLineIndex;
+ if (lineIndex < 0)
+ // Rewrap all.
+ wrapLineIndex = 0;
+ else
+ wrapLineIndex = lineIndex;
+
+ DBG_OBJ_MSGF ("resize", 1, "Rewrapping from line %d (of %d).",
+ wrapLineIndex, lines->size ());
+
+ if (vloat->getGenerator() == this) {
+ bool found = false;
+ // Sometimes, the respective word is not yet part of a
+ // line. Nothing to do, but because of the assertion below
+ // (and also for performace reasons) this should be
+ // considered. TODO: Integrate this below.
+ for (int wordIndex =
+ lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0;
+ !found && wordIndex < words->size(); wordIndex++) {
+ Word *word = words->getRef (wordIndex);
+ if (word->content.type == core::Content::WIDGET_OOF_REF &&
+ word->content.widget == vloat)
+ found = true;
+ }
+
+ // We search for the line of the float reference. There are
+ // two cases when this is not the line corresponsing to y:
+ //
+ // (1) When the float was moved down, due to collisions with
+ // other floats: in this case, the line number gets
+ // smaller (since the float reference is before).
+ //
+ // (2) In some cases, the line number may become larger, due
+ // to the per-line optimization of the words: initially,
+ // lines->size() - 1 is assigned, but it may happen that
+ // the float reference is put into another line.
+ //
+ // Only in the first case, a correction is neccessary, but a
+ // test for the second case is useful. (TODO: I've forgotten
+ // why a correction is neccessary.)
+ //
+ // Searched is done in the following order:
+ //
+ // - wrapLineIndex,
+ // - wrapLineIndex - 1,
+ // - wrapLineIndex + 1,
+ // - wrapLineIndex - 2,
+ // - wrapLineIndex + 2,
+ //
+ // etc. until either the float reference has been found or
+ // all lines have been searched (the latter triggers an
+ // abortion).
+
+ bool exceedsBeginning = false, exceedsEnd = false;
+ for (int i = 0; !found; i++) {
+ bool exceeds;
+ int lineIndex2;
+ if (i % 2 == 0) {
+ // even: +0, +1, +2, ...
+ lineIndex2 = wrapLineIndex + i / 2;
+ if (i > 0)
+ exceeds = exceedsEnd = lineIndex2 >= lines->size ();
+ else
+ exceeds = exceedsEnd = false;
+ } else {
+ // odd: -1, -2, ...
+ lineIndex2 = wrapLineIndex - (i + 1) / 2;
+ exceeds = exceedsBeginning = lineIndex2 < 0;
+ }
+
+ if (exceedsBeginning && exceedsEnd)
+ break;
+
+ if (!exceeds) {
+ Line *line = lines->getRef (lineIndex2);
+ for (int wordIndex = line->firstWord;
+ !found && wordIndex <= line->lastWord; wordIndex++) {
+ Word *word = words->getRef (wordIndex);
+ if (word->content.type == core::Content::WIDGET_OOF_REF &&
+ word->content.widget == vloat) {
+ found = true;
+ // Correct only by smaller values (case (1) above):
+ wrapLineIndex = misc::min (wrapLineIndex, lineIndex2);
+ }
+ }
+ }
+ }
+
+ if (!found)
+ printBorderChangedErrorAndAbort (y, vloat, wrapLineIndex);
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "Corrected to line %d.", wrapLineIndex);
+
+ queueResize (OutOfFlowMgr::createRefNormalFlow (wrapLineIndex), true);
+
+ // Notice that the line no. wrapLineIndex may not exist yet.
+ if (wrapLineIndex == 0)
+ lastWordDrawn = misc::min (lastWordDrawn, -1);
+ else
+ lastWordDrawn = misc::min (lastWordDrawn,
+ lines->getRef(wrapLineIndex - 1)->lastWord);
+ }
+
+ DBG_OBJ_MSG_END ();
+}
+
+void Textblock::printBorderChangedErrorAndAbort (int y, Widget *vloat,
+ int wrapLineIndex)
+{
+ // TODO Should be adjusted to recent changes in borderChanged().
+
+ printf ("*** This call failed! ***\n");
+ printf ("[%p] borderChanged (%d, %p (%s))\n",
+ this, y, vloat, vloat->getClassName());
+ printf (" Initial wrapLineIndex = line %d (of %d lines). "
+ "Has to be corrected.\n", wrapLineIndex, lines->size ());
+
+ printf (" search non-existing line (from %d to %d):\n",
+ lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0,
+ words->size() - 1);
+ for (int wordIndex =
+ lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0;
+ wordIndex < words->size(); wordIndex++) {
+ printf (" word %d: ", wordIndex);
+ printWordShort (words->getRef (wordIndex));
+ printf ("\n");
+ }
+
+ for (int lineIndex2 = wrapLineIndex; lineIndex2 >= 0;
+ lineIndex2--) {
+ Line *line = lines->getRef (lineIndex2);
+ printf (" searching existing line %d (from %d to %d):\n",
+ lineIndex2, line->firstWord, line->lastWord);
+ for (int wordIndex = line->firstWord;
+ wordIndex <= line->lastWord; wordIndex++) {
+ printf (" word %d: ", wordIndex);
+ printWordShort (words->getRef (wordIndex));
+ printf ("\n");
+ }
+ }
+
+ bool found2 = false;
+ for (int lineIndex2 = wrapLineIndex + 1;
+ !found2 && lineIndex2 < lines->size (); lineIndex2++) {
+ Line *line = lines->getRef (lineIndex2);
+ printf (" could have searched existing line %d "
+ "(from %d to %d):\n",
+ lineIndex2, line->firstWord, line->lastWord);
+ for (int wordIndex = line->firstWord;
+ !found2 && wordIndex <= line->lastWord; wordIndex++) {
+ Word *word = words->getRef (wordIndex);
+ if (word->content.type == core::Content::WIDGET_OOF_REF &&
+ word->content.widget == vloat) {
+ found2 = true;
+ printf (" word %d: ", wordIndex);
+ printWordShort (word);
+ printf ("\n");
+ }
+ }
+ }
+
+ lout::misc::assertNotReached ();
+}
+
+Textblock *Textblock::getTextblockForLine (Line *line)
+{
+ return getTextblockForLine (line->firstWord, line->lastWord);
+}
+
+Textblock *Textblock::getTextblockForLine (int lineNo)
+{
+ int firstWord = lineNo == 0 ? 0 :lines->getRef(lineNo - 1)->lastWord + 1;
+ int lastWord = lineNo < lines->size() ?
+ lines->getRef(lineNo)->lastWord : words->size() - 1;
+ return getTextblockForLine (firstWord, lastWord);
+}
+
+Textblock *Textblock::getTextblockForLine (int firstWord, int lastWord)
+{
+ if (firstWord < words->size ()) {
+ //printf ("[%p] GET_TEXTBLOCK_FOR_LINE (%d, %d)\n",
+ // this, firstWord, lastWord);
+
+ // A textblock is always between two line breaks, and so the
+ // first word of the line.
+ Word *word = words->getRef (firstWord);
+
+ if (word->content.type == core::Content::WIDGET_IN_FLOW &&
+ word->content.widget->instanceOf (Textblock::CLASS_ID)) {
+ //printf (" word %d: ", firstWord);
+ //printWordShort (word);
+ //printf ("\n");
+
+ return (Textblock*)word->content.widget;
+ }
+ }
+
+ //printf (" nothing\n");
+ return NULL;
+}
+
+/**
+ * Includes margin, border, and padding.
+ */
+int Textblock::yOffsetOfPossiblyMissingLine (int lineNo)
+{
+ DBG_OBJ_MSGF ("line.yoffset", 0,
+ "<b>yOffsetOfPossiblyMissingLine</b> (%d <i>of %d</i>)",
+ lineNo, lines->size());
+ DBG_OBJ_MSG_START ();
+
+ int result;
+
+ if (lineNo == 0) {
+ result = verticalOffset + getStyle()->boxOffsetY();
+ DBG_OBJ_MSGF ("line.yoffset", 1, "first line: %d + %d = %d",
+ verticalOffset, getStyle()->boxOffsetY(), result);
+ } else {
+ Line *prevLine = lines->getRef (lineNo - 1);
+ result = verticalOffset + getStyle()->boxOffsetY() +
+ prevLine->top + prevLine->boxAscent + prevLine->boxDescent +
+ prevLine->breakSpace;
+ DBG_OBJ_MSGF ("line.yoffset", 1,
+ "other line: %d + %d + %d + (%d + %d) + %d = %d",
+ verticalOffset, getStyle()->boxOffsetY(),
+ prevLine->top, prevLine->boxAscent, prevLine->boxDescent,
+ prevLine->breakSpace, result);
+ }
+
+ DBG_OBJ_MSG_END ();
+
+ return result;
+}
+
+int Textblock::heightOfPossiblyMissingLine (int lineNo)
+{
+ DBG_OBJ_MSGF ("line.height", 0,
+ "<b>heightOfPossiblyMissingLine</b> (%d <i>of %d</i>)",
+ lineNo, lines->size());
+ DBG_OBJ_MSG_START ();
+
+ int result;
+
+ if (lineNo < lines->size()) {
+ // An existing line.
+
+ Line *line = lines->getRef (lineNo);
+
+ // This is sometimes called within addLine, so that the
+ // condition above is true, but line->boxAscent and
+ // line->boxDescent are not set appropriately. We have to
+ // accumulate the heights then.
+
+ if (line->finished) {
+ DBG_OBJ_MSGF ("line.height", 1,
+ "exists and is finished; height = %d + %d = %d",
+ line->boxAscent, line->boxDescent,
+ line->boxAscent + line->boxDescent);
+ result = line->boxAscent + line->boxDescent;
+ } else {
+ DBG_OBJ_MSG ("line.height", 1, "exist but is not finished");
+ result = misc::max (1, newLineAscent + newLineDescent);
+ }
+ } else if (lineNo == lines->size()) {
+ // The line to be constructed: some words exist, but not the
+ // line. Accumulate the word heights.
+
+ // Old comment: Furthermore, this is in some cases incomplete:
+ // see doc/dw-out-of-flow.doc. -- Still the case?
+
+ DBG_OBJ_MSG ("line.height", 1, "does not exist");
+ result = misc::max (1, newLineAscent + newLineDescent);
+ } else
+ result = 1;
+
+ DBG_OBJ_MSGF ("line.height", 0, "result = %d", result);
+ DBG_OBJ_MSG_END ();
+
+ return result;
+}
+
} // namespace dw
diff --git a/dw/textblock.hh b/dw/textblock.hh
index b85937ba..f792b779 100644
--- a/dw/textblock.hh
+++ b/dw/textblock.hh
@@ -4,13 +4,18 @@
#include <limits.h>
#include "core.hh"
+#include "outofflowmgr.hh"
#include "../lout/misc.hh"
-// These were used when improved line breaking and hyphenation were
-// implemented. Should be cleaned up; perhaps reactivate RTFL again.
+// These were used when improved line breaking and hyphenation were implemented.
+// Should be, bit by bit, replaced by RTFL (see ../lout/debug.hh).
#define PRINTF(fmt, ...)
#define PUTCHAR(ch)
+#ifdef DBG_RTFL
+# define DEBUG
+#endif
+
namespace dw {
/**
@@ -18,10 +23,11 @@ namespace dw {
* of paragraphs.
*
* <div style="border: 2px solid #ffff00; margin-top: 0.5em;
- * margin-bottom: 0.5em; padding: 0.5em 1em;
- * background-color: #ffffe0"><b>Info:</b> The recent changes (line
- * breaking and hyphenation) have not yet been incorporated into this
- * documentation. See \ref dw-line-breaking.</div>
+ * margin-bottom: 0.5em; padding: 0.5em 1em; background-color:
+ * #ffffe0"><b>Info:</b> The recent changes (line breaking and
+ * hyphenation on one hand, floats on the other hand) have not yet
+ * been incorporated into this documentation. See \ref
+ * dw-line-breaking and \ref dw-out-of-flow.</div>
*
* <h3>Signals</h3>
*
@@ -47,9 +53,9 @@ namespace dw {
*
* \image html dw-textblock-collapsing-spaces-1-1.png
*
- * are combined like this:
+ * are combined like this:
*
- * \image html dw-textblock-collapsing-spaces-1-2.png
+ * \image html dw-textblock-collapsing-spaces-1-2.png
*
* <li> a) If one paragraph is the first paragraph within another, the upper
* space of these paragraphs collapse. b) The analogue is the case for the
@@ -73,9 +79,9 @@ namespace dw {
* automatically. See the code of dw::Textblock::addParBreak.
*
* <li> To collapse spaces according to rule 2b,
- * dw::Textblock::addParBreak::handOverBreak must be called for
- * the \em inner widget. The HTML parser does this in
- * Html_eventually_pop_dw.
+ * dw::Textblock::addParBreak::handOverBreak must be called for
+ * the \em inner widget. The HTML parser does this in
+ * Html_eventually_pop_dw.
* </ul>
*
*
@@ -218,6 +224,7 @@ private:
bool lineCanBeBroken (int penaltyIndex);
int compareTo (int penaltyIndex, BadnessAndPenalty *other);
+ void intoStringBuffer(lout::misc::StringBuffer *sb);
void print ();
};
@@ -236,6 +243,9 @@ private:
static const char *hyphenDrawChar;
+ Textblock *containingBlock;
+ OutOfFlowMgr *outOfFlowMgr;
+
protected:
/**
* \brief Implementation used for words.
@@ -308,10 +318,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 +344,20 @@ 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;
+
+ /* The word index of the last OOF reference (most importantly:
+ * float) whic is positioned before this line, or -1, if there
+ * is no OOF reference positioned before.
+ *
+ * **Important:** These references may still be part of this or
+ * even a following line, when positioned before (this is the
+ * reason this attribute exists); see \ref dw-out-of-flow. */
+ int lastOofRefPositionedBeforeThisLine;
};
struct Word
@@ -409,13 +444,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 +461,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 +516,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,16 +553,19 @@ 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);
- Line *addLine (int firstWord, int lastWord, bool temporary);
+ Line *addLine (int firstWord, int lastWord, int newLastOofPos,
+ bool temporary);
void calcWidgetSize (core::Widget *widget, core::Requisition *size);
void rewrap ();
void fillParagraphs ();
+ void initNewLine ();
+ void calcBorders (int lastOofRef, int height);
void showMissingLines ();
void removeTemporaryLines ();
+ void setVerticalOffset (int verticalOffset);
void decorateText (core::View *view, core::style::Style *style,
core::style::Color::Shading shading,
@@ -520,13 +584,18 @@ 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);
Word *addWord (int width, int ascent, int descent, short flags,
core::style::Style *style);
+ void breakAdded ();
void initWord (int wordNo);
+ void cleanupWord (int wordNo);
void removeWordImgRenderer (int wordNo);
void setWordImgRenderer (int wordNo);
void removeSpaceImgRenderer (int wordNo);
@@ -543,25 +612,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 +642,7 @@ protected:
inline int lineYOffsetCanvasAllocation (Line *line,
core::Allocation *allocation)
{
- return allocation->y + lineYOffsetWidgetAllocation(line, allocation);
+ return allocation->y + lineYOffsetWidgetAllocation (line, allocation);
}
/**
@@ -597,6 +658,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));
@@ -606,12 +674,26 @@ protected:
{
if (lines->size() == 0)
return 0;
- else
- return
- (words->getRef(lines->getLastRef()->lastWord)->flags &
- (Word::DIV_CHAR_AT_EOL | Word::PERM_DIV_CHAR)) ? 1 : 0;
+ else {
+ Line *line = lines->getLastRef();
+ if (line->firstWord <= line->lastWord)
+ return
+ (words->getRef(line->lastWord)->flags &
+ (Word::DIV_CHAR_AT_EOL | Word::PERM_DIV_CHAR)) ? 1 : 0;
+ else
+ // empty line
+ return 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 +701,25 @@ protected:
int *maxOfMinWidth, int *sumOfMaxWidth);
void processWord (int wordIndex);
virtual bool wordWrap (int wordIndex, bool wrapAll);
+ bool wrapWordInFlow (int wordIndex, bool wrapAll);
+ void balanceBreakPosAndHeight (int wordIndex, int firstIndex,
+ int *searchUntil, bool tempNewLine,
+ int penaltyIndex, bool borderIsCalculated,
+ bool *thereWillBeMoreSpace, bool wrapAll,
+ bool *wordListChanged, int *wordIndexEnd,
+ int lastFloatPos, bool regardBorder,
+ int *height, int *breakPos);
+ int searchBreakPos (int wordIndex, int firstIndex, int *searchUntil,
+ bool tempNewLine, int penaltyIndex,
+ bool thereWillBeMoreSpace, bool wrapAll,
+ bool *wordListChanged, int *wordIndexEnd);
int searchMinBap (int firstWord, int lastWordm, int penaltyIndex,
- bool correctAtEnd);
+ bool thereWillBeMoreSpace, bool correctAtEnd);
int considerHyphenation (int firstIndex, int breakPos);
bool isHyphenationCandidate (Word *word);
-
+ int calcLinePartHeight (int firstWord, int lastWord);
+
+
void handleWordExtremes (int wordIndex);
void correctLastWordExtremes ();
@@ -632,6 +728,7 @@ protected:
static int getLineShrinkability(Word *lastWord);
static int getLineStretchability(Word *lastWord);
int hyphenateWord (int wordIndex);
+ void moveWordIndices (int wordIndex, int num);
void accumulateWordForLine (int lineIndex, int wordIndex);
void accumulateWordData (int wordIndex);
int calcAvailWidth (int lineIndex);
@@ -645,6 +742,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 +763,7 @@ protected:
core::style::Style *style,
int numBreaks, int *breakPos,
core::Requisition *wordSize);
+ static bool isContainingBlock (Widget *widget);
public:
static int CLASS_ID;
@@ -699,8 +799,68 @@ 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 void oofSizeChanged (bool extremesChanged)
+ { queueResize (-1, extremesChanged); }
+ inline int getAvailWidth () { return availWidth; }
+ inline int getAvailAscent () { return availAscent; }
+ inline int getAvailDescent () { return availDescent; }
};
+#define DBG_SET_WORD(n) \
+ D_STMT_START { \
+ switch (words->getRef(n)->content.type) { \
+ case ::dw::core::Content::TEXT: \
+ DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "TEXT"); \
+ DBG_OBJ_ARRATTRSET_STR ("words", n, "text/widget/breakSpace", \
+ words->getRef(n)->content.text); \
+ break; \
+ case ::dw::core::Content::WIDGET_IN_FLOW: \
+ DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "WIDGET_IN_FLOW"); \
+ DBG_OBJ_ARRATTRSET_PTR ("words", n, "text/widget/breakSpace", \
+ words->getRef(n)->content.widget); \
+ break; \
+ case ::dw::core::Content::WIDGET_OOF_REF: \
+ DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "WIDGET_OOF_REF"); \
+ DBG_OBJ_ARRATTRSET_PTR ("words", n, "text/widget/breakSpace", \
+ words->getRef(n)->content.widget); \
+ break; \
+ case ::dw::core::Content::BREAK: \
+ DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "TEXT"); \
+ DBG_OBJ_ARRATTRSET_NUM ("words", n, "text/widget/breakSpace", \
+ words->getRef(n)->content.breakSpace); \
+ break; \
+ default: \
+ DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "???"); \
+ DBG_OBJ_ARRATTRSET_SYM ("words", n, "text/widget/breakSpace", "???"); \
+ } \
+ } D_STMT_END
+
+#define DBG_MSG_WORD(aspect, prio, prefix, n, suffix) \
+ D_STMT_START { \
+ switch (words->getRef(n)->content.type) { \
+ case ::dw::core::Content::TEXT: \
+ DBG_OBJ_MSGF (aspect, prio, prefix "TEXT / \"%s\"" suffix, \
+ words->getRef(n)->content.text); \
+ break; \
+ case ::dw::core::Content::WIDGET_IN_FLOW: \
+ DBG_OBJ_MSGF (aspect, prio, prefix "WIDGET_IN_FLOW / %p" suffix, \
+ words->getRef(n)->content.widget); \
+ break; \
+ case ::dw::core::Content::WIDGET_OOF_REF: \
+ DBG_OBJ_MSGF (aspect, prio, prefix "WIDGET_OOF_REF / %p" suffix, \
+ words->getRef(n)->content.widget); \
+ break; \
+ case ::dw::core::Content::BREAK: \
+ DBG_OBJ_MSGF (aspect, prio, prefix "BREAK / %d" suffix, \
+ words->getRef(n)->content.breakSpace); \
+ break; \
+ default: \
+ DBG_OBJ_MSG (aspect, prio, prefix "??? / ???"); \
+ } \
+ } D_STMT_END
+
} // namespace dw
#endif // __DW_TEXTBLOCK_HH__
diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc
index 7531ecd5..c26b7f6e 100644
--- a/dw/textblock_iterator.cc
+++ b/dw/textblock_iterator.cc
@@ -35,20 +35,34 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
bool atEnd):
core::Iterator (textblock, mask, atEnd)
{
- index = atEnd ? textblock->words->size () : -1;
+ if (atEnd) {
+ if (textblock->outOfFlowMgr) {
+ oofm = true;
+ index = textblock->outOfFlowMgr->getNumWidgets();
+ } else {
+ oofm = false;
+ index = textblock->words->size();
+ }
+ } else {
+ oofm = false;
+ index = -1;
+ }
+
content.type = atEnd ? core::Content::END : core::Content::START;
}
Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
core::Content::Type mask,
- int index):
+ bool oofm, int index):
core::Iterator (textblock, mask, false)
{
+ this->oofm = oofm;
this->index = index;
+ // TODO To be completely exact, oofm should be considered here.
if (index < 0)
content.type = core::Content::START;
- else if (index >= textblock->words->size ())
+ else if (index >= textblock->words->size())
content.type = core::Content::END;
else
content = textblock->words->getRef(index)->content;
@@ -56,12 +70,20 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
object::Object *Textblock::TextblockIterator::clone()
{
- return new TextblockIterator ((Textblock*)getWidget(), getMask(), index);
+ return
+ new TextblockIterator ((Textblock*)getWidget(), getMask(), oofm, index);
}
int Textblock::TextblockIterator::compareTo(object::Comparable *other)
{
- return index - ((TextblockIterator*)other)->index;
+ TextblockIterator *otherTI = (TextblockIterator*)other;
+
+ if (oofm && !otherTI->oofm)
+ return +1;
+ else if (!oofm && otherTI->oofm)
+ return -1;
+ else
+ return index - otherTI->index;
}
bool Textblock::TextblockIterator::next ()
@@ -71,15 +93,52 @@ bool Textblock::TextblockIterator::next ()
if (content.type == core::Content::END)
return false;
+ short type;
+
do {
index++;
- if (index >= textblock->words->size ()) {
- content.type = core::Content::END;
- return false;
+
+ if (oofm) {
+ // Iterating over OOFM.
+ if (index >= textblock->outOfFlowMgr->getNumWidgets()) {
+ // End of OOFM list reached.
+ content.type = core::Content::END;
+ return false;
+ }
+ type = core::Content::WIDGET_OOF_CONT;
+ } else {
+ // Iterating over words list.
+ if (index < textblock->words->size ())
+ // Still words left.
+ type = textblock->words->getRef(index)->content.type;
+ else {
+ // End of words list reached.
+ if (textblock->outOfFlowMgr) {
+ oofm = true;
+ index = 0;
+ if (textblock->outOfFlowMgr->getNumWidgets() > 0)
+ // Start with OOFM widgets.
+ type = core::Content::WIDGET_OOF_CONT;
+ else {
+ // No OOFM widgets (number is 0).
+ content.type = core::Content::END;
+ return false;
+ }
+ } else {
+ // No OOFM widgets (no OOFM agt all).
+ content.type = core::Content::END;
+ return false;
+ }
+ }
}
- } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
+ } while ((type & getMask()) == 0);
+
+ if (oofm) {
+ content.type = core::Content::WIDGET_OOF_CONT;
+ content.widget = textblock->outOfFlowMgr->getWidget (index);
+ } else
+ content = textblock->words->getRef(index)->content;
- content = textblock->words->getRef(index)->content;
return true;
}
@@ -90,132 +149,188 @@ bool Textblock::TextblockIterator::prev ()
if (content.type == core::Content::START)
return false;
+ short type;
+
do {
index--;
- if (index < 0) {
- content.type = core::Content::START;
- return false;
+
+ if (oofm) {
+ // Iterating over OOFM.
+ if (index >= 0)
+ // Still widgets left.
+ type = core::Content::WIDGET_OOF_CONT;
+ else {
+ // Beginning of OOFM list reached. Continue with words.
+ oofm = false;
+ index = textblock->words->size() - 1;
+ if (index < 0) {
+ // There are no words. (Actually, this case should not
+ // happen: When there are OOF widgets, ther must be OOF
+ // references, or widgets in flow, which contain
+ // references.
+ content.type = core::Content::END;
+ return false;
+ }
+ type = textblock->words->getRef(index)->content.type;
+ }
+ } else {
+ // Iterating over words list.
+ if (index < 0) {
+ // Beginning of words list reached.
+ content.type = core::Content::START;
+ return false;
+ }
+ type = textblock->words->getRef(index)->content.type;
}
- } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
+ } while ((type & getMask()) == 0);
+
+ if (oofm) {
+ content.type = core::Content::WIDGET_OOF_CONT;
+ content.type = false;
+ content.widget = textblock->outOfFlowMgr->getWidget (index);
+ } else
+ content = textblock->words->getRef(index)->content;
- content = textblock->words->getRef(index)->content;
return true;
}
void Textblock::TextblockIterator::highlight (int start, int end,
core::HighlightLayer layer)
{
- Textblock *textblock = (Textblock*)getWidget();
- int index1 = index, index2 = index;
-
- int oldStartIndex = textblock->hlStart[layer].index;
- int oldStartChar = textblock->hlStart[layer].nChar;
- int oldEndIndex = textblock->hlEnd[layer].index;
- int oldEndChar = textblock->hlEnd[layer].nChar;
-
- if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
- /* nothing is highlighted */
- textblock->hlStart[layer].index = index;
- textblock->hlEnd[layer].index = index;
- }
-
- if (textblock->hlStart[layer].index >= index) {
- index2 = textblock->hlStart[layer].index;
- textblock->hlStart[layer].index = index;
- textblock->hlStart[layer].nChar = start;
- }
-
- if (textblock->hlEnd[layer].index <= index) {
- index2 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index;
- textblock->hlEnd[layer].nChar = end;
+ if (!oofm) {
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ int oldStartIndex = textblock->hlStart[layer].index;
+ int oldStartChar = textblock->hlStart[layer].nChar;
+ int oldEndIndex = textblock->hlEnd[layer].index;
+ int oldEndChar = textblock->hlEnd[layer].nChar;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
+ /* nothing is highlighted */
+ textblock->hlStart[layer].index = index;
+ textblock->hlEnd[layer].index = index;
+ }
+
+ if (textblock->hlStart[layer].index >= index) {
+ index2 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index;
+ textblock->hlStart[layer].nChar = start;
+ }
+
+ if (textblock->hlEnd[layer].index <= index) {
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index;
+ textblock->hlEnd[layer].nChar = end;
+ }
+
+ if (oldStartIndex != textblock->hlStart[layer].index ||
+ oldStartChar != textblock->hlStart[layer].nChar ||
+ oldEndIndex != textblock->hlEnd[layer].index ||
+ oldEndChar != textblock->hlEnd[layer].nChar)
+ textblock->queueDrawRange (index1, index2);
}
- if (oldStartIndex != textblock->hlStart[layer].index ||
- oldStartChar != textblock->hlStart[layer].nChar ||
- oldEndIndex != textblock->hlEnd[layer].index ||
- oldEndChar != textblock->hlEnd[layer].nChar)
- textblock->queueDrawRange (index1, index2);
+ // TODO What about OOF widgets?
}
void Textblock::TextblockIterator::unhighlight (int direction,
core::HighlightLayer layer)
{
- Textblock *textblock = (Textblock*)getWidget();
- int index1 = index, index2 = index;
-
- if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
- return;
-
- int oldStartIndex = textblock->hlStart[layer].index;
- int oldStartChar = textblock->hlStart[layer].nChar;
- int oldEndIndex = textblock->hlEnd[layer].index;
- int oldEndChar = textblock->hlEnd[layer].nChar;
-
- if (direction == 0) {
- index1 = textblock->hlStart[layer].index;
- index2 = textblock->hlEnd[layer].index;
- textblock->hlStart[layer].index = 1;
- textblock->hlEnd[layer].index = 0;
- } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
- index1 = textblock->hlStart[layer].index;
- textblock->hlStart[layer].index = index + 1;
- textblock->hlStart[layer].nChar = 0;
- } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
- index1 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index - 1;
- textblock->hlEnd[layer].nChar = INT_MAX;
+ if (!oofm) {
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
+ return;
+
+ int oldStartIndex = textblock->hlStart[layer].index;
+ int oldStartChar = textblock->hlStart[layer].nChar;
+ int oldEndIndex = textblock->hlEnd[layer].index;
+ int oldEndChar = textblock->hlEnd[layer].nChar;
+
+ if (direction == 0) {
+ index1 = textblock->hlStart[layer].index;
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlStart[layer].index = 1;
+ textblock->hlEnd[layer].index = 0;
+ } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
+ index1 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index + 1;
+ textblock->hlStart[layer].nChar = 0;
+ } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
+ index1 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index - 1;
+ textblock->hlEnd[layer].nChar = INT_MAX;
+ }
+
+ if (oldStartIndex != textblock->hlStart[layer].index ||
+ oldStartChar != textblock->hlStart[layer].nChar ||
+ oldEndIndex != textblock->hlEnd[layer].index ||
+ oldEndChar != textblock->hlEnd[layer].nChar)
+ textblock->queueDrawRange (index1, index2);
}
- if (oldStartIndex != textblock->hlStart[layer].index ||
- oldStartChar != textblock->hlStart[layer].nChar ||
- oldEndIndex != textblock->hlEnd[layer].index ||
- oldEndChar != textblock->hlEnd[layer].nChar)
- textblock->queueDrawRange (index1, index2);
+ // TODO What about OOF widgets?
}
void Textblock::TextblockIterator::getAllocation (int start, int end,
core::Allocation *allocation)
{
Textblock *textblock = (Textblock*)getWidget();
- int lineIndex = textblock->findLineOfWord (index);
- Line *line = textblock->lines->getRef (lineIndex);
- Word *word = textblock->words->getRef (index);
-
- allocation->x =
- textblock->allocation.x + textblock->lineXOffsetWidget (line);
- for (int i = line->firstWord; i < index; i++) {
- Word *w = textblock->words->getRef(i);
- allocation->x += w->size.width + w->effSpace;
- }
- if (start > 0 && word->content.type == core::Content::TEXT) {
- allocation->x += textblock->textWidth (word->content.text, 0, start,
- word->style,
- word->flags & Word::WORD_START,
- (word->flags & Word::WORD_END)
- && word->content.text[start] == 0);
- }
- allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
- word->size.ascent;
-
- allocation->width = word->size.width;
- if (word->content.type == core::Content::TEXT) {
- int wordEnd = strlen(word->content.text);
-
- if (start > 0 || end < wordEnd) {
- end = misc::min(end, wordEnd); /* end could be INT_MAX */
- allocation->width =
- textblock->textWidth (word->content.text, start, end - start,
- word->style,
- (word->flags & Word::WORD_START)
- && start == 0,
- (word->flags & Word::WORD_END)
- && word->content.text[end] == 0);
+ if (oofm) {
+ // TODO Consider start and end?
+ *allocation =
+ *(textblock->outOfFlowMgr->getWidget(index)->getAllocation());
+ } else {
+ int lineIndex = textblock->findLineOfWord (index);
+ Line *line = textblock->lines->getRef (lineIndex);
+ Word *word = textblock->words->getRef (index);
+
+ allocation->x =
+ textblock->allocation.x + line->offsetCompleteWidget;
+
+ for (int i = line->firstWord; i < index; i++) {
+ Word *w = textblock->words->getRef(i);
+ allocation->x += w->size.width + w->effSpace;
+ }
+ if (start > 0 && word->content.type == core::Content::TEXT) {
+ allocation->x += textblock->textWidth (word->content.text, 0, start,
+ word->style,
+ word->flags & Word::WORD_START,
+ (word->flags & Word::WORD_END)
+ && word->content.text[start]
+ == 0);
+ }
+ allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
+ word->size.ascent;
+
+ allocation->width = word->size.width;
+ if (word->content.type == core::Content::TEXT) {
+ int wordEnd = strlen(word->content.text);
+
+ if (start > 0 || end < wordEnd) {
+ end = misc::min(end, wordEnd); /* end could be INT_MAX */
+ allocation->width =
+ textblock->textWidth (word->content.text, start, end - start,
+ word->style,
+ (word->flags & Word::WORD_START)
+ && start == 0,
+ (word->flags & Word::WORD_END)
+ && word->content.text[end] == 0);
+ }
}
+ allocation->ascent = word->size.ascent;
+ allocation->descent = word->size.descent;
}
- allocation->ascent = word->size.ascent;
- allocation->descent = word->size.descent;
+}
+
+void Textblock::TextblockIterator::print ()
+{
+ Iterator::print ();
+ printf (", oofm = %s, index = %d", oofm ? "true" : "false", index);
+
}
} // namespace dw
diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc
index 32e400fa..c5143dcf 100644
--- a/dw/textblock_linebreaking.cc
+++ b/dw/textblock_linebreaking.cc
@@ -23,6 +23,7 @@
#include "textblock.hh"
#include "hyphenator.hh"
#include "../lout/msg.h"
+#include "../lout/debug.hh"
#include "../lout/misc.hh"
#include <stdio.h>
@@ -192,61 +193,63 @@ int Textblock::BadnessAndPenalty::compareTo (int penaltyIndex,
void Textblock::BadnessAndPenalty::print ()
{
+ misc::StringBuffer sb;
+ intoStringBuffer(&sb);
+ printf ("%s", sb.getChars ());
+}
+
+void Textblock::BadnessAndPenalty::intoStringBuffer(misc::StringBuffer *sb)
+{
switch (badnessState) {
case NOT_STRETCHABLE:
- printf ("not stretchable");
+ sb->append ("not stretchable");
break;
case TOO_TIGHT:
- printf ("too tight");
+ sb->append ("too tight");
break;
case QUITE_LOOSE:
- printf ("quite loose (ratio = %d)", ratio);
+ sb->append ("quite loose (ratio = ");
+ sb->appendInt (ratio);
+ sb->append (")");
break;
case BADNESS_VALUE:
- printf ("%d", badness);
+ sb->appendInt (badness);
break;
}
#ifdef DEBUG
- printf (" [%d + %d - %d vs. %d]",
- totalWidth, totalStretchability, totalShrinkability, idealWidth);
+ sb->append (" [");
+ sb->appendInt (totalWidth);
+ sb->append (" + ");
+ sb->appendInt (totalStretchability);
+ sb->append (" - ");
+ sb->appendInt (totalShrinkability);
+ sb->append (" vs. ");
+ sb->appendInt (idealWidth);
+ sb->append ("]");
#endif
- printf (" + (");
+ sb->append (" + (");
for (int i = 0; i < 2; i++) {
if (penalty[i] == INT_MIN)
- printf ("-inf");
+ sb->append ("-inf");
else if (penalty[i] == INT_MAX)
- printf ("inf");
+ sb->append ("inf");
else
- printf ("%d", penalty[i]);
+ sb->appendInt (penalty[i]);
if (i == 0)
- printf (", ");
+ sb->append (", ");
}
- printf (")");
+ sb->append (")");
}
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)
@@ -287,8 +290,11 @@ void Textblock::printWord (Word *word)
*/
void Textblock::justifyLine (Line *line, int diff)
{
- /* To avoid rounding errors, the calculation is based on accumulated
- * values. */
+ DBG_OBJ_MSGF ("construct.line", 0, "<b>justifyLine</b> (..., %d)", diff);
+ DBG_OBJ_MSG_START ();
+
+ // To avoid rounding errors, the calculation is based on accumulated
+ // values. See doc/rounding-errors.doc.
if (diff > 0) {
int spaceStretchabilitySum = 0;
@@ -306,8 +312,8 @@ void Textblock::justifyLine (Line *line, int diff)
- spaceDiffCum;
spaceDiffCum += spaceDiff;
- PRINTF (" %d (of %d): diff = %d\n", i, words->size (),
- spaceDiff);
+ DBG_OBJ_MSGF ("construct.line", 1, "%d (of %d): diff = %d",
+ i, words->size (), spaceDiff);
word->effSpace = word->origSpace + spaceDiff;
}
@@ -328,38 +334,57 @@ void Textblock::justifyLine (Line *line, int diff)
- spaceDiffCum;
spaceDiffCum += spaceDiff;
+ DBG_OBJ_MSGF ("construct.line", 1, "%d (of %d): diff = %d",
+ i, words->size (), spaceDiff);
+
word->effSpace = word->origSpace + spaceDiff;
}
}
}
+
+ DBG_OBJ_MSG_END ();
}
Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
- bool temporary)
+ int newLastOofPos, bool temporary)
{
- PRINTF ("[%p] ADD_LINE (%d, %d) => %d\n",
- this, firstWord, lastWord, lines->size ());
+ DBG_OBJ_MSGF ("construct.line", 0, "<b>addLine</b> (%d, %d) => %d",
+ firstWord, lastWord, lines->size ());
+ DBG_OBJ_MSG_START ();
+
+ int lineWidth;
+ if (lastWord >= firstWord) {
+ DBG_MSG_WORD ("construct.line", 1, "<i>first word:</i> ", firstWord, "");
+ DBG_MSG_WORD ("construct.line", 1, "<i>last word:</i> ", lastWord, "");
+
+ Word *lastWordOfLine = words->getRef(lastWord);
+ // Word::totalWidth includes the hyphen (which is what we want here).
+ lineWidth = lastWordOfLine->totalWidth;
+ DBG_OBJ_MSGF ("construct.line", 1, "lineWidth (from last word): %d",
+ lineWidth);
+ } else {
+ // empty line
+ lineWidth = 0;
+ DBG_OBJ_MSGF ("construct.line", 1, "lineWidth (empty line): %d",
+ lineWidth);
+ }
- Word *lastWordOfLine = words->getRef(lastWord);
- // Word::totalWidth includes the hyphen (which is what we want here).
- int lineWidth = lastWordOfLine->totalWidth;
// "lineWidth" is relative to leftOffset, so we may have to add
// "line1OffsetEff" (remember: this is, for list items, negative).
- if (lines->size () == 0)
+ if (lines->size () == 0) {
lineWidth += line1OffsetEff;
+ DBG_OBJ_MSGF ("construct.line", 1, "lineWidth (line1OffsetEff): %d",
+ lineWidth);
+ }
int maxOfMinWidth, sumOfMaxWidth;
accumulateWordExtremes (firstWord, lastWord, &maxOfMinWidth,
&sumOfMaxWidth);
- PRINTF (" words[%d]->totalWidth = %d\n", lastWord,
- lastWordOfLine->totalWidth);
-
- PRINTF ("[%p] ##### LINE ADDED: %d, from %d to %d #####\n",
- this, lines->size (), firstWord, lastWord);
-
lines->increase ();
+ DBG_OBJ_SET_NUM ("lines.size", lines->size ());
+
if(!temporary) {
// If the last line was temporary, this will be temporary, too, even
// if not requested.
@@ -379,38 +404,59 @@ 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++) {
Word *word = words->getRef (i);
lineWidth += (word->effSpace - word->origSpace);
+ DBG_OBJ_MSGF ("construct.line", 1,
+ "lineWidth [corrected space (%d - %d) after word %d]: %d",
+ word->effSpace, word->origSpace, i, lineWidth);
}
if (lines->size () == 1) {
// first line
line->top = 0;
line->maxLineWidth = lineWidth;
+ line->lastOofRefPositionedBeforeThisLine = -1;
} else {
Line *prevLine = lines->getRef (lines->size () - 2);
line->top = prevLine->top + prevLine->boxAscent +
prevLine->boxDescent + prevLine->breakSpace;
line->maxLineWidth = misc::max (lineWidth, prevLine->maxLineWidth);
+ line->lastOofRefPositionedBeforeThisLine =
+ prevLine->lastOofRefPositionedBeforeThisLine;
}
for(int i = line->firstWord; i <= line->lastWord; i++)
accumulateWordForLine (lineIndex, i);
- PRINTF (" line[%d].top = %d\n", lines->size () - 1, line->top);
- PRINTF (" line[%d].boxAscent = %d\n", lines->size () - 1, line->boxAscent);
- PRINTF (" line[%d].boxDescent = %d\n",
- lines->size () - 1, line->boxDescent);
- PRINTF (" line[%d].contentAscent = %d\n", lines->size () - 1,
- line->contentAscent);
- PRINTF (" line[%d].contentDescent = %d\n",
- lines->size () - 1, line->contentDescent);
-
- PRINTF (" line[%d].maxLineWidth = %d\n",
- lines->size () - 1, line->maxLineWidth);
+ // Especially empty lines (possible when there are floats) have
+ // zero height, which may cause endless loops. For this reasons,
+ // the height should be positive.
+ line->boxAscent = misc::max (line->boxAscent, 1);
+
+ // Calculate offsetCompleteWidget, which includes also floats.
+ int leftBorder = mustBorderBeRegarded (line) ? newLineLeftBorder : 0;
+ line->offsetCompleteWidget =
+ misc::max (leftBorder,
+ getStyle()->boxOffsetX() + innerPadding
+ + (lineIndex == 0 ? line1OffsetEff : 0))
+ + line->leftOffset;
+
+ DBG_OBJ_MSGF ("construct.line", 1, "top = %d\n", line->top);
+ DBG_OBJ_MSGF ("construct.line", 1, "boxAscent = %d\n", line->boxAscent);
+ DBG_OBJ_MSGF ("construct.line", 1, "boxDescent = %d\n", line->boxDescent);
+ DBG_OBJ_MSGF ("construct.line", 1, "contentAscent = %d\n",
+ line->contentAscent);
+ DBG_OBJ_MSGF ("construct.line", 1, "contentDescent = %d\n",
+ line->contentDescent);
+ DBG_OBJ_MSGF ("construct.line", 1, "maxLineWidth = %d (lineWidth = %d)\n",
+ line->maxLineWidth, lineWidth);
+ DBG_OBJ_MSGF ("construct.line", 1, "offsetCompleteWidget = %d\n",
+ line->offsetCompleteWidget);
mustQueueResize = true;
@@ -421,7 +467,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 +477,15 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
xWidget += word->size.width + word->effSpace;
}
+ line->finished = true;
+ line->lastOofRefPositionedBeforeThisLine =
+ misc::max (line->lastOofRefPositionedBeforeThisLine, newLastOofPos);
+ DBG_OBJ_SET_NUM ("lastLine.lastOofRefPositionedBeforeThisLine",
+ line->lastOofRefPositionedBeforeThisLine);
+
+ initNewLine ();
+
+ DBG_OBJ_MSG_END ();
return line;
}
@@ -507,18 +562,18 @@ void Textblock::processWord (int wordIndex)
*/
bool Textblock::wordWrap (int wordIndex, bool wrapAll)
{
- PRINTF ("[%p] WORD_WRAP (%d, %s)\n",
- this, wordIndex, wrapAll ? "true" : "false");
+ DBG_OBJ_MSGF ("construct.word", 0, "<b>wordWrap</b> (%d, %s)",
+ wordIndex, wrapAll ? "true" : "false");
+ DBG_OBJ_MSG_START ();
- Word *word;
- bool wordListChanged = false;
+ DBG_MSG_WORD ("construct.word", 1, "<i>wrapped word:</i> ", wordIndex, "");
if (!wrapAll)
removeTemporaryLines ();
initLine1Offset (wordIndex);
- word = words->getRef (wordIndex);
+ Word *word = words->getRef (wordIndex);
word->effSpace = word->origSpace;
accumulateWordData (wordIndex);
@@ -527,43 +582,122 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll)
//printWord (word);
//printf ("\n");
+ bool b;
+ if (word->content.type == core::Content::WIDGET_OOF_REF)
+ b = false;
+ else
+ b = wrapWordInFlow (wordIndex, wrapAll);
+
+ DBG_OBJ_MSG_END ();
+
+ return b;
+}
+
+bool Textblock::wrapWordInFlow (int wordIndex, bool wrapAll)
+{
+ DBG_OBJ_MSGF ("construct.word", 0, "<b>wrapWordInFlow</b> (%d, %s)",
+ wordIndex, wrapAll ? "true" : "false");
+ DBG_OBJ_MSG_START ();
+
+ Word *word = words->getRef (wordIndex);
+ bool wordListChanged = false;
+
int penaltyIndex = calcPenaltyIndexForNewLine ();
bool newLine;
do {
+ // This variable, thereWillBeMoreSpace, is set to true, if, due
+ // to floats, this line is smaller than following lines will be
+ // (and, at the end, there will be surely lines without
+ // floats). If this is the case, lines may, in an extreme case,
+ // be left empty.
+
+ // (In other cases, lines are never left empty, even if this means
+ // that the contents is wider than the available witdh. Leaving
+ // lines empty does not make sense without floats, since there will
+ // be no possibility with more space anymore.)
+
+ bool regardBorder = mustBorderBeRegarded (lines->size ());
+ bool thereWillBeMoreSpace = regardBorder ?
+ newLineHasFloatLeft || newLineHasFloatRight : false;
+
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "thereWillBeMoreSpace = %s ? %s || %s : false = %s",
+ regardBorder ? "true" : "false",
+ newLineHasFloatLeft ? "true" : "false",
+ newLineHasFloatRight ? "true" : "false",
+ thereWillBeMoreSpace ? "true" : "false");
+
+
bool tempNewLine = false;
int firstIndex =
lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1;
int searchUntil;
- if (wrapAll && wordIndex >= firstIndex && wordIndex == words->size() -1) {
+ if (wordIndex < firstIndex)
+ // Current word is already part of a line (ending with
+ // firstIndex - 1), so no new line has to be added.
+ newLine = false;
+ else if (wrapAll && wordIndex >= firstIndex &&
+ wordIndex == words->size() -1) {
newLine = true;
searchUntil = wordIndex;
tempNewLine = true;
- PRINTF (" NEW LINE: last word\n");
+ DBG_OBJ_MSG ("construct.word", 1, "<b>new line:</b> last word");
} else if (wordIndex >= firstIndex &&
// TODO: lineMustBeBroken should be independent of
// the penalty index?
word->badnessAndPenalty.lineMustBeBroken (penaltyIndex)) {
newLine = true;
searchUntil = wordIndex;
- PRINTF (" NEW LINE: forced break\n");
+ DBG_OBJ_MSG ("construct.word", 1, "<b>new line:</b> forced break");
} else {
// Break the line when too tight, but only when there is a
// possible break point so far. (TODO: I've forgotten the
// original bug which is fixed by this.)
+
+ // Exception of the latter rule: thereWillBeMoreSpace; see
+ // above, where it is defined.
+
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "possible line break between %d and %d?",
+ firstIndex, wordIndex - 1);
+ DBG_OBJ_MSG_START ();
+
bool possibleLineBreak = false;
- for (int i = firstIndex; !possibleLineBreak && i <= wordIndex - 1; i++)
+ for (int i = firstIndex;
+ !(thereWillBeMoreSpace || possibleLineBreak)
+ && i <= wordIndex - 1;
+ i++) {
+ DBG_OBJ_MSGF ("construct.word", 2, "examining word %d", i);
if (words->getRef(i)->badnessAndPenalty
- .lineCanBeBroken (penaltyIndex))
+ .lineCanBeBroken (penaltyIndex)) {
+ DBG_MSG_WORD ("construct.word", 2, "break possible for word:",
+ i, "");
possibleLineBreak = true;
+ }
+ }
+
+ DBG_OBJ_MSG_END ();
+ DBG_OBJ_MSGF ("construct.word", 1, "=> %s",
+ possibleLineBreak ? "true" : "false");
- if (possibleLineBreak && word->badnessAndPenalty.lineTooTight ()) {
+ DBG_OBJ_MSGF ("construct.word", 1, "word->... too tight: %s",
+ word->badnessAndPenalty.lineTooTight () ?
+ "true" : "false");
+
+ if ((thereWillBeMoreSpace || possibleLineBreak)
+ && word->badnessAndPenalty.lineTooTight ()) {
+ // TODO Should hyphenation be considered here? (Here,
+ // because it is to late in searchBreakPos?)
newLine = true;
searchUntil = wordIndex - 1;
- PRINTF (" NEW LINE: line too tight\n");
- } else
+ DBG_OBJ_MSG ("construct.word", 1,
+ "<b>new line:</b> line too tight");
+ } else {
+ DBG_OBJ_MSG ("construct.word", 1, "no <b>new line</b>");
newLine = false;
+ }
}
if(!newLine && !wrapAll)
@@ -575,65 +709,98 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll)
// newLine is calculated as "true".
mustQueueResize = true;
- if(newLine) {
+ 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;
+ int height = 1; // assumed by calcBorders before (see there)
+ int breakPos;
+
+ balanceBreakPosAndHeight (wordIndex, firstIndex, &searchUntil,
+ tempNewLine, penaltyIndex, true,
+ &thereWillBeMoreSpace, wrapAll,
+ &wordListChanged, &wordIndexEnd,
+ lines->size() > 0 ?
+ lines->getLastRef()
+ ->lastOofRefPositionedBeforeThisLine : -1,
+ regardBorder, &height, &breakPos);
+
+ int lastFloatPos = lines->size() > 0 ?
+ lines->getLastRef()->lastOofRefPositionedBeforeThisLine : -1;
+ bool floatHandled;
+ int yNewLine = yOffsetOfPossiblyMissingLine (lines->size ());
- 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);
- 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;
- }
+ DBG_OBJ_MSG ("construct.word", 1, "<i>floatHandled loop cycle</i>");
+ DBG_OBJ_MSG_START ();
+
+ DBG_OBJ_MSGF ("construct.word", 2,
+ "breakPos = %d, height = %d, lastFloatPos = %d",
+ breakPos, height, lastFloatPos);
- PRINTF ("[%p] accumulating again from %d to %d\n",
- this, breakPos + 1, wordIndexEnd);
- for(int i = breakPos + 1; i <= wordIndexEnd; i++)
- accumulateWordData (i);
+ int startSearch = misc::max (firstIndex, lastFloatPos + 1);
+ int newFloatPos = -1;
+
+ // Step 1: search for the next float.
+ DBG_OBJ_MSGF ("construct.word", 2, "searching from %d to %d",
+ startSearch, breakPos);
+ for (int i = startSearch; newFloatPos == -1 && i <= breakPos; i++) {
+ core::Content *content = &(words->getRef(i)->content);
+ if (content->type == core::Content::WIDGET_OOF_REF &&
+ // Later, absolutepositioned elements (which do not affect
+ // borders) can be ignored at this point.
+ (containingBlock->outOfFlowMgr->affectsLeftBorder
+ (content->widget) ||
+ containingBlock->outOfFlowMgr->affectsRightBorder
+ (content->widget)))
+ newFloatPos = i;
+ }
+
+ DBG_OBJ_MSGF ("construct.word", 2, "newFloatPos = %d", newFloatPos);
+
+ if (newFloatPos == -1)
+ floatHandled = false;
+ else {
+ floatHandled = true;
- } while(!lineAdded);
+ // Step 2: position the float and re-calculate the line.
+ lastFloatPos = newFloatPos;
+
+ containingBlock->outOfFlowMgr->tellPosition
+ (words->getRef(lastFloatPos)->content.widget, yNewLine);
+
+ balanceBreakPosAndHeight (wordIndex, firstIndex, &searchUntil,
+ tempNewLine, penaltyIndex, false,
+ &thereWillBeMoreSpace, wrapAll,
+ &wordListChanged, &wordIndexEnd,
+ lastFloatPos, regardBorder, &height,
+ &breakPos);
+ }
+
+ DBG_OBJ_MSG_END ();
+ } while (floatHandled);
+
+ addLine (firstIndex, breakPos, lastFloatPos, tempNewLine);
+
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "accumulating again from %d to %d\n",
+ breakPos + 1, wordIndexEnd);
+ for(int i = breakPos + 1; i <= wordIndexEnd; i++)
+ accumulateWordData (i);
+
+ // update word pointer as hyphenateWord() can trigger a
+ // reorganization of the words structure
+ word = words->getRef (wordIndex);
+
+ penaltyIndex = calcPenaltyIndexForNewLine ();
}
} 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,41 +817,247 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll)
firstWordWithoutLine = lines->getLastRef()->lastWord + 1;
if (wordIndex >= firstWordWithoutLine) {
- word->content.widget->parentRef = lines->size ();
+ word->content.widget->parentRef =
+ OutOfFlowMgr::createRefNormalFlow (lines->size ());
PRINTF ("The %s %p is assigned parentRef = %d.\n",
word->content.widget->getClassName(), word->content.widget,
word->content.widget->parentRef);
}
}
+ DBG_OBJ_MSG_END ();
+
return wordListChanged;
}
+// *height must be initialized, but not *breakPos.
+// *wordIndexEnd must be initialized (initially to wordIndex)
+void Textblock::balanceBreakPosAndHeight (int wordIndex, int firstIndex,
+ int *searchUntil, bool tempNewLine,
+ int penaltyIndex,
+ bool borderIsCalculated,
+ bool *thereWillBeMoreSpace,
+ bool wrapAll, bool *wordListChanged,
+ int *wordIndexEnd, int lastFloatPos,
+ bool regardBorder, int *height,
+ int *breakPos)
+{
+ DBG_OBJ_MSGF ("construct.word", 0,
+ "<b>balanceBreakPosAndHeight</b> (%d, %d. %d, %s, %d, %s, "
+ "..., %s, ..., %d, %s, %d, ...)",
+ wordIndex, firstIndex, *searchUntil,
+ tempNewLine ? "true" : "false", penaltyIndex,
+ borderIsCalculated ? "true" : "false",
+ wrapAll ? "true" : "false", lastFloatPos,
+ regardBorder ? "true" : "false", *height);
+ DBG_OBJ_MSG_START ();
+
+ // The height of this part of the line (until the new break
+ // position) may change with the break position, but the break
+ // position may depend on the height. We try to let these values
+ // converge.
+ //
+ // The height, as a function of the break position, is
+ // monotonically (but not strictly) increasing, since more words
+ // may make the line higher (but not flatter). The break position,
+ // as a function of the height, is, however, monotonically (but not
+ // strictly) *de*creasing, since flatter lines may fit easier
+ // between floats (although this is a rare case). So a convergence
+ // is not necessary.
+ //
+ // For this reason, we iterate only as long as the height does not
+ // increase again, and stop if it remains the same. As the minimum
+ // is 1, this approach will force the iteration to stop.
+ //
+ // (As a side effect, this will lead to a larger break position,
+ // and so place as much words as possible in the line.)
+
+ int runNo = 1;
+ while (true) {
+ if (!(borderIsCalculated && runNo == 1)) {
+ // borderIsCalculated is, of course, only valid in the first run
+ calcBorders (lastFloatPos, *height);
+ *thereWillBeMoreSpace = regardBorder ?
+ newLineHasFloatLeft || newLineHasFloatRight : false;
+
+ for(int i = firstIndex; i <= *wordIndexEnd; i++)
+ accumulateWordData (i);
+ }
+
+ DBG_OBJ_MSGF ("construct.word", 1, "thereWillBeMoreSpace = %s",
+ *thereWillBeMoreSpace ? "true" : "false");
+
+ int newBreakPos =
+ searchBreakPos (wordIndex, firstIndex, searchUntil,
+ tempNewLine, penaltyIndex,
+ *thereWillBeMoreSpace, wrapAll,
+ wordListChanged, wordIndexEnd);
+ int newHeight = calcLinePartHeight (firstIndex, newBreakPos);
+
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "runNo = %d, newBreakPos = %d, newHeight = %d",
+ runNo, newBreakPos, newHeight);
+ if (runNo == 1)
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "old: height = %d, breakPos undefined", *height);
+ else
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "old: height = %d, breakPos = %d", *height, *breakPos);
+
+ if (runNo != 1 /* Since *some* value are needed, the results
+ from the first run are never discarded. */
+ && newHeight >= *height) {
+ if (newHeight == *height) {
+ // newHeight == height: convergence, stop here. The new break
+ // position is, nevertheless, adopted.
+ DBG_OBJ_MSG ("construct.word", 1, "stopping, adopting new values");
+ *breakPos = newBreakPos;
+ } else
+ // newHeight > height: do not proceed, discard new values,
+ // which are less desirable than the old ones (see above).
+ DBG_OBJ_MSG ("construct.word", 1,
+ "stopping, discarding new values");
+ break;
+ } else {
+ DBG_OBJ_MSG ("construct.word", 1, "adopting new values, continuing");
+ *height = newHeight;
+ *breakPos = newBreakPos;
+ }
+
+ runNo++;
+ }
+
+ DBG_OBJ_MSG_END ();
+}
+
+// *wordIndexEnd must be initialized (initially to wordIndex)
+int Textblock::searchBreakPos (int wordIndex, int firstIndex, int *searchUntil,
+ bool tempNewLine, int penaltyIndex,
+ bool thereWillBeMoreSpace, bool wrapAll,
+ bool *wordListChanged, int *wordIndexEnd)
+{
+ DBG_OBJ_MSGF ("construct.word", 0,
+ "<b>searchBreakPos</b> (%d, %d. %d, %s, %d, %s, %s, ...)",
+ wordIndex, firstIndex, *searchUntil,
+ tempNewLine ? "true" : "false", penaltyIndex,
+ thereWillBeMoreSpace ? "true" : "false",
+ wrapAll ? "true" : "false");
+ DBG_OBJ_MSG_START ();
+
+ int result;
+ bool lineAdded;
+
+ do {
+ DBG_OBJ_MSG ("construct.word", 1, "<i>searchBreakPos loop cycle</i>");
+ DBG_OBJ_MSG_START ();
+
+ if (firstIndex > *searchUntil) {
+ // empty line
+ DBG_OBJ_MSG ("construct.word", 1, "empty line");
+ assert (*searchUntil == firstIndex - 1);
+ result = firstIndex - 1;
+ lineAdded = true;
+ } else if (thereWillBeMoreSpace &&
+ words->getRef(firstIndex)->badnessAndPenalty.lineTooTight ()) {
+ // TODO Consider hyphenation.
+ DBG_OBJ_MSG ("construct.word", 1, "too tight => empty line");
+ result = firstIndex - 1;
+ lineAdded = true;
+ } else {
+ DBG_OBJ_MSG ("construct.word", 1, "non-empty line");
+
+ int breakPos =
+ searchMinBap (firstIndex, *searchUntil, penaltyIndex,
+ thereWillBeMoreSpace, wrapAll);
+ int hyphenatedWord = considerHyphenation (firstIndex, breakPos);
+
+ DBG_OBJ_MSGF ("construct.word", 1, "breakPos = %d", breakPos);
+ DBG_MSG_WORD ("construct.word", 1, "<i>break at word:</i> ",
+ breakPos, "");
+ DBG_OBJ_MSGF ("construct.word", 1, "hyphenatedWord = %d",
+ hyphenatedWord);
+ if (hyphenatedWord != -1)
+ DBG_MSG_WORD ("construct.word", 1,
+ "<i>hyphenate at word:</i> ",
+ hyphenatedWord, "");
+
+ if(hyphenatedWord == -1) {
+ result = breakPos;
+ lineAdded = true;
+ } else {
+ // TODO hyphenateWord() should return whether something
+ // has changed at all. So that a second run, with
+ // !word->canBeHyphenated, is unnecessary.
+ // TODO Update: The return value of hyphenateWord() should
+ // be checked.
+ DBG_OBJ_MSGF ("construct.word", 1, "old searchUntil = %d",
+ *searchUntil);
+ int n = hyphenateWord (hyphenatedWord);
+ *searchUntil += n;
+ if (hyphenatedWord <= wordIndex)
+ *wordIndexEnd += n;
+ DBG_OBJ_MSGF ("construct.word", 1, "new searchUntil = %d",
+ *searchUntil);
+ lineAdded = false;
+ result = 1000000000; // compiler happiness
+
+ if (n > 0 && hyphenatedWord <= wordIndex)
+ *wordListChanged = true;
+
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "accumulating again from %d to %d\n",
+ breakPos + 1, *wordIndexEnd);
+ for(int i = breakPos + 1; i <= *wordIndexEnd; i++)
+ accumulateWordData (i);
+ }
+ }
+
+ DBG_OBJ_MSG_END ();
+ } while(!lineAdded);
+
+ DBG_OBJ_MSGF ("construct.word", 1, "=> %d", result);
+ DBG_OBJ_MSG_END ();
+
+ return result;
+}
+
int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex,
- bool correctAtEnd)
+ bool thereWillBeMoreSpace, bool correctAtEnd)
{
- PRINTF (" searching from %d to %d\n", firstWord, lastWord);
+ DBG_OBJ_MSGF ("construct.word", 0,
+ "<b>searchMinBap</b> (%d, %d, %d, %s, %s)",
+ firstWord, lastWord, penaltyIndex,
+ thereWillBeMoreSpace ? "true" : "false",
+ correctAtEnd ? "true" : "false");
+ DBG_OBJ_MSG_START ();
int pos = -1;
+ DBG_OBJ_MSG_START ();
for (int i = firstWord; i <= lastWord; i++) {
Word *w = words->getRef(i);
- //printf (" %d (of %d): ", i, words->size ());
- //printWord (w);
- //printf ("\n");
-
+ DBG_IF_RTFL {
+ misc::StringBuffer sb;
+ w->badnessAndPenalty.intoStringBuffer (&sb);
+ DBG_OBJ_MSGF ("construct.word", 2, "%d (of %d): b+p: %s",
+ i, words->size (), sb.getChars ());
+ DBG_MSG_WORD ("construct.word", 2, "(<i>i. e.:</i> ", i, ")");
+ }
+
+ // "<=" instead of "<" in the next lines (see also
+ // "correctedBap.compareTo ...) tends to result in more words
+ // per line -- theoretically. Practically, the case "==" will
+ // never occur.
if (pos == -1 ||
w->badnessAndPenalty.compareTo (penaltyIndex,
&words->getRef(pos)
->badnessAndPenalty) <= 0)
- // "<=" instead of "<" in the next lines tends to result in
- // more words per line -- theoretically. Practically, the
- // case "==" will never occur.
pos = i;
}
+ DBG_OBJ_MSG_END ();
- PRINTF (" found at %d\n", pos);
+ DBG_OBJ_MSGF ("construct.word", 1, "found at %d\n", pos);
if (correctAtEnd && lastWord == words->size () - 1) {
// Since no break and no space is added, the last word will have
@@ -699,21 +1072,24 @@ int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex,
BadnessAndPenalty correctedBap = w->badnessAndPenalty;
correctedBap.setPenalty (0);
- //printf (" corrected bap: ");
- //correctedBap.print ();
- //printf ("\n");
-
+ DBG_IF_RTFL {
+ misc::StringBuffer sb;
+ correctedBap.intoStringBuffer (&sb);
+ DBG_OBJ_MSGF ("construct.word", 1, "corrected b+p: %s",
+ sb.getChars ());
+ }
+
if (correctedBap.compareTo(penaltyIndex,
&words->getRef(pos)->badnessAndPenalty) <= 0) {
pos = lastWord;
- PRINTF (" corrected => %d\n", pos);
+ DBG_OBJ_MSGF ("construct.word", 1, "corrected: %d\n", pos);
}
}
+ DBG_OBJ_MSG_END ();
return pos;
}
-
/**
* Suggest a word to hyphenate, when breaking at breakPos is
* planned. Return a word index or -1, when hyphenation makes no
@@ -764,6 +1140,19 @@ bool Textblock::isHyphenationCandidate (Word *word)
}
+int Textblock::calcLinePartHeight (int firstWord, int lastWord)
+{
+ int ascent = 0, descent = 0;
+
+ for (int i = firstWord; i <= lastWord; i++) {
+ Word *word = words->getRef (i);
+ ascent = misc::max (ascent, word->size.ascent);
+ descent = misc::max (descent, word->size.descent);
+ }
+
+ return misc::max (ascent + descent, 1);
+}
+
/**
* Counter part to wordWrap(), but for extremes, not size calculation.
*/
@@ -771,13 +1160,18 @@ void Textblock::handleWordExtremes (int wordIndex)
{
// TODO Overall, clarify penalty index.
+ DBG_OBJ_MSGF ("construct.paragraph", 0,
+ "<b>handleWordExtremes</b> (%d)", wordIndex);
+ DBG_OBJ_MSG_START ();
+
Word *word = words->getRef (wordIndex);
+ DBG_MSG_WORD ("construct.paragraph", 1,
+ "<i>handled word:</i> ", wordIndex, "");
+
core::Extremes wordExtremes;
getWordExtremes (word, &wordExtremes);
-
- //printf ("[%p] HANDLE_WORD_EXTREMES (%d): ", this, wordIndex);
- //printWordWithFlags (word);
- //printf (" => %d / %d\n", wordExtremes.minWidth, wordExtremes.maxWidth);
+ DBG_OBJ_MSGF ("construct.paragraph", 1, "extremes: %d / %d",
+ wordExtremes.minWidth, wordExtremes.maxWidth);
if (wordIndex == 0) {
wordExtremes.minWidth += line1Offset;
@@ -802,10 +1196,12 @@ void Textblock::handleWordExtremes (int wordIndex)
} else
par->maxParMin = par->maxParMax = 0;
- PRINTF (" new par: %d\n", paragraphs->size() - 1);
+ DBG_OBJ_MSGF ("construct.paragraph", 1, "new par: %d",
+ paragraphs->size() - 1);
}
- PRINTF (" last par: %d\n", paragraphs->size() - 1);
+ DBG_OBJ_MSGF ("construct.paragraph", 1, "last par: %d",
+ paragraphs->size() - 1);
Paragraph *lastPar = paragraphs->getLastRef();
int corrDiffMin, corrDiffMax;
@@ -821,8 +1217,10 @@ void Textblock::handleWordExtremes (int wordIndex)
} else
corrDiffMin = corrDiffMax = 0;
- PRINTF (" (lastPar from %d to %d; corrDiffMin = %d, corDiffMax = %d)\n",
- lastPar->firstWord, lastPar->lastWord, corrDiffMin, corrDiffMax);
+ DBG_OBJ_MSGF ("construct.paragraph", 1,
+ "(lastPar from %d to %d; corrDiffMin = %d, corDiffMax = %d)",
+ lastPar->firstWord, lastPar->lastWord, corrDiffMin,
+ corrDiffMax);
// Minimum: between two *possible* breaks.
// Shrinkability could be considered, but really does not play a role.
@@ -836,11 +1234,13 @@ void Textblock::handleWordExtremes (int wordIndex)
lastPar->parMax += wordExtremes.maxWidth + word->hyphenWidth + corrDiffMax;
lastPar->maxParMax = misc::max (lastPar->maxParMax, lastPar->parMax);
- PRINTF (" => parMin = %d (max = %d), parMax = %d (max = %d)\n",
- lastPar->parMin, lastPar->maxParMin, lastPar->parMax,
- lastPar->maxParMax);
+ DBG_OBJ_MSGF ("construct.paragraph", 1,
+ "=> parMin = %d (max = %d), parMax = %d (max = %d)",
+ lastPar->parMin, lastPar->maxParMin, lastPar->parMax,
+ lastPar->maxParMax);
lastPar->lastWord = wordIndex;
+ DBG_OBJ_MSG_END ();
}
/**
@@ -882,10 +1282,18 @@ int Textblock::hyphenateWord (int wordIndex)
PRINTF ("[%p] %d words ...\n", this, words->size ());
words->insert (wordIndex, numBreaks);
+
+ DBG_IF_RTFL {
+ for (int i = wordIndex + numBreaks; i < words->size (); i++)
+ DBG_SET_WORD (i);
+ }
+
for (int i = 0; i < numBreaks; i++)
initWord (wordIndex + i);
PRINTF ("[%p] ... => %d words\n", this, words->size ());
+ moveWordIndices (wordIndex, numBreaks);
+
// Adjust anchor indexes.
for (int i = 0; i < anchors->size (); i++) {
Anchor *anchor = anchors->getRef (i);
@@ -909,6 +1317,10 @@ int Textblock::hyphenateWord (int wordIndex)
end - start);
PRINTF (" [%d] -> '%s'\n", wordIndex + i, w->content.text);
+ DBG_OBJ_ARRATTRSET_SYM ("words", wordIndex + i, "type", "TEXT");
+ DBG_OBJ_ARRATTRSET_STR ("words", wordIndex + i,
+ "text/widget/breakSpace", w->content.text);
+
// Note: there are numBreaks + 1 word parts.
if (i == 0)
w->flags |= Word::WORD_START;
@@ -941,13 +1353,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?
@@ -961,13 +1380,33 @@ int Textblock::hyphenateWord (int wordIndex)
return numBreaks;
}
+void Textblock::moveWordIndices (int wordIndex, int num)
+{
+ if (containingBlock->outOfFlowMgr)
+ containingBlock->outOfFlowMgr->moveExternalIndices (this, wordIndex, num);
+
+ for (int i = lines->size () - 1; i >= 0; i--) {
+ Line *line = lines->getRef (i);
+ if (line->lastOofRefPositionedBeforeThisLine < wordIndex)
+ // Since lastOofRefPositionedBeforeThisLine are ascending,
+ // the search can be stopped here.
+ break;
+ else
+ line->lastOofRefPositionedBeforeThisLine += num;
+ }
+}
+
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 +1421,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 +1445,8 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
+ word->content.widget->getStyle()->margin.top
- collapseMarginTop);
- word->content.widget->parentRef = lineIndex;
+ word->content.widget->parentRef =
+ OutOfFlowMgr::createRefNormalFlow (lineIndex);
} else {
line->marginDescent =
misc::max (line->marginDescent, line->boxDescent);
@@ -1021,6 +1461,10 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
void Textblock::accumulateWordData (int wordIndex)
{
+ DBG_OBJ_MSGF ("construct.word", 1, "<b>accumulateWordData</b> (%d)",
+ wordIndex);
+ DBG_OBJ_MSG_START ();
+
// Typically, the word in question is in the last line; in any case
// quite at the end of the text, so that linear search is actually
// the fastest option.
@@ -1035,14 +1479,14 @@ void Textblock::accumulateWordData (int wordIndex)
firstWordOfLine = lines->getRef(lineIndex - 1)->lastWord + 1;
Word *word = words->getRef (wordIndex);
- PRINTF ("[%p] ACCUMULATE_WORD_DATA (%d); lineIndex = %d: ...\n",
- this, wordIndex, lineIndex);
+ DBG_OBJ_MSGF ("construct.word", 2, "lineIndex = %d", lineIndex);
int availWidth = calcAvailWidth (lineIndex);
- PRINTF (" (%s existing line %d starts with word %d)\n",
- lineIndex < lines->size () ? "already" : "not yet",
- lineIndex, firstWordOfLine);
+ DBG_OBJ_MSGF ("construct.word", 2,
+ "(%s existing line %d starts with word %d; availWidth = %d)",
+ lineIndex < lines->size () ? "already" : "not yet",
+ lineIndex, firstWordOfLine, availWidth);
if (wordIndex == firstWordOfLine) {
// first word of the (not neccessarily yet existing) line
@@ -1051,6 +1495,11 @@ void Textblock::accumulateWordData (int wordIndex)
word->maxDescent = word->size.descent;
word->totalSpaceStretchability = 0;
word->totalSpaceShrinkability = 0;
+
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "first word of line: words[%d].totalWidth = %d + %d = %d",
+ wordIndex, word->size.width, word->hyphenWidth,
+ word->totalWidth);
} else {
Word *prevWord = words->getRef (wordIndex - 1);
@@ -1063,6 +1512,13 @@ void Textblock::accumulateWordData (int wordIndex)
prevWord->totalSpaceStretchability + getSpaceStretchability(prevWord);
word->totalSpaceShrinkability =
prevWord->totalSpaceShrinkability + getSpaceShrinkability(prevWord);
+
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "not first word of line: words[%d].totalWidth = %d + %d - "
+ "%d + %d + %d = %d",
+ wordIndex, prevWord->totalWidth, prevWord->origSpace,
+ prevWord->hyphenWidth, word->size.width,
+ word->hyphenWidth, word->totalWidth);
}
int totalStretchability =
@@ -1073,26 +1529,48 @@ void Textblock::accumulateWordData (int wordIndex)
totalStretchability,
totalShrinkability);
- //printf (" => ");
- //printWord (word);
- //printf ("\n");
+ DBG_IF_RTFL {
+ misc::StringBuffer sb;
+ word->badnessAndPenalty.intoStringBuffer (&sb);
+ DBG_OBJ_MSGF ("construct.word", 1, "b+p: %s", sb.getChars ());
+ }
+
+ DBG_OBJ_MSG_END ();
}
int Textblock::calcAvailWidth (int lineIndex)
{
- int availWidth =
- this->availWidth - getStyle()->boxDiffWidth() - innerPadding;
+ DBG_OBJ_MSGF ("construct.word", 1, "<b>calcAvailWidth</b> (%d <i>of %d</i>)",
+ lineIndex, lines->size());
+ DBG_OBJ_MSG_START ();
+
+ int availWidth = this->availWidth - innerPadding;
if (limitTextWidth &&
layout->getUsesViewport () &&
- availWidth > layout->getWidthViewport () - 10)
+ // margin/border/padding will be subtracted later, via OOFM.
+ availWidth - getStyle()->boxDiffWidth()
+ > layout->getWidthViewport () - 10)
availWidth = layout->getWidthViewport () - 10;
if (lineIndex == 0)
availWidth -= line1OffsetEff;
- //PRINTF("[%p] CALC_AVAIL_WIDTH => %d - %d - %d = %d\n",
- // this, this->availWidth, getStyle()->boxDiffWidth(), innerPadding,
- // availWidth);
+ int leftBorder, rightBorder;
+ if (mustBorderBeRegarded (lineIndex)) {
+ leftBorder = newLineLeftBorder;
+ rightBorder = newLineRightBorder;
+ } else
+ leftBorder = rightBorder = 0;
+
+ leftBorder = misc::max (leftBorder, getStyle()->boxOffsetX());
+ rightBorder = misc::max (rightBorder, getStyle()->boxRestWidth());
+
+ availWidth -= (leftBorder + rightBorder);
+ DBG_OBJ_MSGF ("construct.word", 2, "=> %d - %d - (%d + %d) = %d\n",
+ this->availWidth, innerPadding, leftBorder, rightBorder,
+ availWidth);
+
+ DBG_OBJ_MSG_END ();
return availWidth;
}
@@ -1108,7 +1586,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 {
@@ -1131,49 +1609,70 @@ void Textblock::initLine1Offset (int wordIndex)
*/
void Textblock::alignLine (int lineIndex)
{
+ DBG_OBJ_MSGF ("construct.line", 0, "<b>alignLine</b> (%d)", lineIndex);
+ DBG_OBJ_MSG_START ();
+
Line *line = lines->getRef (lineIndex);
int availWidth = calcAvailWidth (lineIndex);
- Word *firstWord = words->getRef (line->firstWord);
- Word *lastWord = words->getRef (line->lastWord);
-
- for (int i = line->firstWord; i < line->lastWord; i++)
- words->getRef(i)->origSpace = words->getRef(i)->effSpace;
-
- if (firstWord->content.type != core::Content::BREAK) {
- switch (firstWord->style->textAlign) {
- case core::style::TEXT_ALIGN_LEFT:
- case core::style::TEXT_ALIGN_STRING: /* handled elsewhere (in the
- * future)? */
- line->leftOffset = 0;
- break;
- case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */
- line->leftOffset = 0;
- // Do not justify the last line of a paragraph (which ends on a
- // BREAK or with the last word of the page).
- if(!(lastWord->content.type == core::Content::BREAK ||
- line->lastWord == words->size () - 1) ||
- // In some cases, however, an unjustified line would be too wide:
- // when the line would be shrunken otherwise. (This solution is
- // far from perfect, but a better solution would make changes in
- // the line breaking algorithm necessary.)
- availWidth < lastWord->totalWidth)
- justifyLine (line, availWidth - lastWord->totalWidth);
- break;
- case core::style::TEXT_ALIGN_RIGHT:
- line->leftOffset = availWidth - lastWord->totalWidth;
- break;
- case core::style::TEXT_ALIGN_CENTER:
- line->leftOffset = (availWidth - lastWord->totalWidth) / 2;
- break;
- default:
- /* compiler happiness */
- line->leftOffset = 0;
+ if (line->firstWord <= line->lastWord) {
+ Word *firstWord = words->getRef (line->firstWord);
+ Word *lastWord = words->getRef (line->lastWord);
+
+ for (int i = line->firstWord; i < line->lastWord; i++)
+ words->getRef(i)->origSpace = words->getRef(i)->effSpace;
+
+ if (firstWord->content.type != core::Content::BREAK) {
+ switch (firstWord->style->textAlign) {
+ case core::style::TEXT_ALIGN_LEFT:
+ DBG_OBJ_MSG ("construct.line", 1,
+ "first word has 'text-align: left'");
+ line->leftOffset = 0;
+ break;
+ case core::style::TEXT_ALIGN_STRING: /* handled elsewhere (in the
+ * future)? */
+ DBG_OBJ_MSG ("construct.line", 1,
+ "first word has 'text-align: string'");
+ line->leftOffset = 0;
+ break;
+ case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */
+ line->leftOffset = 0;
+ DBG_OBJ_MSG ("construct.line", 1,
+ "first word has 'text-align: justify'");
+ // Do not justify the last line of a paragraph (which ends on a
+ // BREAK or with the last word of the page).
+ if(!(lastWord->content.type == core::Content::BREAK ||
+ line->lastWord == words->size () - 1) ||
+ // In some cases, however, an unjustified line would be too wide:
+ // when the line would be shrunken otherwise. (This solution is
+ // far from perfect, but a better solution would make changes in
+ // the line breaking algorithm necessary.)
+ availWidth < lastWord->totalWidth)
+ justifyLine (line, availWidth - lastWord->totalWidth);
+ break;
+ case core::style::TEXT_ALIGN_RIGHT:
+ DBG_OBJ_MSG ("construct.line", 1,
+ "first word has 'text-align: right'");
+ line->leftOffset = availWidth - lastWord->totalWidth;
+ break;
+ case core::style::TEXT_ALIGN_CENTER:
+ DBG_OBJ_MSG ("construct.line", 1,
+ "first word has 'text-align: center'");
+ line->leftOffset = (availWidth - lastWord->totalWidth) / 2;
+ break;
+ default:
+ /* compiler happiness */
+ line->leftOffset = 0;
+ }
+
+ /* For large lines (images etc), which do not fit into the viewport: */
+ if (line->leftOffset < 0)
+ line->leftOffset = 0;
}
+ } else
+ // empty line
+ line->leftOffset = 0;
- /* For large lines (images etc), which do not fit into the viewport: */
- if (line->leftOffset < 0)
- line->leftOffset = 0;
- }
+ DBG_OBJ_MSG_END ();
}
/**
@@ -1184,43 +1683,53 @@ void Textblock::alignLine (int lineIndex)
*/
void Textblock::rewrap ()
{
- PRINTF ("[%p] REWRAP: wrapRef = %d\n", this, wrapRef);
+ DBG_OBJ_MSG ("construct.line", 0, "<b>rewrap</b> ()");
+ DBG_OBJ_MSG_START ();
if (wrapRefLines == -1)
- /* page does not have to be rewrapped */
- return;
-
- /* All lines up from wrapRef will be rebuild from the word list,
- * the line list up from this position is rebuild. */
- lines->setSize (wrapRefLines);
- nonTemporaryLines = misc::min (nonTemporaryLines, wrapRefLines);
-
- int firstWord;
- if (lines->size () > 0)
- firstWord = lines->getLastRef()->lastWord + 1;
- else
- firstWord = 0;
+ DBG_OBJ_MSG ("construct.line", 0, "does not have to be rewrapped");
+ else {
+ // All lines up from wrapRef will be rebuild from the word list,
+ // the line list up from this position is rebuild.
+ lines->setSize (wrapRefLines);
+ DBG_OBJ_SET_NUM ("lines.size", lines->size ());
+ nonTemporaryLines = misc::min (nonTemporaryLines, wrapRefLines);
+
+ initNewLine ();
+
+ int firstWord;
+ if (lines->size () > 0) {
+ Line *lastLine = lines->getLastRef();
+ firstWord = lastLine->lastWord + 1;
+ } else
+ firstWord = 0;
+
+ DBG_OBJ_MSGF ("construct.line", 0, "starting with word %d", firstWord);
- for (int i = firstWord; i < words->size (); i++) {
- Word *word = words->getRef (i);
+ for (int i = firstWord; i < words->size (); i++) {
+ Word *word = words->getRef (i);
- if (word->content.type == core::Content::WIDGET)
- calcWidgetSize (word->content.widget, &word->size);
-
- wordWrap (i, false);
+ if (word->content.type == core::Content::WIDGET_IN_FLOW)
+ calcWidgetSize (word->content.widget, &word->size);
+
+ wordWrap (i, false);
+
+ // Somewhat historical, but still important, note:
+ //
+ // For the case that something else is done with this word, it
+ // is important that wordWrap() may insert some new words; since
+ // NotSoSimpleVector is used for the words list, the internal
+ // structure may have changed, so getRef() must be called again.
+ //
+ // So this is necessary: word = words->getRef (i);
+ }
- // Somewhat historical, but still important, note:
- //
- // For the case that something else is done with this word, it
- // is important that wordWrap() may insert some new words; since
- // NotSoSimpleVector is used for the words list, the internal
- // structure may have changed, so getRef() must be called again.
- //
- // So this is necessary: word = words->getRef (i);
+ // Next time, the page will not have to be rewrapped.
+ wrapRefLines = -1;
+ DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines);
}
-
- /* Next time, the page will not have to be rewrapped. */
- wrapRefLines = -1;
+
+ DBG_OBJ_MSG_END ();
}
/**
@@ -1228,14 +1737,20 @@ void Textblock::rewrap ()
*/
void Textblock::fillParagraphs ()
{
+ DBG_OBJ_MSG ("resize", 0, "<b>fillParagraphs</b>");
+ DBG_OBJ_MSG_START ();
+
if (wrapRefParagraphs == -1)
return;
// Notice that wrapRefParagraphs refers to the lines, not to the paragraphs.
int firstWordOfLine;
- if (lines->size () > 0 && wrapRefParagraphs > 0)
- firstWordOfLine = lines->getRef(wrapRefParagraphs - 1)->lastWord + 1;
- else
+ if (lines->size () > 0 && wrapRefParagraphs > 0) {
+ // Sometimes, wrapRefParagraphs is larger than lines->size(), due to
+ // floats? (Has to be clarified.)
+ int lineNo = misc::min (wrapRefParagraphs, lines->size ()) - 1;
+ firstWordOfLine = lines->getRef(lineNo)->lastWord + 1;
+ } else
firstWordOfLine = 0;
int parNo;
@@ -1268,22 +1783,175 @@ void Textblock::fillParagraphs ()
handleWordExtremes (i);
wrapRefParagraphs = -1;
+ DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs);
+
+ DBG_OBJ_MSG_END ();
+}
+
+void Textblock::initNewLine ()
+{
+ DBG_OBJ_MSG ("construct.line", 0, "<b>initNewLine</b> ()");
+ DBG_OBJ_MSG_START ();
+
+ // At the very beginning, in Textblock::Textblock, where this
+ // method is called, containingBlock is not yet defined.
+
+ if (containingBlock && containingBlock->outOfFlowMgr) {
+ if (lines->size () == 0) {
+ int clearPosition =
+ containingBlock->outOfFlowMgr->getClearPosition (this);
+ setVerticalOffset (misc::max (clearPosition, 0));
+ }
+ }
+
+ calcBorders (lines->size() > 0 ?
+ lines->getLastRef()->lastOofRefPositionedBeforeThisLine : -1,
+ 1);
+
+ newLineAscent = newLineDescent = 0;
+
+ DBG_OBJ_SET_NUM ("newLineAscent", newLineAscent);
+ DBG_OBJ_SET_NUM ("newLineDescent", newLineDescent);
+
+ DBG_OBJ_MSG_END ();
+}
+
+void Textblock::calcBorders (int lastOofRef, int height)
+{
+ DBG_OBJ_MSGF ("construct.line", 0, "<b>calcBorders</b> (%d, %d)",
+ lastOofRef, height);
+ DBG_OBJ_MSG_START ();
+
+ if (containingBlock && containingBlock->outOfFlowMgr) {
+ // Consider the example:
+ //
+ // <div>
+ // Some text A ...
+ // <p> Some text B ... <img style="float:right" ...> </p>
+ // Some more text C ...
+ // </div>
+ //
+ // If the image is large enough, it should float around the last
+ // paragraph, "Some more text C ...":
+ //
+ // Some more text A ...
+ //
+ // Some more ,---------.
+ // text B ... | |
+ // | <img> |
+ // Some more | | <---- Consider this line!
+ // text C ... '---------'
+ //
+ // Since this float is generated in the <p> element, not in the-
+ // <div> element, and since they are represented by different
+ // instances of dw::Textblock, lastOofRefPositionedBeforeThisLine,
+ // and so lastOofRef, is -1 for the line marked with an arrow;
+ // this would result in ignoring the float, because -1 is
+ // equivalent to the very beginning of the <div> element ("Some
+ // more text A ..."), which is not affected by the float.
+ //
+ // On the other hand, the only relevant values of
+ // Line::lastOofRefPositionedBeforeThisLine are those greater
+ // than the first word of the new line, so a solution is to use
+ // the maximum of both.
+
+
+ int firstWordOfLine = lines->size() > 0 ?
+ lines->getLastRef()->lastWord + 1 : 0;
+ int effOofRef = misc::max (lastOofRef, firstWordOfLine);
+
+ int y = yOffsetOfPossiblyMissingLine (lines->size ());
+
+ newLineHasFloatLeft =
+ containingBlock->outOfFlowMgr->hasFloatLeft (this, y, height, this,
+ effOofRef);
+ newLineHasFloatRight =
+ containingBlock->outOfFlowMgr->hasFloatRight (this, y, height, this,
+ effOofRef);
+ newLineLeftBorder =
+ containingBlock->outOfFlowMgr->getLeftBorder (this, y, height, this,
+ effOofRef);
+ newLineRightBorder =
+ containingBlock->outOfFlowMgr->getRightBorder (this, y, height, this,
+ effOofRef);
+
+ DBG_OBJ_MSGF ("construct.line", 0,
+ "%d (%s) / %d (%s), at %d (%d), until %d = max (%d, %d)\n",
+ newLineLeftBorder, newLineHasFloatLeft ? "true" : "false",
+ newLineRightBorder, newLineHasFloatRight ? "true" : "false",
+ y, height, effOofRef, lastOofRef, firstWordOfLine);
+ } else {
+ newLineHasFloatLeft = newLineHasFloatRight = false;
+ newLineLeftBorder = newLineRightBorder = 0;
+
+ DBG_OBJ_MSG ("construct.line", 0, "<i>no CB of OOFM</i>");
+ }
+
+ DBG_OBJ_MSG_END ();
}
void Textblock::showMissingLines ()
{
- int firstWordToWrap = lines->size () > 0 ?
- lines->getRef(lines->size () - 1)->lastWord + 1 : 0;
- PRINTF ("[%p] SHOW_MISSING_LINES: wrap from %d to %d\n",
- this, firstWordToWrap, words->size () - 1);
+ DBG_OBJ_MSG ("construct.line", 0, "<b>showMissingLines</b> ()");
+ DBG_OBJ_MSG_START ();
+
+ // "Temporary word": when the last word is an OOF reference, it is
+ // not processed, and not part of any line. For this reason, we
+ // introduce a "temporary word", which is in flow, after this last
+ // OOF reference, and later removed again.
+ bool tempWord = words->size () > 0 &&
+ words->getLastRef()->content.type == core::Content::WIDGET_OOF_REF;
+ int firstWordToWrap =
+ lines->size () > 0 ? lines->getLastRef()->lastWord + 1 : 0;
+
+ DBG_OBJ_MSGF ("construct.line", 1,
+ "words->size() = %d, firstWordToWrap = %d, tempWord = %s",
+ words->size (), firstWordToWrap, tempWord ? "true" : "false");
+
+ if (tempWord) {
+ core::Requisition size = { 0, 0, 0 };
+ addText0 ("", 0, Word::WORD_START | Word::WORD_END, getStyle (), &size);
+ }
+
for (int i = firstWordToWrap; i < words->size (); i++)
wordWrap (i, true);
+
+ // Remove temporary word again. The only reference should be the line.
+ if (tempWord) {
+ cleanupWord (words->size () - 1);
+ words->setSize (words->size () - 1);
+ if (lines->getLastRef()->lastWord > words->size () - 1)
+ lines->getLastRef()->lastWord = words->size () - 1;
+ }
+
+ // The following old code should not be necessary anymore, after
+ // the introduction of the "virtual word". Instead, test the
+ // condition.
+ assert (lines->size () == 0 ||
+ lines->getLastRef()->lastWord == words->size () - 1);
+ /*
+ // In some cases, there are some words of type WIDGET_OOF_REF left, which
+ // are not added to line, since addLine() is only called within
+ // wrapWordInFlow(), but not within wrapWordOofRef(). The missing line
+ // is created here, so it is ensured that the last line ends with the last
+ // word.
+
+ int firstWordNotInLine =
+ lines->size () > 0 ? lines->getLastRef()->lastWord + 1: 0;
+ DBG_OBJ_MSGF ("construct.line", 1, "firstWordNotInLine = %d (of %d)",
+ firstWordNotInLine, words->size ());
+ if (firstWordNotInLine < words->size ())
+ addLine (firstWordNotInLine, words->size () - 1, -1, true);
+ */
+
+ DBG_OBJ_MSG_END ();
}
void Textblock::removeTemporaryLines ()
{
lines->setSize (nonTemporaryLines);
+ DBG_OBJ_SET_NUM ("lines.size", lines->size ());
}
int Textblock::getSpaceShrinkability(struct Word *word)
diff --git a/dw/types.cc b/dw/types.cc
index 86836bc1..56af66d1 100644
--- a/dw/types.cc
+++ b/dw/types.cc
@@ -268,5 +268,90 @@ void Region::addRectangle (Rectangle *rPointer)
rectangleList->append (r);
}
+Content::Type Content::maskForSelection (bool followReferences)
+{
+ Content::Type widgetMask = (Content::Type)
+ (Content::WIDGET_IN_FLOW |
+ (followReferences ? Content::WIDGET_OOF_REF : Content::WIDGET_OOF_CONT));
+ return (Content::Type)(Content::SELECTION_CONTENT | widgetMask);
+}
+
+void Content::intoStringBuffer(Content *content, misc::StringBuffer *sb)
+{
+ switch(content->type) {
+ case START:
+ sb->append ("<start>");
+ break;
+ case END:
+ sb->append ("<end>");
+ break;
+ case TEXT:
+ sb->append ("\"");
+ sb->append (content->text);
+ sb->append ("\"");
+ break;
+ case WIDGET_IN_FLOW:
+ sb->append ("<widget in flow: ");
+ sb->appendPointer (content->widget);
+ sb->append (" (");
+ sb->append (content->widget->getClassName());
+ sb->append (")>");
+ break;
+ case WIDGET_OOF_REF:
+ sb->append ("<widget oof ref: ");
+ sb->appendPointer (content->widget);
+ sb->append (" (");
+ sb->append (content->widget->getClassName());
+ sb->append (")>");
+ break;
+ case WIDGET_OOF_CONT:
+ sb->append ("<widget oof cont: ");
+ sb->appendPointer (content->widget);
+ sb->append (" (");
+ sb->append (content->widget->getClassName());
+ sb->append (")>");
+ break;
+ case BREAK:
+ sb->append ("<break>");
+ break;
+ default:
+ sb->append ("<");
+ sb->appendInt (content->type);
+ sb->append ("?>");
+ break;
+ }
+}
+
+void Content::maskIntoStringBuffer(Type mask, misc::StringBuffer *sb)
+{
+ sb->append ((mask & START) ? "st" : "--");
+ sb->append (":");
+ sb->append ((mask & END) ? "en" : "--");
+ sb->append (":");
+ sb->append ((mask & TEXT) ? "tx" : "--");
+ sb->append (":");
+ sb->append ((mask & WIDGET_IN_FLOW) ? "wf" : "--");
+ sb->append (":");
+ sb->append ((mask & WIDGET_OOF_REF) ? "Wr" : "--");
+ sb->append (":");
+ sb->append ((mask & WIDGET_OOF_CONT) ? "Wc" : "--");
+ sb->append (":");
+ sb->append ((mask & BREAK) ? "br" : "--");
+}
+
+void Content::print (Content *content)
+{
+ misc::StringBuffer sb;
+ intoStringBuffer (content, &sb);
+ printf ("%s", sb.getChars ());
+}
+
+void Content::printMask (Type mask)
+{
+ misc::StringBuffer sb;
+ maskIntoStringBuffer (mask, &sb);
+ printf ("%s", sb.getChars ());
+}
+
} // namespace core
} // namespace dw
diff --git a/dw/types.hh b/dw/types.hh
index f04fc138..e910d296 100644
--- a/dw/types.hh
+++ b/dw/types.hh
@@ -188,11 +188,27 @@ struct Content
START = 1 << 0,
END = 1 << 1,
TEXT = 1 << 2,
- WIDGET = 1 << 3,
- BREAK = 1 << 4,
+
+ /** \brief widget in normal flow, so that _this_ widget
+ (containing this content) is both container (parent) and
+ generator */
+ WIDGET_IN_FLOW = 1 << 3,
+
+ /** \brief widget out of flow (OOF); _this_ widget (containing
+ this content) is only the container (parent), but _not_
+ generator */
+ WIDGET_OOF_CONT = 1 << 4,
+
+ /** \brief reference to a widget out of flow (OOF); _this_
+ widget (containing this content) is only the generator
+ (parent), but _not_ container */
+ WIDGET_OOF_REF = 1 << 5,
+ BREAK = 1 << 6,
+
ALL = 0xff,
REAL_CONTENT = 0xff ^ (START | END),
- SELECTION_CONTENT = TEXT | WIDGET | BREAK
+ SELECTION_CONTENT = TEXT | BREAK, // WIDGET_* must be set additionally
+ ANY_WIDGET = WIDGET_IN_FLOW | WIDGET_OOF_CONT | WIDGET_OOF_REF,
};
/* Content is embedded in struct Word therefore we
@@ -205,6 +221,13 @@ struct Content
Widget *widget;
int breakSpace;
};
+
+ static Content::Type maskForSelection (bool followReferences);
+
+ static void intoStringBuffer(Content *content, lout::misc::StringBuffer *sb);
+ static void maskIntoStringBuffer(Type mask, lout::misc::StringBuffer *sb);
+ static void print (Content *content);
+ static void printMask (Type mask);
};
} // namespace core
diff --git a/dw/ui.cc b/dw/ui.cc
index 22199980..75314342 100644
--- a/dw/ui.cc
+++ b/dw/ui.cc
@@ -39,6 +39,7 @@ Embed::Embed(Resource *resource)
registerName ("dw::core::ui::Embed", &CLASS_ID);
this->resource = resource;
resource->setEmbed (this);
+ DBG_OBJ_ASSOC_CHILD (resource);
}
Embed::~Embed()
@@ -89,17 +90,32 @@ bool Embed::buttonPressImpl (core::EventButton *event)
void Embed::setWidth (int width)
{
+ DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width);
+ DBG_OBJ_MSG_START ();
+
resource->setWidth (width);
+
+ DBG_OBJ_MSG_END ();
}
void Embed::setAscent (int ascent)
{
+ DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent);
+ DBG_OBJ_MSG_START ();
+
resource->setAscent (ascent);
+
+ DBG_OBJ_MSG_END ();
}
void Embed::setDescent (int descent)
{
+ DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent);
+ DBG_OBJ_MSG_START ();
+
resource->setDescent (descent);
+
+ DBG_OBJ_MSG_END ();
}
void Embed::setDisplayed (bool displayed)
@@ -180,6 +196,7 @@ void Resource::ActivateEmitter::emitLeave (Resource *resource)
Resource::~Resource ()
{
+ DBG_OBJ_DELETE ();
}
void Resource::setEmbed (Embed *embed)
@@ -189,10 +206,17 @@ void Resource::setEmbed (Embed *embed)
void Resource::getExtremes (Extremes *extremes)
{
+ DBG_OBJ_MSG ("resize", 0, "<b>getExtremes</b>");
+ DBG_OBJ_MSG_START ();
+
/* Simply return the requisition width */
Requisition requisition;
sizeRequest (&requisition);
extremes->minWidth = extremes->maxWidth = requisition.width;
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d / %d",
+ extremes->minWidth, extremes->maxWidth);
+ DBG_OBJ_MSG_END ();
}
void Resource::sizeAllocate (Allocation *allocation)
@@ -271,11 +295,12 @@ void ComplexButtonResource::LayoutReceiver::canvasSizeChanged (int width,
/**
* \todo Verify that this is correct.
*/
- resource->queueResize (resource->childWidget->extremesChanged ());
+ resource->queueResize (resource->childWidget->extremesQueued ());
}
ComplexButtonResource::ComplexButtonResource ()
{
+ DBG_OBJ_CREATE ("dw::core::ui::ComplexButtonResource");
layout = NULL;
layoutReceiver.resource = this;
click_x = click_y = -1;
@@ -287,6 +312,7 @@ void ComplexButtonResource::init (Widget *widget)
layout = new Layout (createPlatform ());
setLayout (layout);
+ DBG_OBJ_ASSOC_CHILD (layout);
layout->setWidget (widget);
layout->connect (&layoutReceiver);
}
@@ -302,23 +328,38 @@ void ComplexButtonResource::setEmbed (Embed *embed)
ComplexButtonResource::~ComplexButtonResource ()
{
delete layout;
+ DBG_OBJ_DELETE ();
}
void ComplexButtonResource::sizeRequest (Requisition *requisition)
{
+ DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>");
+ DBG_OBJ_MSG_START ();
+
Requisition widgetRequisition;
childWidget->sizeRequest (&widgetRequisition);
requisition->width = widgetRequisition.width + 2 * reliefXThickness ();
requisition->ascent = widgetRequisition.ascent + reliefYThickness ();
requisition->descent = widgetRequisition.descent + reliefYThickness ();
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+ DBG_OBJ_MSG_END ();
}
void ComplexButtonResource::getExtremes (Extremes *extremes)
{
+ DBG_OBJ_MSG ("resize", 0, "<b>getExtremes</b>");
+ DBG_OBJ_MSG_START ();
+
Extremes widgetExtremes;
childWidget->getExtremes (&widgetExtremes);
extremes->minWidth = widgetExtremes.minWidth + 2 * reliefXThickness ();
extremes->maxWidth = widgetExtremes.maxWidth + 2 * reliefXThickness ();
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d / %d",
+ extremes->minWidth, extremes->maxWidth);
+ DBG_OBJ_MSG_END ();
}
void ComplexButtonResource::sizeAllocate (Allocation *allocation)
@@ -327,17 +368,32 @@ void ComplexButtonResource::sizeAllocate (Allocation *allocation)
void ComplexButtonResource::setWidth (int width)
{
+ DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width);
+ DBG_OBJ_MSG_START ();
+
childWidget->setWidth (width - 2 * reliefXThickness ());
+
+ DBG_OBJ_MSG_END ();
}
void ComplexButtonResource::setAscent (int ascent)
{
+ DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent);
+ DBG_OBJ_MSG_START ();
+
childWidget->setAscent (ascent - reliefYThickness ());
+
+ DBG_OBJ_MSG_END ();
}
void ComplexButtonResource::setDescent (int descent)
{
+ DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent);
+ DBG_OBJ_MSG_START ();
+
childWidget->setDescent (descent - reliefYThickness ());
+
+ DBG_OBJ_MSG_END ();
}
Iterator *ComplexButtonResource::iterator (Content::Type mask, bool atEnd)
diff --git a/dw/ui.hh b/dw/ui.hh
index d46705fe..75d69341 100644
--- a/dw/ui.hh
+++ b/dw/ui.hh
@@ -329,7 +329,8 @@ protected:
clickedEmitter.emitClicked (this, event); }
public:
- inline Resource () { embed = NULL; }
+ inline Resource ()
+ { embed = NULL; DBG_OBJ_CREATE ("dw::core::ui::Resource"); }
virtual ~Resource ();
diff --git a/dw/widget.cc b/dw/widget.cc
index 385bdb97..ba49c158 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;
@@ -106,7 +106,7 @@ Widget::~Widget ()
if (parent)
parent->removeChild (this);
- else
+ else if (layout)
layout->removeWidget ();
DBG_OBJ_DELETE ();
@@ -150,15 +150,17 @@ void Widget::setParent (Widget *parent)
buttonSensitive = parent->buttonSensitive;
DBG_OBJ_ASSOC_PARENT (parent);
-
//printf ("The %s %p becomes a child of the %s %p\n",
// getClassName(), this, parent->getClassName(), parent);
+
+ notifySetParent();
}
void Widget::queueDrawArea (int x, int y, int width, int height)
{
/** \todo Maybe only the intersection? */
- layout->queueDraw (x + allocation.x, y + allocation.y, width, height);
+ if (layout)
+ layout->queueDraw (x + allocation.x, y + allocation.y, width, height);
_MSG("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height);
}
@@ -167,40 +169,98 @@ void Widget::queueDrawArea (int x, int y, int width, int height)
*/
void Widget::queueResize (int ref, bool extremesChanged)
{
+ DBG_OBJ_MSGF ("resize", 0, "<b>queueResize</b> (%d, %s)",
+ ref, extremesChanged ? "true" : "false");
+ DBG_OBJ_MSG_START ();
+
+ // queueResize() can be called recursively; calls are queued, so
+ // that actualQueueResize() is clean.
+
+ if (queueResizeEntered ()) {
+ DBG_OBJ_MSG ("resize", 1, "put into queue");
+ layout->queueQueueResizeList->put (new Layout::QueueResizeItem
+ (this, ref, extremesChanged));
+ } else {
+ actualQueueResize (ref, extremesChanged);
+
+ while (layout != NULL && layout->queueQueueResizeList->size () > 0) {
+ Layout::QueueResizeItem *item = layout->queueQueueResizeList->get (0);
+ DBG_OBJ_MSGF ("resize", 1, "taken out of queue queue (size = %d)",
+ layout->queueQueueResizeList->size ());
+ item->widget->actualQueueResize (item->ref, item->extremesChanged);
+ layout->queueQueueResizeList->remove (0); // hopefully not too large
+ }
+ }
+
+ DBG_OBJ_MSG_END ();
+}
+
+void Widget::actualQueueResize (int ref, bool extremesChanged)
+{
+ assert (!queueResizeEntered ());
+
+ DBG_OBJ_MSGF ("resize", 0, "<b>actualQueueResize</b> (%d, %s)",
+ ref, extremesChanged ? "true" : "false");
+ DBG_OBJ_MSG_START ();
+
+ enterQueueResize ();
+
Widget *widget2, *child;
- //printf("The %stop-level %s %p with parentRef = %d has changed its size.\n",
- // parent ? "non-" : "", getClassName(), this, parentRef);
+ //printf("The %stop-level %s %p with parentRef = %d has changed its size. "
+ // "Layout = %p.\n",
+ // parent ? "non-" : "", getClassName(), this, parentRef, layout);
- setFlags (NEEDS_RESIZE);
- setFlags (NEEDS_ALLOCATE);
- markSizeChange (ref);
+ Flags resizeFlag, extremesFlag;
+ if (layout) {
+ // If RESIZE_QUEUED is set, this widget is already in the list.
+ if (!resizeQueued ())
+ layout->queueResizeList->put (this);
+
+ resizeFlag = RESIZE_QUEUED;
+ extremesFlag = EXTREMES_QUEUED;
+ } else {
+ resizeFlag = NEEDS_RESIZE;
+ extremesFlag = EXTREMES_CHANGED;
+ }
+
+ setFlags (resizeFlag);
+ setFlags (ALLOCATE_QUEUED);
+ markSizeChange (ref);
+
if (extremesChanged) {
- setFlags (EXTREMES_CHANGED);
+ setFlags (extremesFlag);
markExtremesChange (ref);
}
-
- for (widget2 = parent, child = this;
- widget2;
- child = widget2, widget2 = widget2->parent) {
- widget2->setFlags (NEEDS_RESIZE);
- widget2->markSizeChange (child->parentRef);
- widget2->setFlags (NEEDS_ALLOCATE);
-
- //printf (" Setting DW_NEEDS_RESIZE and NEEDS_ALLOCATE for the "
+
+ for (widget2 = parent, child = this; widget2;
+ child = widget2, widget2 = widget2->parent) {
+ //printf (" Setting %s and ALLOCATE_QUEUED for the "
// "%stop-level %s %p with parentRef = %d\n",
+ // resizeFlag == RESIZE_QUEUED ? "RESIZE_QUEUED" : "NEEDS_RESIZE",
// widget2->parent ? "non-" : "", widget2->getClassName(), widget2,
// widget2->parentRef);
+ if (layout && !widget2->resizeQueued ())
+ layout->queueResizeList->put (widget2);
+
+ widget2->setFlags (resizeFlag);
+ widget2->markSizeChange (child->parentRef);
+ widget2->setFlags (ALLOCATE_QUEUED);
+
if (extremesChanged) {
- widget2->setFlags (EXTREMES_CHANGED);
+ widget2->setFlags (extremesFlag);
widget2->markExtremesChange (child->parentRef);
}
}
if (layout)
layout->queueResize ();
+
+ leaveQueueResize ();
+
+ DBG_OBJ_MSG_END ();
}
@@ -210,6 +270,28 @@ void Widget::queueResize (int ref, bool extremesChanged)
*/
void Widget::sizeRequest (Requisition *requisition)
{
+ assert (!queueResizeEntered ());
+
+ DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>");
+ DBG_OBJ_MSG_START ();
+
+ enterSizeRequest ();
+
+ //printf ("The %stop-level %s %p with parentRef = %d: needsResize: %s, "
+ // "resizeQueued = %s\n",
+ // parent ? "non-" : "", getClassName(), this, parentRef,
+ // needsResize () ? "true" : "false",
+ // resizeQueued () ? "true" : "false");
+
+ if (resizeQueued ()) {
+ // This method is called outside of Layout::resizeIdle.
+ setFlags (NEEDS_RESIZE);
+ unsetFlags (RESIZE_QUEUED);
+ // The widget is not taken out of Layout::queueResizeList, since
+ // other *_QUEUED flags may still be set and processed in
+ // Layout::resizeIdle.
+ }
+
if (needsResize ()) {
/** \todo Check requisition == &(this->requisition) and do what? */
sizeRequestImpl (requisition);
@@ -221,6 +303,13 @@ void Widget::sizeRequest (Requisition *requisition)
DBG_OBJ_SET_NUM ("requisition.descent", requisition->descent);
} else
*requisition = this->requisition;
+
+ //printf (" ==> Result: %d x (%d + %d)\n",
+ // requisition->width, requisition->ascent, requisition->descent);
+
+ leaveSizeRequest ();
+
+ DBG_OBJ_MSG_END ();
}
/**
@@ -228,6 +317,22 @@ void Widget::sizeRequest (Requisition *requisition)
*/
void Widget::getExtremes (Extremes *extremes)
{
+ assert (!queueResizeEntered ());
+
+ DBG_OBJ_MSG ("resize", 0, "<b>getExtremes</b>");
+ DBG_OBJ_MSG_START ();
+
+ enterGetExtremes ();
+
+ if (extremesQueued ()) {
+ // This method is called outside of Layout::resizeIdle.
+ setFlags (EXTREMES_CHANGED);
+ unsetFlags (EXTREMES_QUEUED);
+ // The widget is not taken out of Layout::queueResizeList, since
+ // other *_QUEUED flags may still be set and processed in
+ // Layout::resizeIdle.
+ }
+
if (extremesChanged ()) {
getExtremesImpl (extremes);
this->extremes = *extremes;
@@ -237,6 +342,10 @@ void Widget::getExtremes (Extremes *extremes)
DBG_OBJ_SET_NUM ("extremes.maxWidth", extremes->maxWidth);
} else
*extremes = this->extremes;
+
+ leaveGetExtremes ();
+
+ DBG_OBJ_MSG_END ();
}
/**
@@ -245,6 +354,28 @@ void Widget::getExtremes (Extremes *extremes)
*/
void Widget::sizeAllocate (Allocation *allocation)
{
+ assert (!queueResizeEntered ());
+ assert (!sizeRequestEntered ());
+ assert (!getExtremesEntered ());
+ assert (resizeIdleEntered ());
+
+ DBG_OBJ_MSGF ("resize", 0, "<b>sizeAllocate</b> (%d, %d; %d * (%d + %d))",
+ allocation->x, allocation->y, allocation->width,
+ allocation->ascent, allocation->descent);
+ DBG_OBJ_MSG_START ();
+
+ enterSizeAllocate ();
+
+ /*printf ("The %stop-level %s %p is allocated:\n",
+ parent ? "non-" : "", getClassName(), this);
+ printf (" old = (%d, %d, %d + (%d + %d))\n",
+ this->allocation.x, this->allocation.y, this->allocation.width,
+ this->allocation.ascent, this->allocation.descent);
+ printf (" new = (%d, %d, %d + (%d + %d))\n",
+ allocation->x, allocation->y, allocation->width, allocation->ascent,
+ allocation->descent);
+ printf (" NEEDS_ALLOCATE = %s\n", needsAllocate () ? "true" : "false");*/
+
if (needsAllocate () ||
allocation->x != this->allocation.x ||
allocation->y != this->allocation.y ||
@@ -285,6 +416,10 @@ void Widget::sizeAllocate (Allocation *allocation)
}
/*unsetFlags (NEEDS_RESIZE);*/
+
+ leaveSizeAllocate ();
+
+ DBG_OBJ_MSG_END ();
}
bool Widget::buttonPress (EventButton *event)
@@ -507,6 +642,25 @@ int Widget::getLevel ()
}
/**
+ * \brief Get the level of the widget within the tree, regarting the
+ * generators, not the parents.
+ *
+ * The root widget has the level 0.
+ */
+int Widget::getGeneratorLevel ()
+{
+ Widget *widget = this;
+ int level = 0;
+
+ while (widget->getGenerator ()) {
+ level++;
+ widget = widget->getGenerator ();
+ }
+
+ return level;
+}
+
+/**
* \brief Get the widget with the highest level, which is a direct ancestor of
* widget1 and widget2.
*/
@@ -566,7 +720,9 @@ Widget *Widget::getWidgetAtPoint (int x, int y, int level)
* is such a child, it is returned. Otherwise, this widget is returned.
*/
childAtPoint = NULL;
- it = iterator (Content::WIDGET, false);
+ it = iterator ((Content::Type)
+ (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT),
+ false);
while (childAtPoint == NULL && it->next ())
childAtPoint = it->getContent()->widget->getWidgetAtPoint (x, y,
@@ -627,6 +783,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..e7ac7f55 100644
--- a/dw/widget.hh
+++ b/dw/widget.hh
@@ -27,23 +27,46 @@ class Widget: public lout::identity::IdentifiableObject
protected:
enum Flags {
/**
+ * \todo Comment this.
+ */
+ RESIZE_QUEUED = 1 << 0,
+
+ /**
+ * \todo Comment this.
+ */
+ EXTREMES_QUEUED = 1 << 1,
+
+ /**
* \brief Set, when dw::core::Widget::requisition is not up to date
* anymore.
+ *
+ * \todo Update, see RESIZE_QUEUED.
*/
- NEEDS_RESIZE = 1 << 0,
+ NEEDS_RESIZE = 1 << 2,
/**
* \brief Only used internally, set to enforce size allocation.
*
- * (I've forgotten the case, for which this is necessary.)
+ * In some cases, the size of a widget remains the same, but the
+ * children are allocated at different positions and in
+ * different sizes, so that a simple comparison of old and new
+ * allocation is insufficient. Therefore, this flag is set
+ * (indirectly, as ALLOCATE_QUEUED) in queueResize.
*/
- NEEDS_ALLOCATE = 1 << 1,
+ NEEDS_ALLOCATE = 1 << 3,
+
+ /**
+ * \todo Comment this.
+ */
+ ALLOCATE_QUEUED = 1 << 4,
/**
* \brief Set, when dw::core::Widget::extremes is not up to date
* anymore.
+ *
+ * \todo Update, see RESIZE_QUEUED.
*/
- EXTREMES_CHANGED = 1 << 2,
+ EXTREMES_CHANGED = 1 << 5,
/**
* \brief Set by the widget itself (in the constructor), when set...
@@ -51,7 +74,7 @@ protected:
*
* Will hopefully be removed, after redesigning the size model.
*/
- USES_HINTS = 1 << 3,
+ USES_HINTS = 1 << 6,
/**
* \brief Set by the widget itself (in the constructor), when it contains
@@ -59,19 +82,19 @@ protected:
*
* Will hopefully be removed, after redesigning the size model.
*/
- HAS_CONTENTS = 1 << 4,
+ HAS_CONTENTS = 1 << 7,
/**
* \brief Set, when a widget was already once allocated,
*
* The dw::Image widget uses this flag, see dw::Image::setBuffer.
*/
- WAS_ALLOCATED = 1 << 5,
+ WAS_ALLOCATED = 1 << 8,
/**
* \brief Set for block-level widgets (as opposed to inline widgets)
*/
- BLOCK_LEVEL = 1 << 6,
+ BLOCK_LEVEL = 1 << 9,
};
/**
@@ -101,6 +124,14 @@ private:
* \brief The parent widget, NULL for top-level widgets.
*/
Widget *parent;
+
+ /**
+ * \brief The generating widget, NULL for top-level widgets, or if
+ * not set; in the latter case, the effective generator (see
+ * getGenerator) is the parent.
+ */
+ Widget *generator;
+
style::Style *style;
Flags flags;
@@ -133,6 +164,8 @@ private:
*/
bool buttonSensitiveSet;
+ void actualQueueResize (int ref, bool extremesChanged);
+
public:
/**
* \brief This value is defined by the parent widget, and used for
@@ -200,6 +233,9 @@ protected:
*/
virtual void markExtremesChange (int ref);
+ virtual void notifySetAsTopLevel();
+ virtual void notifySetParent();
+
virtual bool buttonPressImpl (EventButton *event);
virtual bool buttonReleaseImpl (EventButton *event);
virtual bool motionNotifyImpl (EventMotion *event);
@@ -216,7 +252,7 @@ protected:
{ layout->changeAnchor (this, name, y); }
inline void removeAnchor (char* name)
- { layout->removeAnchor (this, name); }
+ { if (layout) layout->removeAnchor (this, name); }
//inline void updateBgColor () { layout->updateBgColor (); }
@@ -249,14 +285,37 @@ public:
inline void setDeleteCallback(DW_Callback_t func, void *data)
{ deleteCallbackFunc = func; deleteCallbackData = data; }
+private:
+ bool resizeIdleEntered () { return layout && layout->resizeIdleCounter; }
+
+ void enterQueueResize () { if (layout) layout->queueResizeCounter++; }
+ void leaveQueueResize () { if (layout) layout->queueResizeCounter--; }
+ bool queueResizeEntered () { return layout && layout->queueResizeCounter; }
+
+ void enterSizeAllocate () { if (layout) layout->sizeAllocateCounter++; }
+ void leaveSizeAllocate () { if (layout) layout->sizeAllocateCounter--; }
+ bool sizeAllocateEntered () { return layout && layout->sizeAllocateCounter; }
+
+ void enterSizeRequest () { if (layout) layout->sizeRequestCounter++; }
+ void leaveSizeRequest () { if (layout) layout->sizeRequestCounter--; }
+ bool sizeRequestEntered () { return layout && layout->sizeRequestCounter; }
+
+ void enterGetExtremes () { if (layout) layout->getExtremesCounter++; }
+ void leaveGetExtremes () { if (layout) layout->getExtremesCounter--; }
+ bool getExtremesEntered () { return layout && layout->getExtremesCounter; }
+
+
public:
static int CLASS_ID;
Widget ();
~Widget ();
+ inline bool resizeQueued () { return flags & RESIZE_QUEUED; }
+ inline bool extremesQueued () { return flags & EXTREMES_QUEUED; }
inline bool needsResize () { return flags & NEEDS_RESIZE; }
inline bool needsAllocate () { return flags & NEEDS_ALLOCATE; }
+ inline bool allocateQueued () { return flags & ALLOCATE_QUEUED; }
inline bool extremesChanged () { return flags & EXTREMES_CHANGED; }
inline bool wasAllocated () { return flags & WAS_ALLOCATED; }
inline bool usesHints () { return flags & USES_HINTS; }
@@ -265,6 +324,8 @@ public:
void setParent (Widget *parent);
+ void setGenerator (Widget *generator) { this->generator = generator; }
+
inline style::Style *getStyle () { return style; }
/** \todo I do not like this. */
inline Allocation *getAllocation () { return &allocation; }
@@ -302,8 +363,11 @@ public:
inline Widget *getParent () { return parent; }
Widget *getTopLevel ();
int getLevel ();
+ int getGeneratorLevel ();
Widget *getNearestCommonAncestor (Widget *otherWidget);
+ inline Widget *getGenerator () { return generator ? generator : parent; }
+
inline Layout *getLayout () { return layout; }
virtual Widget *getWidgetAtPoint (int x, int y, int level);
diff --git a/lout/Makefile.am b/lout/Makefile.am
index bef9696e..f2219360 100644
--- a/lout/Makefile.am
+++ b/lout/Makefile.am
@@ -1,5 +1,6 @@
AM_CPPFLAGS = \
- -I$(top_srcdir)
+ -I$(top_srcdir) \
+ -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/lout"'
noinst_LIBRARIES = liblout.a
diff --git a/lout/container.cc b/lout/container.cc
index d3385137..db1cc560 100644
--- a/lout/container.cc
+++ b/lout/container.cc
@@ -22,6 +22,7 @@
#include "container.hh"
#include "misc.hh"
+#include "debug.hh"
namespace lout {
@@ -103,6 +104,8 @@ void Collection::intoStringBuffer(misc::StringBuffer *sb)
Vector::Vector(int initSize, bool ownerOfObjects)
{
+ DBG_OBJ_CREATE ("lout::container::untyped::Vector");
+
numAlloc = initSize == 0 ? 1 : initSize;
this->ownerOfObjects = ownerOfObjects;
numElements = 0;
@@ -113,6 +116,8 @@ Vector::~Vector()
{
clear();
free(array);
+
+ DBG_OBJ_DELETE ();
}
void Vector::put(Object *newElement, int newPos)
@@ -188,9 +193,10 @@ void Vector::remove(int pos)
/**
* Sort the elements in the vector. Assumes that all elements are Comparable's.
*/
-void Vector::sort()
+void Vector::sort(Comparator *comparator)
{
- qsort (array, numElements, sizeof(Object*), Comparable::compareFun);
+ Comparator::compareFunComparator = comparator;
+ qsort (array, numElements, sizeof(Object*), Comparator::compareFun);
}
/**
@@ -202,44 +208,58 @@ void Vector::sort()
* size of the array. (This is the value which can be used for
* insertion; see insertSortet()).
*/
-int Vector::bsearch(Object *key, bool mustExist)
+int Vector::bsearch(Object *key, bool mustExist, int start, int end,
+ Comparator *comparator)
{
// The case !mustExist is not handled by bsearch(3), so here is a
// new implementation.
- if (numElements == 0)
- return mustExist ? -1 : 0;
-
- int high = numElements - 1, low = 0;
-
- while (true) {
- int index = (low + high) / 2;
- int c = ((Comparable*) key)->compareTo ((Comparable*)array[index]);
- if (c == 0)
- return index;
- else {
- if (low >= high) {
- if (mustExist)
- return -1;
+
+ DBG_OBJ_MSGF ("container", 0,
+ "<b>bsearch</b> (<i>key</i>, %s, %d, %d, <i>comparator</i>) "
+ "[size is %d]",
+ mustExist ? "true" : "false", start, end, size ());
+ DBG_OBJ_MSG_START ();
+
+ int result = -123; // Compiler happiness: GCC 4.7 does not handle this?
+
+ if (start > end) {
+ DBG_OBJ_MSG ("container", 1, "empty");
+ result = mustExist ? -1 : start;
+ } else {
+ int low = start, high = end;
+ bool found = false;
+
+ while (!found) {
+ int index = (low + high) / 2;
+ int c = comparator->compare (key, array[index]);
+ DBG_OBJ_MSGF ("container", 1,
+ "searching within %d and %d; compare key with #%d => %d",
+ low, high, index, c);
+ if (c == 0) {
+ found = true;
+ result = index;
+ } else {
+ if (low >= high) {
+ if (mustExist) {
+ found = true;
+ result = -1;
+ } else {
+ found = true;
+ result = c > 0 ? index + 1 : index;
+ }
+ }
+
+ if (c < 0)
+ high = index - 1;
else
- return c > 0 ? index + 1 : index;
+ low = index + 1;
}
-
- if (c < 0)
- high = index - 1;
- else
- low = index + 1;
}
- }
-
+ }
- /*
- void *result = ::bsearch (&key, array, numElements, sizeof (Object*),
- Comparable::compareFun);
- if (result)
- return (Object**)result - array;
- else
- return -1;
- */
+ DBG_OBJ_MSGF ("container", 1, "result = %d", result);
+ DBG_OBJ_MSG_END ();
+ return result;
}
Object *Vector::VectorIterator::getNext()
diff --git a/lout/container.hh b/lout/container.hh
index 14803140..03800efd 100644
--- a/lout/container.hh
+++ b/lout/container.hh
@@ -137,16 +137,23 @@ public:
* Notice that insertion is not very efficient, unless the position
* is rather at the end.
*/
- inline void insertSorted(object::Object *newElement)
- { insert (newElement, bsearch (newElement, false)); }
+ inline void insertSorted(object::Object *newElement,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { insert (newElement, bsearch (newElement, false, comparator)); }
void remove(int pos);
inline object::Object *get(int pos) const
{ return (pos >= 0 && pos < numElements) ? array[pos] : NULL; }
inline int size() { return numElements; }
void clear();
- void sort();
- int bsearch(Object *key, bool mustExist);
+ void sort(object::Comparator *comparator = &object::standardComparator);
+ int bsearch(Object *key, bool mustExist, int start, int end,
+ object::Comparator *comparator = &object::standardComparator);
+ inline int bsearch(Object *key, bool mustExist,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { return bsearch (key, mustExist, 0, size () - 1, comparator); }
};
@@ -406,16 +413,28 @@ public:
{ ((untyped::Vector*)this->base)->put(newElement, newPos); }
inline void insert(T *newElement, int pos)
{ ((untyped::Vector*)this->base)->insert(newElement, pos); }
- inline void insertSorted(T *newElement)
- { ((untyped::Vector*)this->base)->insertSorted(newElement); }
+ inline void insertSorted(T *newElement,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { ((untyped::Vector*)this->base)->insertSorted(newElement, comparator); }
inline void remove(int pos) { ((untyped::Vector*)this->base)->remove(pos); }
inline T *get(int pos) const
{ return (T*)((untyped::Vector*)this->base)->get(pos); }
inline int size() const { return ((untyped::Vector*)this->base)->size(); }
inline void clear() { ((untyped::Vector*)this->base)->clear(); }
- inline void sort() { ((untyped::Vector*)this->base)->sort(); }
- inline int bsearch(T *key, bool mustExist)
- { return ((untyped::Vector*)this->base)->bsearch(key, mustExist); }
+ inline void sort(object::Comparator *comparator =
+ &object::standardComparator)
+ { ((untyped::Vector*)this->base)->sort(comparator); }
+ inline int bsearch(T *key, bool mustExist, int start, int end,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { return ((untyped::Vector*)this->base)->bsearch(key, mustExist, start, end,
+ comparator); }
+ inline int bsearch(T *key, bool mustExist,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { return ((untyped::Vector*)this->base)->bsearch(key, mustExist,
+ comparator); }
};
diff --git a/lout/debug.hh b/lout/debug.hh
index 083234f8..a2393470 100644
--- a/lout/debug.hh
+++ b/lout/debug.hh
@@ -17,6 +17,8 @@
#define D_STMT_START do
#define D_STMT_END while (0)
+#define D_STMT_NOP D_STMT_START { } D_STMT_END
+
# ifdef DEBUG_LEVEL
# define DEBUG_MSG(level, ...) \
D_STMT_START { \
@@ -38,10 +40,12 @@
#include <unistd.h>
#include <stdio.h>
+#define DBG_IF_RTFL if(1)
+
// "\n" at the beginning just in case that the previous line is not finished
// yet.
#define RTFL_PREFIX_FMT "\n[rtfl]%s:%d:%d:"
-#define RTFL_PREFIX_ARGS __FILE__, __LINE__, getpid()
+#define RTFL_PREFIX_ARGS CUR_WORKING_DIR "/" __FILE__, __LINE__, getpid()
#define DBG_OBJ_MSG(aspect, prio, msg) \
D_STMT_START { \
@@ -50,6 +54,15 @@
fflush (stdout); \
} D_STMT_END
+// Variant which does not use "this", but an explicitly passed
+// object. Should be applied to other macros. (Also, for use in C.)
+#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:%s\n", \
+ RTFL_PREFIX_ARGS, obj, aspect, prio, msg); \
+ fflush (stdout); \
+ } D_STMT_END
+
#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) \
D_STMT_START { \
printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:" fmt "\n", \
@@ -57,6 +70,14 @@
fflush (stdout); \
} D_STMT_END
+// See DBG_OBJ_MSG_O.
+#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-msg:%p:%s:%d:" fmt "\n", \
+ RTFL_PREFIX_ARGS, obj, aspect, prio, __VA_ARGS__); \
+ fflush (stdout); \
+ } D_STMT_END
+
#define DBG_OBJ_MSG_START() \
D_STMT_START { \
printf (RTFL_PREFIX_FMT "obj-msg-start:%p\n", \
@@ -64,6 +85,14 @@
fflush (stdout); \
} D_STMT_END
+// See DBG_OBJ_MSG_O.
+#define DBG_OBJ_MSG_START_O(obj) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-msg-start:%p\n", \
+ RTFL_PREFIX_ARGS, obj); \
+ fflush (stdout); \
+ } D_STMT_END
+
#define DBG_OBJ_MSG_END() \
D_STMT_START { \
printf (RTFL_PREFIX_FMT "obj-msg-end:%p\n", \
@@ -71,6 +100,14 @@
fflush (stdout); \
} D_STMT_END
+// See DBG_OBJ_MSG_O.
+#define DBG_OBJ_MSG_END_O(obj) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-msg-end:%p\n", \
+ RTFL_PREFIX_ARGS, obj); \
+ fflush (stdout); \
+ } D_STMT_END
+
#define DBG_OBJ_CREATE(klass) \
D_STMT_START { \
printf (RTFL_PREFIX_FMT "obj-create:%p:%s\n", \
@@ -124,13 +161,27 @@
fflush (stdout); \
} D_STMT_END
-#define DBG_OBJ_SET_STR(var, val) \
+#define DBG_OBJ_SET_NUM_O(obj, var, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%d\n", \
+ RTFL_PREFIX_ARGS, obj, var, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_SET_SYM(var, val) \
D_STMT_START { \
printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \
RTFL_PREFIX_ARGS, this, var, val); \
fflush (stdout); \
} D_STMT_END
+#define DBG_OBJ_SET_STR(var, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s:\"%s\"\n", \
+ RTFL_PREFIX_ARGS, this, var, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
#define DBG_OBJ_SET_PTR(var, val) \
D_STMT_START { \
printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%p\n", \
@@ -140,22 +191,57 @@
#define DBG_OBJ_ARRSET_NUM(var, ind, val) \
D_STMT_START { \
- printf (RTFL_PREFIX_FMT "obj-set:%p:" var ".%d:%d\n", \
- RTFL_PREFIX_ARGS, this, ind, val); \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%d\n", \
+ RTFL_PREFIX_ARGS, this, var, ind, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ARRSET_SYM(var, ind, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%s\n", \
+ RTFL_PREFIX_ARGS, this, var, ind, val); \
fflush (stdout); \
} D_STMT_END
#define DBG_OBJ_ARRSET_STR(var, ind, val) \
D_STMT_START { \
- printf (RTFL_PREFIX_FMT "obj-set:%p:" var ".%d:%s\n", \
- RTFL_PREFIX_ARGS, this, ind, val); \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:\"%s\"\n", \
+ RTFL_PREFIX_ARGS, this, var, ind, val); \
fflush (stdout); \
} D_STMT_END
#define DBG_OBJ_ARRSET_PTR(var, ind, val) \
D_STMT_START { \
- printf (RTFL_PREFIX_FMT "obj-set:%p:" var ".%d:%p\n", \
- RTFL_PREFIX_ARGS, this, ind, val); \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%p\n", \
+ RTFL_PREFIX_ARGS, this, var, ind, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%d\n", \
+ RTFL_PREFIX_ARGS, this, var, ind, attr, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%s\n", \
+ RTFL_PREFIX_ARGS, this, var, ind, attr, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:\"%s\"\n", \
+ RTFL_PREFIX_ARGS, this, var, ind, attr, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d.%s:%p\n", \
+ RTFL_PREFIX_ARGS, this, var, ind, attr, val); \
fflush (stdout); \
} D_STMT_END
@@ -168,23 +254,36 @@
#else /* DBG_RTFL */
-#define DBG_OBJ_MSG(aspect, prio, msg)
-#define DBG_OBJ_MSGF(aspect, prio, fmt, ...)
-#define DBG_OBJ_MSG_START()
-#define DBG_OBJ_MSG_END()
-#define DBG_OBJ_CREATE(klass)
-#define DBG_OBJ_DELETE()
-#define DBG_OBJ_BASECLASS(klass)
-#define DBG_OBJ_ASSOC_PARENT(parent)
-#define DBG_OBJ_ASSOC_CHILD(child)
-#define DBG_OBJ_ASSOC(parent, child)
-#define DBG_OBJ_SET_NUM(var, val)
-#define DBG_OBJ_SET_STR(var, val)
-#define DBG_OBJ_SET_PTR(var, val)
-#define DBG_OBJ_ARRSET_NUM(var, ind, val)
-#define DBG_OBJ_ARRSET_STR(var, ind, val)
-#define DBG_OBJ_ARRSET_PTR(var, ind, val)
-#define DBG_OBJ_COLOR(klass, color)
+#define DBG_IF_RTFL if(0)
+
+#define DBG_OBJ_MSG(aspect, prio, msg) D_STMT_NOP
+#define DBG_OBJ_MSG_O(aspect, prio, obj, msg) D_STMT_NOP
+#define DBG_OBJ_MSGF(aspect, prio, fmt, ...) D_STMT_NOP
+#define DBG_OBJ_MSGF_O(aspect, prio, obj, fmt, ...) D_STMT_NOP
+#define DBG_OBJ_MSG_START() D_STMT_NOP
+#define DBG_OBJ_MSG_START_O(obj) D_STMT_NOP
+#define DBG_OBJ_MSG_END() D_STMT_NOP
+#define DBG_OBJ_MSG_END_O(obj) D_STMT_NOP
+#define DBG_OBJ_CREATE(klass) D_STMT_NOP
+#define DBG_OBJ_DELETE() D_STMT_NOP
+#define DBG_OBJ_BASECLASS(klass) D_STMT_NOP
+#define DBG_OBJ_ASSOC_PARENT(parent) D_STMT_NOP
+#define DBG_OBJ_ASSOC_CHILD(child) D_STMT_NOP
+#define DBG_OBJ_ASSOC(parent, child) D_STMT_NOP
+#define DBG_OBJ_SET_NUM(var, val) D_STMT_NOP
+#define DBG_OBJ_SET_NUM_O(obj, var, val) D_STMT_NOP
+#define DBG_OBJ_SET_SYM(var, val) D_STMT_NOP
+#define DBG_OBJ_SET_STR(var, val) D_STMT_NOP
+#define DBG_OBJ_SET_PTR(var, val) D_STMT_NOP
+#define DBG_OBJ_ARRSET_NUM(var, ind, val) D_STMT_NOP
+#define DBG_OBJ_ARRSET_SYM(var, ind, val) D_STMT_NOP
+#define DBG_OBJ_ARRSET_STR(var, ind, val) D_STMT_NOP
+#define DBG_OBJ_ARRSET_PTR(var, ind, val) D_STMT_NOP
+#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val) D_STMT_NOP
+#define DBG_OBJ_ARRATTRSET_SYM(var, ind, attr, val) D_STMT_NOP
+#define DBG_OBJ_ARRATTRSET_STR(var, ind, attr, val) D_STMT_NOP
+#define DBG_OBJ_ARRATTRSET_PTR(var, ind, attr, val) D_STMT_NOP
+#define DBG_OBJ_COLOR(klass, color) D_STMT_NOP
#endif /* DBG_RTFL */
diff --git a/lout/identity.cc b/lout/identity.cc
index 6fe679b4..61f59ace 100644
--- a/lout/identity.cc
+++ b/lout/identity.cc
@@ -17,8 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-
#include "identity.hh"
#include <stdio.h>
@@ -41,6 +39,22 @@ IdentifiableObject::Class::Class (IdentifiableObject::Class *parent, int id,
this->className = className;
}
+void IdentifiableObject::Class::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append ("<class ");
+ sb->append (className);
+ sb->append (" (");
+ sb->appendInt (id);
+ sb->append (")");
+
+ if (parent) {
+ sb->append (", parent: ");
+ parent->intoStringBuffer (sb);
+ }
+
+ sb->append (">");
+}
+
HashTable <ConstString, IdentifiableObject::Class>
*IdentifiableObject::classesByName =
new HashTable<ConstString, IdentifiableObject::Class> (true, true);
@@ -55,7 +69,9 @@ IdentifiableObject::IdentifiableObject ()
void IdentifiableObject::intoStringBuffer(misc::StringBuffer *sb)
{
- sb->append("<instance of ");
+ sb->append("<instance ");
+ sb->appendPointer(this);
+ sb->append(" of ");
sb->append(getClassName());
sb->append(">");
}
@@ -78,6 +94,7 @@ void IdentifiableObject::registerName (const char *className, int *classId)
}
this->classId = klass->id;
+ *classId = klass->id;
currentlyConstructedClass = klass;
}
diff --git a/lout/identity.hh b/lout/identity.hh
index 1f0b4bdf..df42b204 100644
--- a/lout/identity.hh
+++ b/lout/identity.hh
@@ -106,6 +106,8 @@ private:
const char *className;
Class (Class *parent, int id, const char *className);
+
+ void intoStringBuffer(misc::StringBuffer *sb);
};
static container::typed::HashTable <object::ConstString,
@@ -121,7 +123,7 @@ protected:
public:
IdentifiableObject ();
- virtual void intoStringBuffer(misc::StringBuffer *sb);
+ void intoStringBuffer(misc::StringBuffer *sb);
/**
* \brief Returns the class identifier.
diff --git a/lout/misc.hh b/lout/misc.hh
index 6a04c89a..3082f33c 100644
--- a/lout/misc.hh
+++ b/lout/misc.hh
@@ -421,14 +421,17 @@ public:
*/
inline T* getRef (int i) const
{
- if (this->startExtra == -1)
+ if (this->startExtra == -1) {
+ assert (i >= 0 && i < this->numMain);
return this->arrayMain + i;
- else {
- if (i < this->startExtra)
+ } else {
+ if (i < this->startExtra) {
+ assert (i >= 0);
return this->arrayMain + i;
- else if (i >= this->startExtra + this->numExtra)
+ } else if (i >= this->startExtra + this->numExtra) {
+ assert (i < this->numMain + this->numExtra);
return this->arrayMain + i - this->numExtra;
- else
+ } else
return this->arrayExtra1 + i - this->startExtra;
}
}
@@ -515,6 +518,11 @@ public:
* about memory management.
*/
inline void append(const char *str) { appendNoCopy(strdup(str)); }
+ inline void appendInt(int n)
+ { char buf[32]; sprintf (buf, "%d", n); append (buf); }
+ inline void appendPointer(void *p)
+ { char buf[32]; sprintf (buf, "%p", p); append (buf); }
+ inline void appendBool(bool b) { append (b ? "true" : "false"); }
void appendNoCopy(char *str);
const char *getChars();
void clear ();
diff --git a/lout/object.cc b/lout/object.cc
index 99b5902d..74328d22 100644
--- a/lout/object.cc
+++ b/lout/object.cc
@@ -94,7 +94,9 @@ const char *Object::toString()
*/
void Object::intoStringBuffer(misc::StringBuffer *sb)
{
- sb->append("<not further specified object>");
+ sb->append("<not further specified object ");
+ sb->appendPointer(this);
+ sb->append(">");
}
/**
@@ -107,29 +109,44 @@ size_t Object::sizeOf()
}
// ----------------
-// Comparable
+// Comparator
// ----------------
+Comparator *Comparator::compareFunComparator = NULL;
+
/**
* \brief This static method may be used as compare function for
* qsort(3) and bsearch(3), for an array of Object* (Object*[] or
* Object**).
+ *
+ * "compareFunComparator" should be set before.
+ *
+ * \todo Not reentrant. Consider switching to reentrant variants
+ * (qsort_r), and compare function with an additional argument.
*/
-int Comparable::compareFun(const void *p1, const void *p2)
+int Comparator::compareFun(const void *p1, const void *p2)
{
- Comparable *c1 = *(Comparable**)p1;
- Comparable *c2 = *(Comparable**)p2;
+ return compareFunComparator->compare (*(Object**)p1, *(Object**)p2);
+}
+
+// ------------------------
+// StandardComparator
+// ------------------------
- if (c1 && c2)
- return ((c1)->compareTo(c2));
- else if (c1)
+int StandardComparator::compare(Object *o1, Object *o2)
+{
+ if (o1 && o2)
+ return ((Comparable*)o1)->compareTo ((Comparable*)o2);
+ else if (o1)
return 1;
- else if (c2)
+ else if (o2)
return -1;
else
return 0;
}
+StandardComparator standardComparator;
+
// -------------
// Pointer
// -------------
diff --git a/lout/object.hh b/lout/object.hh
index fd612863..5a4935c5 100644
--- a/lout/object.hh
+++ b/lout/object.hh
@@ -42,10 +42,11 @@ class Comparable: public Object
{
public:
/**
- * \brief Compare two objects c1 and c2.
+ * \brief Compare two objects, this and other.
*
- * Return a value < 0, when c1 is less than c2, a value > 0, when c1
- * is greater than c2, or 0, when c1 and c2 are equal.
+ * Return a value < 0, when this is less than other, a value > 0,
+ * when this is greater than other, or 0, when this and other are
+ * equal.
*
* If c1.equals(c2) (as defined in Object), c1.compareTo(c2) must
* be 0, but, unlike you may expect, the reversed is not
@@ -55,10 +56,43 @@ public:
* care about.
*/
virtual int compareTo(Comparable *other) = 0;
+};
+
+/**
+ * \brief Used for other orders as the one defined by Comparable.
+ *
+ * Compared objects must not neccessary be instances of Comparable.
+ */
+class Comparator: public Object
+{
+public:
+ /**
+ * \brief Compare two objects o1 and o2.
+ *
+ * Return a value < 0, when o1 is less than o2, a value > 0, when o1
+ * is greater than o2, or 0, when o1 and o2 are equal.
+ *
+ * If o1.equals(o2) (as defined in Object), compare(o1, o2) must be
+ * 0, but, unlike you may expect, the reversed is not necessarily
+ * true. This method returns 0, if, according to the rules for
+ * sorting, there is no difference, but there may still be
+ * differences (not relevant for sorting), which "equals" will care
+ * about.
+ */
+ virtual int compare(Object *o1, Object *o2) = 0;
+ static Comparator *compareFunComparator;
static int compareFun(const void *p1, const void *p2);
};
+class StandardComparator: public Comparator
+{
+public:
+ int compare(Object *o1, Object *o2);
+};
+
+extern StandardComparator standardComparator;
+
/**
* \brief An object::Object wrapper for void pointers.
*/
diff --git a/src/Makefile.am b/src/Makefile.am
index 65a42cad..16398f21 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,7 +2,9 @@ AM_CPPFLAGS= \
-I$(top_srcdir) \
-DDILLO_SYSCONF='"$(sysconfdir)/"' \
-DDILLO_DOCDIR='"$(docdir)/"' \
+ -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/src"'
@LIBJPEG_CPPFLAGS@
+
AM_CFLAGS = @LIBPNG_CFLAGS@
AM_CXXFLAGS = @LIBPNG_CFLAGS@ @LIBFLTK_CXXFLAGS@
diff --git a/src/cssparser.cc b/src/cssparser.cc
index 369dd67f..f567c333 100644
--- a/src/cssparser.cc
+++ b/src/cssparser.cc
@@ -72,6 +72,10 @@ static const char *const Css_border_width_enum_vals[] = {
"thin", "medium", "thick", NULL
};
+static const char *const Css_clear_enum_vals[] = {
+ "left", "right", "both", "none", NULL
+};
+
static const char *const Css_cursor_enum_vals[] = {
"crosshair", "default", "pointer", "move", "e-resize", "ne-resize",
"nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize",
@@ -84,6 +88,10 @@ static const char *const Css_display_enum_vals[] = {
"table-cell", NULL
};
+static const char *const Css_float_enum_vals[] = {
+ "none", "left", "right", NULL
+};
+
static const char *const Css_font_size_enum_vals[] = {
"large", "larger", "medium", "small", "smaller", "xx-large", "xx-small",
"x-large", "x-small", NULL
@@ -121,6 +129,10 @@ static const char *const Css_list_style_type_enum_vals[] = {
"katakana-iroha", "none", NULL
};
+static const char *const Css_position_enum_vals[] = {
+ "static", "relative", "absolute", "fixed", NULL
+};
+
static const char *const Css_text_align_enum_vals[] = {
"left", "right", "center", "justify", "string", NULL
};
@@ -182,9 +194,9 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
Css_border_style_enum_vals},
{"border-top-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
Css_border_width_enum_vals},
- {"bottom", {CSS_TYPE_UNUSED}, NULL},
+ {"bottom", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
{"caption-side", {CSS_TYPE_UNUSED}, NULL},
- {"clear", {CSS_TYPE_UNUSED}, NULL},
+ {"clear", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_clear_enum_vals},
{"clip", {CSS_TYPE_UNUSED}, NULL},
{"color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
{"content", {CSS_TYPE_STRING, CSS_TYPE_UNUSED}, NULL},
@@ -194,7 +206,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
{"direction", {CSS_TYPE_UNUSED}, NULL},
{"display", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_display_enum_vals},
{"empty-cells", {CSS_TYPE_UNUSED}, NULL},
- {"float", {CSS_TYPE_UNUSED}, NULL},
+ {"float", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_float_enum_vals},
{"font-family", {CSS_TYPE_SYMBOL, CSS_TYPE_UNUSED}, NULL},
{"font-size", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED},
Css_font_size_enum_vals},
@@ -239,9 +251,9 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
{"padding-left", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
{"padding-right", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
{"padding-top", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
- {"position", {CSS_TYPE_UNUSED}, NULL},
+ {"position", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_position_enum_vals},
{"quotes", {CSS_TYPE_UNUSED}, NULL},
- {"right", {CSS_TYPE_UNUSED}, NULL},
+ {"right", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
{"text-align", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_text_align_enum_vals},
{"text-decoration", {CSS_TYPE_MULTI_ENUM, CSS_TYPE_UNUSED},
Css_text_decoration_enum_vals},
@@ -249,7 +261,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
{"text-shadow", {CSS_TYPE_UNUSED}, NULL},
{"text-transform", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
Css_text_transform_enum_vals},
- {"top", {CSS_TYPE_UNUSED}, NULL},
+ {"top", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
{"unicode-bidi", {CSS_TYPE_UNUSED}, NULL},
{"vertical-align",{CSS_TYPE_ENUM, CSS_TYPE_UNUSED},Css_vertical_align_vals},
{"visibility", {CSS_TYPE_UNUSED}, NULL},
diff --git a/src/dillo.cc b/src/dillo.cc
index d73b855a..4d96988f 100644
--- a/src/dillo.cc
+++ b/src/dillo.cc
@@ -382,6 +382,14 @@ int main(int argc, char **argv)
DBG_OBJ_COLOR("#ffa0a0", "dw::core::*");
DBG_OBJ_COLOR("#ffe0a0", "dw::core::style::*");
+ DBG_OBJ_COLOR ("#80ffa0", "dw::Image");
+ DBG_OBJ_COLOR ("#f0ff80", "dw::Textblock");
+ DBG_OBJ_COLOR ("#d0ff80", "dw::OutOfFlowMgr");
+ DBG_OBJ_COLOR ("#e0ff80", "dw::AlignedTextblock");
+ DBG_OBJ_COLOR ("#b0ff80", "dw::ListItem");
+ DBG_OBJ_COLOR ("#80ff80", "dw::TableCell");
+ DBG_OBJ_COLOR ("#80ffc0", "dw::Table");
+
uint_t opt_id;
uint_t options_got = 0;
uint32_t xid = 0;
diff --git a/src/html.cc b/src/html.cc
index 15c37576..9224f43b 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 a5c43bf4..024ef56d 100644
--- a/src/styleengine.cc
+++ b/src/styleengine.cc
@@ -588,6 +588,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;
@@ -597,6 +603,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;
@@ -648,6 +660,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;
@@ -660,6 +678,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..d9056dfe 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,5 +1,7 @@
AM_CPPFLAGS = \
- -I$(top_srcdir)
+ -I$(top_srcdir) \
+ -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/test"'
+
AM_CFLAGS = @LIBFLTK_CFLAGS@
AM_CXXFLAGS = @LIBFLTK_CXXFLAGS@
@@ -7,6 +9,7 @@ noinst_PROGRAMS = \
dw-anchors-test \
dw-example \
dw-find-test \
+ dw-float-test \
dw-links \
dw-links2 \
dw-image-background \
@@ -52,6 +55,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?