aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore2
-rw-r--r--ChangeLog34
-rw-r--r--Doxyfile3
-rw-r--r--configure.ac18
-rw-r--r--dillorc20
-rw-r--r--dlib/dlib.c12
-rw-r--r--doc/Makefile.am3
-rw-r--r--doc/dw-example-screenshot.pngbin3808 -> 2264 bytes
-rw-r--r--doc/dw-floats-01.pngbin0 -> 3410 bytes
-rw-r--r--doc/dw-grows.doc184
-rw-r--r--doc/dw-out-of-flow-2.doc69
-rw-r--r--doc/dw-out-of-flow.doc214
-rw-r--r--doc/dw-size-of-widget.pngbin2825 -> 1749 bytes
-rw-r--r--doc/dw-style-box-model.pngbin5116 -> 3889 bytes
-rw-r--r--doc/dw-style-length-absolute.pngbin756 -> 575 bytes
-rw-r--r--doc/dw-style-length-percentage.pngbin1105 -> 890 bytes
-rw-r--r--doc/dw-style-length-relative.pngbin1083 -> 868 bytes
-rw-r--r--doc/dw-widget-sizes.doc251
-rw-r--r--doc/not-so-simple-container.pngbin13812 -> 5738 bytes
-rw-r--r--doc/user_help.html2
-rw-r--r--dpi/cookies.c7
-rw-r--r--dpi/downloads.cc2
-rw-r--r--dpi/file.c4
-rw-r--r--dpi/https.c14
-rw-r--r--dw/Makefile.am11
-rw-r--r--dw/alignedtablecell.cc197
-rw-r--r--dw/alignedtablecell.hh44
-rw-r--r--dw/bullet.cc21
-rw-r--r--dw/bullet.hh3
-rw-r--r--dw/findtext.cc6
-rw-r--r--dw/fltkimgbuf.cc10
-rw-r--r--dw/fltkplatform.hh2
-rw-r--r--dw/fltkui.cc76
-rw-r--r--dw/fltkui.hh4
-rw-r--r--dw/hyphenator.cc2
-rw-r--r--dw/image.cc176
-rw-r--r--dw/image.hh3
-rw-r--r--dw/imgrenderer.cc19
-rw-r--r--dw/iterator.cc146
-rw-r--r--dw/iterator.hh15
-rw-r--r--dw/layout.cc200
-rw-r--r--dw/layout.hh43
-rw-r--r--dw/listitem.cc2
-rw-r--r--dw/outofflowmgr.cc2292
-rw-r--r--dw/outofflowmgr.hh436
-rw-r--r--dw/ruler.cc30
-rw-r--r--dw/ruler.hh5
-rw-r--r--dw/selection.hh2
-rw-r--r--dw/simpletablecell.cc122
-rw-r--r--dw/simpletablecell.hh35
-rw-r--r--dw/style.cc77
-rw-r--r--dw/style.hh64
-rw-r--r--dw/table.cc1529
-rw-r--r--dw/table.hh105
-rw-r--r--dw/table_iterator.cc134
-rw-r--r--dw/tablecell.cc145
-rw-r--r--dw/tablecell.hh34
-rw-r--r--dw/textblock.cc1502
-rw-r--r--dw/textblock.hh433
-rw-r--r--dw/textblock_iterator.cc360
-rw-r--r--dw/textblock_linebreaking.cc1690
-rw-r--r--dw/types.cc85
-rw-r--r--dw/types.hh31
-rw-r--r--dw/ui.cc189
-rw-r--r--dw/ui.hh58
-rw-r--r--dw/widget.cc1188
-rw-r--r--dw/widget.hh251
-rw-r--r--lout/Makefile.am3
-rw-r--r--lout/container.cc156
-rw-r--r--lout/container.hh70
-rw-r--r--lout/debug.hh249
-rw-r--r--lout/identity.cc23
-rw-r--r--lout/identity.hh4
-rw-r--r--lout/misc.hh47
-rw-r--r--lout/object.cc61
-rw-r--r--lout/object.hh57
-rw-r--r--lout/unicode.cc20
-rw-r--r--src/IO/IO.c13
-rw-r--r--src/IO/about.c70
-rw-r--r--src/IO/http.c270
-rw-r--r--src/Makefile.am3
-rw-r--r--src/cache.c145
-rw-r--r--src/cache.h3
-rw-r--r--src/capi.c149
-rw-r--r--src/colors.c2
-rw-r--r--src/cookies.h3
-rw-r--r--src/css.cc2
-rw-r--r--src/css.hh2
-rw-r--r--src/cssparser.cc68
-rw-r--r--src/decode.c26
-rw-r--r--src/decode.h16
-rw-r--r--src/dicache.c272
-rw-r--r--src/dicache.h13
-rw-r--r--src/dillo.cc20
-rw-r--r--src/domain.c4
-rw-r--r--src/form.cc38
-rw-r--r--src/gif.c19
-rw-r--r--src/html.cc697
-rw-r--r--src/html_charrefs.h2138
-rw-r--r--src/html_common.hh5
-rw-r--r--src/image.cc6
-rw-r--r--src/jpeg.c1
-rw-r--r--src/klist.c2
-rw-r--r--src/menu.cc35
-rw-r--r--src/paths.cc6
-rw-r--r--src/png.c11
-rw-r--r--src/prefs.c3
-rw-r--r--src/prefs.h3
-rw-r--r--src/prefsparser.cc3
-rw-r--r--src/styleengine.cc60
-rw-r--r--src/styleengine.hh2
-rw-r--r--src/table.cc27
-rw-r--r--src/ui.cc2
-rw-r--r--src/uicmd.cc19
-rw-r--r--src/url.c31
-rw-r--r--src/url.h9
-rw-r--r--src/web.cc2
-rw-r--r--test/Makefile.am61
-rw-r--r--test/containers.cc51
-rw-r--r--test/cookies.c16
-rw-r--r--test/dw_float_test.cc145
-rw-r--r--test/dw_simple_container.cc241
-rw-r--r--test/dw_simple_container.hh56
-rw-r--r--test/dw_simple_container_test.cc114
-rw-r--r--test/dw_table.cc1
-rw-r--r--test/dw_table_aligned.cc6
-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
135 files changed, 15409 insertions, 3103 deletions
diff --git a/.hgignore b/.hgignore
index 9ab76864..f3e9fd41 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$
@@ -36,6 +37,7 @@
^test/dw-links2$
^test/dw-lists$
^test/dw-resource-test$
+^test/dw-simple-container-test$
^test/dw-table$
^test/dw-table-aligned$
^test/dw-ui-test$
diff --git a/ChangeLog b/ChangeLog
index f7b7673b..0622e288 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,40 @@ Here we list changes that are relatively significant and/or visible to the
user. For a history of changes in full detail, see our Mercurial repository
at http://hg.dillo.org/dillo
+
+dillo-3.1 [not released yet]
+
++- Floating elements.
+ - Redesign of widget sizes ("GROWS").
+ - Applied CSS attribute 'width' to all elements, 'height' is now also
+ supported.
+ - Suport for 'min-width', 'max-width', 'min-height' and 'max-height'.
+ - Suport for 'display: inline-block'.
+ - <BUTTON>'s are now inline.
+ - Image aspect ratio is preserved when one dimension is specified by a
+ percentage value.
+ - New dillorc options 'adjust_min_width' and 'adjust_table_min_width'.
+ - Make building of test/ files more robust.
+ - Work on collapsing spaces: more cases supported.
+ - Fix crash that's possible searching for text while page still being built.
+ Patches: Sebastian Geerken
++- Image buffer/cache improvements.
+ Patch: Jorge Arellano Cid
++- Crosscompile/buildroot-friendly fltk-config test.
+ Patch: Peter Seiderer
++- HTML5 character references.
+ - Give images lower priority when requesting resources (responsiveness).
+ - Reuse of connections for HTTP (enable w/ http_persistent_conns in dillorc).
+ - Fix X11 icon name.
+ - Abort failed queries.
+ - In location bar, tend toward showing beginning of URL instead of end.
+ - Handle irix's version of vsnprintf().
+ Patches: corvid
++- Avoid requesting background images if an ancestor has display:none.
+ Patch: Johannes Hofmann
+
+-----------------------------------------------------------------------------
+
dillo-3.0.4.1 [December 24, 2014]
+- Avoid a corner case segfault when no search URL is found in dillorc.
diff --git a/Doxyfile b/Doxyfile
index 971cf7be..ee527bab 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -599,8 +599,7 @@ EXCLUDE = dlib \
dpi \
dpid \
dpip \
- src \
- test
+ src
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
# directories that are symbolic links (a Unix filesystem feature) are excluded
diff --git a/configure.ac b/configure.ac
index 325411fc..04df681a 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)
@@ -112,12 +120,13 @@ dnl -------------------------
dnl
dnl For debugging and to be user friendly
AC_MSG_CHECKING([FLTK 1.3])
-fltk_version="`fltk-config --version 2>/dev/null`"
+AC_PATH_PROG(FLTK_CONFIG,fltk-config)
+fltk_version="`$FLTK_CONFIG --version 2>/dev/null`"
case $fltk_version in
1.3.*) AC_MSG_RESULT(yes)
- LIBFLTK_CXXFLAGS=`fltk-config --cxxflags`
- LIBFLTK_CFLAGS=`fltk-config --cflags`
- LIBFLTK_LIBS=`fltk-config --ldflags`;;
+ LIBFLTK_CXXFLAGS=`$FLTK_CONFIG --cxxflags`
+ LIBFLTK_CFLAGS=`$FLTK_CONFIG --cflags`
+ LIBFLTK_LIBS=`$FLTK_CONFIG --ldflags`;;
?*) AC_MSG_RESULT(no)
AC_MSG_ERROR(FLTK 1.3 required; version found: $fltk_version);;
*) AC_MSG_RESULT(no)
@@ -482,6 +491,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/dillorc b/dillorc
index 7395abd3..d3ef98b8 100644
--- a/dillorc
+++ b/dillorc
@@ -21,7 +21,7 @@
# Change this if you want background images to be loaded initially.
# (While browsing, this can be changed from the tools/settings menu.)
-#load_background_images=FALSE
+#load_background_images=NO
# Change this if you want to disable loading of CSS stylesheets initially.
# (While browsing, this can be changed from the tools/settings menu.)
@@ -76,10 +76,18 @@
# Show tooltip popups for HTML title attributes
#show_tooltip=YES
-# Set this to YES if you want to limit the word wrap width to the viewport
-# width (may be useful for iPAQ)
+# Set this to YES to limit the word wrap width to the viewport width
#limit_text_width=NO
+# If this is set to YES, all CSS size specifications are adjusted so that
+# all contents can be displayed. (Except for tables, see below.)
+#adjust_min_width=NO
+
+# If this is set to YES, all CSS size specifications for tables are
+# adjusted so that all contents can be displayed. This is seperated
+# from "adjust_min_width" (with another standard value) to mimic
+# Firefox, which differenciates between tables and, say, textblocks.
+#adjust_table_min_width=YES
#-------------------------------------------------------------------------
# PENALTIES
@@ -161,6 +169,7 @@
search_url="dd DuckDuckGo http://duckduckgo.com/lite/?kp=-1&q=%s"
search_url="Wikipedia http://www.wikipedia.org/w/index.php?search=%s&go=Go"
search_url="Free Dictionary http://www.thefreedictionary.com/%s"
+search_url="Startpage http://www.startpage.com/do/search?query=%s"
search_url="Google http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s"
# If set, dillo will ask web servers to send pages in this language.
@@ -175,6 +184,11 @@ search_url="Google http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s"
# Maximum number of simultaneous TCP connections to a single server or proxy.
# http_max_conns=6
+# Change this if you want Dillo to reuse HTTP connections to a server or proxy
+# when possible instead of making a new connection for every request for a new
+# page/image/stylesheet. Currently, HTTPS connections are never reused.
+#http_persistent_conns=NO
+
# Set the proxy information for http.
# Note that the http_proxy environment variable overrides this setting.
# WARNING: FTP and downloads plugins use wget. To use a proxy with them,
diff --git a/dlib/dlib.c b/dlib/dlib.c
index d60f1bc6..23534730 100644
--- a/dlib/dlib.c
+++ b/dlib/dlib.c
@@ -406,6 +406,17 @@ void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp)
va_copy(argp2, argp);
n = vsnprintf(ds->str + ds->len, ds->sz - ds->len, format, argp2);
va_end(argp2);
+#if defined(__sgi)
+ /* IRIX does not conform to C99; if the entire argument did not fit
+ * into the buffer, n = buffer space used (minus 1 for terminator)
+ */
+ if (n > -1 && n + 1 < ds->sz - ds->len) {
+ ds->len += n; /* Success! */
+ break;
+ } else {
+ n_sz = ds->sz * 2;
+ }
+#else
if (n > -1 && n < ds->sz - ds->len) {
ds->len += n; /* Success! */
break;
@@ -414,6 +425,7 @@ void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp)
} else { /* old glibc */
n_sz = ds->sz * 2;
}
+#endif
dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
}
}
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-example-screenshot.png b/doc/dw-example-screenshot.png
index a4d37903..94f272ab 100644
--- a/doc/dw-example-screenshot.png
+++ b/doc/dw-example-screenshot.png
Binary files differ
diff --git a/doc/dw-floats-01.png b/doc/dw-floats-01.png
new file mode 100644
index 00000000..116d36b3
--- /dev/null
+++ b/doc/dw-floats-01.png
Binary files differ
diff --git a/doc/dw-grows.doc b/doc/dw-grows.doc
new file mode 100644
index 00000000..15150338
--- /dev/null
+++ b/doc/dw-grows.doc
@@ -0,0 +1,184 @@
+/** \page dw-grows GROWS - Grand Redesign Of Widget Sizes
+
+This paper describes (will describe) some design changes to
+calculating widget sizes. Goals are:
+
+- Simplification of widget size calculation by the parent widget;
+ dw::Textblock::calcWidgetSize, dw::OutOfFlowMgr::ensureFloatSize
+ etc. should become simpler or perhaps even obsolete.
+
+- Making the implementation of some features possible:
+
+ - *max-width*, *max-height*, *min-width*, *min-height*;
+ - correct aspect ratio for images with only one percentage size defined;
+ - *display: inline-block*;
+ - <button>.
+
+
+A short sketch
+==============
+
+**dw::core::Widget::sizeRequest and dw::core::Widget::getExtremes will
+return final results.** The caller does not have to correct the size,
+e.&nbsp;g. when percentages are defined. As an example,
+dw::Textblock::calcWidgetSize has already become much simpler.
+
+**A new hierarchy, *container*:** Aside from dw::core::Widget::parent
+and dw::core::Widget::generator, there is a third hierarchy
+dw::core::Widget::container, which is (unlike *generator*) always a
+direct ancestor, and represents what in CSS is called *containing
+block*. Containers are important to define the "context size", which
+is (not solely) used for percentage sizes.
+
+(There is another "containing block", dw::Textblock::containingBlock;
+these may be consolidated some day.)
+
+**The process of size calculation is split between the widget itself
+and its container:**
+
+- The container provides some abstract methods:
+ dw::core::Widget::getAvailWidthOfChild,
+ dw::core::Widget::getAvailHeightOfChild,
+ dw::core::Widget::correctRequisitionOfChild, and
+ dw::core::Widget::correctExtremesOfChild, which can be used in the
+ actual implementation of dw::core::Widget::sizeRequestImpl;
+ different containers with different ways how to arrange their
+ children will implement these methods in a different way. (Simple
+ example: the *available width* for children within a textblock is
+ the *available width* for the textblock itself, minus
+ margin/border/padding; on the other hand, it is completely different
+ for children of tables, for which a complex column width calculation
+ is used.)
+
+- The actual size calculation is, however, controlled by the widget
+ itself, which only *uses* these methods above.
+
+<div style="border: 2px solid #ffff00; margin-top: 0.5em;
+ margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0">
+ <b>Update:</b> This is not fully correct; the parents are also involved
+ for calculating available widths and heights, at least when CSS 'width'
+ and 'height' are not set.</div>
+
+**Size hints are removed.** Instead, the container methods in the
+previous paragraph are used. Changes of container sizes (especially
+viewport the size) are handled in a different way.
+
+**Extremes are extended by intrinsic values.** In some cases (see
+dw::Table::forceCalcCellSizes, case *minWidth* > *totalWidth*, for an
+example) it is useful to know about minimal and maximal width of a
+widget independent of CSS attributes. For this, dw::core::Extremes is
+extended by:
+
+- dw::core::Extremes::minWidthIntrinsic and
+- dw::core::Extremes::maxWidthIntrinsic.
+
+The rules for the calculation:
+
+1. If a widget has no children, it calculates *minWidthIntrinsic* and
+ *maxWidthIntrinsic* as those values not affected by CSS hints.
+ (dw::core::Widget::correctExtremes will not change these values.)
+2. A widget must calculate *minWidthIntrinsic* and *maxWidthIntrinsic*
+ from *minWidthIntrinsic* and *maxWidthIntrinsic* of its children,
+ and *minWidth* and *maxWidth* from *minWidth* and *maxWidth* of its
+ children.
+3. At the end, *minWidth* and *maxWidth* of a widget are corrected by
+ CSS attributes. (dw::core::Widget::correctExtremes will do this.)
+
+<div style="border: 2px solid #ffff00; margin-top: 0.5em;
+ margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0">
+ <b>Notice:</b> Currently, dw::core::Widget::getExtremesImpl must
+ set all four members in dw::core::Extremes; this may change.</div>
+
+
+Rules for *new* methods related to resizing
+===========================================
+
+- Of course, *sizeRequestImpl* may (should) call *correctRequisition*,
+ and *getExtremesImpl* may (should) call *correctExtremes*.
+
+- *sizeRequestImpl* (and *correctRequisition*) is allowed to call
+ *getAvailWidth* and *getAvailHeight* with *forceValue* set, but
+ *getExtremesImpl* (and *correctExtremes*) is allowed to call these
+ only with *forceValue* unset.
+
+- For this reason, *sizeRequestImpl* is indeed allowed to call
+ *getExtremes* (dw::Table does so), but the opposite
+ (*getExtremesImpl* calling *sizeRequest*) is not allowed
+ anymore. (Before GROWS, the standard implementation
+ dw::core::Widget::getExtremesImpl did so.)
+
+- Finally, *getAvailWidth* and *getAvailHeight* may call
+ *getExtremes*, if and only if *forceValue* is set.
+
+Here is a diagram showing all permitted dependencies:
+
+\dot
+digraph G {
+ node [shape=record, fontname=Helvetica, fontsize=10, color="#c0c0c0"];
+ edge [arrowhead="open", arrowtail="none", color="#404040"];
+
+ "sizeRequest[Impl]" -> "getExtremes[Impl]";
+ "sizeRequest[Impl]" -> correctRequisition;
+ "getExtremes[Impl]" -> correctExtremes;
+ "sizeRequest[Impl]" -> "getAvail[Width|Height] (true)";
+ "getExtremes[Impl]" -> "getAvail[Width|Height] (false)";
+ correctRequisition -> "getAvail[Width|Height] (true)";
+ correctExtremes -> "getAvail[Width|Height] (false)";
+ "getAvail[Width|Height] (true)" -> "getExtremes[Impl]";
+}
+\enddot
+
+Open issues
+===========
+
+**Do CSS size dimensions override intrinsic sizes in all cases?** If a
+textblock needs at least, say, 100 pixels width so that the text can
+be read, but has a specification "width: 50px", should half of the
+text be invisible? Or should the width be corrected again to 100
+pixels?
+
+Currently, in the CSS size specification is honoured in all cases,
+with one exception: see dw::Textblock::sizeRequestImpl and see
+dw::Textblock::getExtremesImpl (the time when
+dw::core::Widget::correctRequisition and
+dw::core::Widget::correctExtremes, respectively, is called).
+
+*Not* honouring the CSS size specification in all cases could improve
+readability in some cases, so this could depend on a user preference.
+
+**Update:** There is now a dillorc option <tt>adjust_min_width</tt>,
+which is implemented for widths, but not heights (since it is based on
+width extremes, but there are currently no height extremes).
+
+Another problem is that in most cases, there is no clippping, so that
+contents may exceed the allocation of the widget, but redrawing is not
+necessarily triggered.
+
+**Percentage values for margins and paddings, as well as negative
+margins** are interesting applications, but have not been considered
+yet. For negative margins, a new attribute
+dw::core::Widget::extraSpace could solve the problem of widgets
+sticking out of the allocation of parent.
+
+**Clarify percentage heights.** Search in widget.cc, and compare
+section 10.5 ('height') of the CSS 2.1 specification to section 10.2
+('width').
+
+**Fast queue resize does not work fully.** Example: run
+*test/dw-simple-container-test* (dw_simple_container_test.cc), resize
+(best maximize) the window and follow (e.&nbsp;g. by using RTFL) what
+happens in consequence of dw::core::Layout::viewportSizeChanged. The
+dw::SimpleContainer in the middle is not affected, so only the two
+dw::Textblock's (at the top and at the bottom) call queueResize with
+*fast = true*, and so get *NEEDS_RESIZE* set; but since it is not set
+for the dw::SimpleContainer, *sizeRequest* is never called for the
+bottom dw::Textblock.
+
+There does not seem to be a real case for this problem in dillo, since
+all widgets which may contain other widgets (except
+dw::SimpleContainer, which is not used outside tests) use the
+available width and height (dw::core::Widget::usesAvailWidth and
+dw::core::Widget::usesAvailHeight), and so are always affected by
+viewport size changes.
+
+*/
diff --git a/doc/dw-out-of-flow-2.doc b/doc/dw-out-of-flow-2.doc
new file mode 100644
index 00000000..d9d70565
--- /dev/null
+++ b/doc/dw-out-of-flow-2.doc
@@ -0,0 +1,69 @@
+/** \page dw-out-of-flow-2 Handling Elements Out Of Flow (notes 2)
+
+This has to be integrated into \ref dw-out-of-flow.
+
+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..ea4a52bc
--- /dev/null
+++ b/doc/dw-out-of-flow.doc
@@ -0,0 +1,214 @@
+/** \page dw-out-of-flow Handling Elements Out Of Flow
+
+
+<div style="border: 2px solid #ffff00; margin-bottom: 0.5em;
+padding: 0.5em 1em; background-color: #ffffe0"><b>Info:</b>
+Should be incorporated into dw::Textblock.</div>
+
+Introduction
+============
+
+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.)
+
+Both in this text and the code, generating and containing block are
+abbreviated with **GB** and **CB**, respectively.
+
+
+Implementation overview
+=======================
+
+Widget level
+------------
+The terms *generating block* and *containing block* have been raised
+to a higher level, the one of dw::core::Widget, and are here called
+*generating widget* and *containing widget*. To represent the
+distinction, the type of dw::core::Content has been split into three
+parts:
+
+- If a widget is out of flow, the generating widget keeps a reference
+ with the type dw::core::Content::WIDGET_OOF_REF, while the
+ containing block refers to it as dw::core::Content::WIDGET_OOF_CONT.
+- For widgets within flow, dw::core::Content::WIDGET_IN_FLOW is used.
+
+Notice that in the first case, there are two pieces of content
+referring to the same widget.
+
+An application of this distinction is iterators. [TODO: more. And
+still missing: DeepIterator may need the generating parent widget in
+some cases.]
+
+
+Textblock level
+---------------
+Both dw::Textblock::notifySetAsTopLevel and
+dw::Textblock::notifySetParent set the member
+dw::Textblock::containingBlock appropriately, (according to rules
+which should be defined in this document).
+
+Handling widgets out of flow is partly the task of the new class
+dw::OutOfFlowMgr, which is stored by dw::Textblock::outOfFlowMgr, but
+only for containing blocks. Generating blocks should refer to
+*containingBlock->outOfFlowMgr*. (Perhaps dw::OutOfFlowMgr may become
+independent of dw::Textblock.)
+
+dw::Textblock::addWidget is extended, so that floats and absolutely
+positioned elements can be added. Notice that not *this* widget, but
+the containing block becomes the parent of the newly added child, if
+it is out of flow. dw::Textblock::addWidget decides this by calling
+dw::OutOfFlowMgr::isOutOfFlow. (See new content types above.)
+
+dw::core::Widget::parentRef has become a new representation. Before,
+it represented the line numer. Now (least signifant bit left):
+
+ +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ | line number | 0 |
+ +---+ - - - +---+---+- - - - - -+---+---+---+---+
+
+ +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ | left float index | 0 | 0 | 1 |
+ +---+ - - - +---+---+- - - - - -+---+---+---+---+
+
+ +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ | right float index | 1 | 0 | 1 |
+ +---+ - - - +---+---+- - - - - -+---+---+---+---+
+
+ +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ | absolutely positioned index | 1 | 1 |
+ +---+ - - - +---+---+- - - - - -+---+---+---+---+
+
+Details are hidden by static inline methods of dw::OutOfFlowMgr.
+
+
+The sizeRequest/sizeAllocate problem
+========================================
+
+*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. See below (*hasRelationChanged*) for details.
+
+Generally, this pattern (distinguishing between GB and CB) can be
+found everywhere in dw::OutOfFlowMgr.
+
+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.
+
+(This could be solved in a more simple, elegant way, when
+*sizeRequest* would depend on the position. This is, however, only a
+vague idea, perhaps not even feasible, and for which there are no
+concrete plans, certainly not in \ref dw-grows.)
+
+
+Implementation details
+======================
+
+- CB and GB lists (general pattern) (see previous section)
+- binary search; different search criteria, how they accord
+- lastLeftTBIndex, lastRightTBIndex etc.
+- limitiation of search; extIndex etc.
+
+
+How *hasRelationChanged* works
+==============================
+
+...
+
+
+Integration of line breaking and floats
+=======================================
+
+(Positioning of floats, loop, recent works.)
+
+
+Absolute and fixed positiones
+=============================
+
+See <http://flpsed.org/hgweb/dillo_grows>.
+
+*/ \ No newline at end of file
diff --git a/doc/dw-size-of-widget.png b/doc/dw-size-of-widget.png
index eda93ee1..dbdbe0c4 100644
--- a/doc/dw-size-of-widget.png
+++ b/doc/dw-size-of-widget.png
Binary files differ
diff --git a/doc/dw-style-box-model.png b/doc/dw-style-box-model.png
index aa65ecb7..bf2fb1f1 100644
--- a/doc/dw-style-box-model.png
+++ b/doc/dw-style-box-model.png
Binary files differ
diff --git a/doc/dw-style-length-absolute.png b/doc/dw-style-length-absolute.png
index 6b4d8389..9ea28cad 100644
--- a/doc/dw-style-length-absolute.png
+++ b/doc/dw-style-length-absolute.png
Binary files differ
diff --git a/doc/dw-style-length-percentage.png b/doc/dw-style-length-percentage.png
index 15b36958..b1ad79c9 100644
--- a/doc/dw-style-length-percentage.png
+++ b/doc/dw-style-length-percentage.png
Binary files differ
diff --git a/doc/dw-style-length-relative.png b/doc/dw-style-length-relative.png
index d54d99b5..ee79b1a9 100644
--- a/doc/dw-style-length-relative.png
+++ b/doc/dw-style-length-relative.png
Binary files differ
diff --git a/doc/dw-widget-sizes.doc b/doc/dw-widget-sizes.doc
index 419a4a73..a82d3b99 100644
--- a/doc/dw-widget-sizes.doc
+++ b/doc/dw-widget-sizes.doc
@@ -1,22 +1,24 @@
/** \page dw-widget-sizes Sizes of Dillo Widgets
-<h2>Allocation</h2>
+<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
+padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
+Not up to date, see \ref dw-grows.</div>
+
+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 +33,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 +54,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 +64,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,64 +87,49 @@ 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>
-
-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>,
-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>
-Generally, the values should define the available space for the
-widget.
+Size Hints
+==========
-A widget, which depends on size hints, should call
-dw::core::Widget::queueResize, when apropriate.
+<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
+padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
+Size hints have been removed, see \ref dw-grows.</div>
-\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 +145,133 @@ 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?
+
+
+See also
+========
+
+- \ref dw-grows
+
*/
diff --git a/doc/not-so-simple-container.png b/doc/not-so-simple-container.png
index 886b72a8..0af067b5 100644
--- a/doc/not-so-simple-container.png
+++ b/doc/not-so-simple-container.png
Binary files differ
diff --git a/doc/user_help.html b/doc/user_help.html
index 9de2056f..88218f70 100644
--- a/doc/user_help.html
+++ b/doc/user_help.html
@@ -314,7 +314,7 @@
<p>
Example, in dillorc:<br>
<blockquote>
- <code>search_url="dd Duckduckgo http://duckduckgo.com/lite/?kp=-1&q=%s"</code>
+ <code>search_url="dd Duckduckgo http://duckduckgo.com/lite/?kp=-1&amp;q=%s"</code>
</blockquote>
means you can reach the location bar (CTRL+L), then type:
<blockquote>
diff --git a/dpi/cookies.c b/dpi/cookies.c
index 6c5e958e..b858bd53 100644
--- a/dpi/cookies.c
+++ b/dpi/cookies.c
@@ -1042,14 +1042,13 @@ static uint_t Cookies_internal_dots_required(const char *host)
if (tld_len > 0) {
/* These TLDs were chosen by examining the current publicsuffix list
- * in February 2014 and picking out those where it was simplest for
+ * in October 2014 and picking out those where it was simplest for
* them to describe the situation by beginning with a "*.[tld]" rule
* or every rule was "[something].[tld]".
*/
- const char *const tlds[] = {"bd","bn","ck","cy","er","et","fj","fk",
+ const char *const tlds[] = {"bd","bn","ck","cy","er","fj","fk",
"gu","il","jm","ke","kh","kw","mm","mz",
- "ni","np","nz","pg","tr","uk","ye","za",
- "zm","zw"};
+ "ni","np","pg","ye","za","zm","zw"};
uint_t i, tld_num = sizeof(tlds) / sizeof(tlds[0]);
for (i = 0; i < tld_num; i++) {
diff --git a/dpi/downloads.cc b/dpi/downloads.cc
index 70acaa8a..771098e5 100644
--- a/dpi/downloads.cc
+++ b/dpi/downloads.cc
@@ -68,7 +68,7 @@ protected:
void draw();
public:
ProgressBar(int x, int y, int w, int h, const char *lbl = 0);
- void range(double min, double max, double step = 1) {
+ void range(double min, double max, double step = 1) {
mMin = min; mMax = max; mStep = step;
};
void step(double step) { mPresent += step; redraw(); };
diff --git a/dpi/file.c b/dpi/file.c
index 5f1459ad..00e10468 100644
--- a/dpi/file.c
+++ b/dpi/file.c
@@ -346,10 +346,10 @@ static void File_info2html(ClientInfo *client, FileInfo *finfo, int n)
sizeunits = "bytes";
} else if (finfo->size / 1024 <= 9999) {
size = finfo->size / 1024 + (finfo->size % 1024 >= 1024 / 2);
- sizeunits = "Kb";
+ sizeunits = "KB";
} else {
size = finfo->size / 1048576 + (finfo->size % 1048576 >= 1048576 / 2);
- sizeunits = "Mb";
+ sizeunits = "MB";
}
/* we could note if it's a symlink... */
diff --git a/dpi/https.c b/dpi/https.c
index e6d2b0e9..da75b9e8 100644
--- a/dpi/https.c
+++ b/dpi/https.c
@@ -156,9 +156,12 @@ static void yes_ssl_support(void)
}
}
- /* Do not use the SSLv2 protocol. */
+ /* SSL2 has been known to be insecure forever, disabling SSL3 is in response
+ * to POODLE, and disabling compression is in response to CRIME.
+ */
if (exit_error == 0){
- SSL_CTX_set_options(ssl_context, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(ssl_context,
+ SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION);
}
/*Set directory to load certificates from*/
@@ -188,10 +191,11 @@ static void yes_ssl_support(void)
}
if (exit_error == 0){
- /* Need to do the following if we want to deal with all
- * possible ciphers
+ /* Don't want: eNULL, which has no encryption; aNULL, which has no
+ * authentication; LOW, which as of 2014 use 64 or 56-bit encryption;
+ * EXPORT40, which uses 40-bit encryption.
*/
- SSL_set_cipher_list(ssl_connection, "ALL");
+ SSL_CTX_set_cipher_list(ssl_context, "ALL:!aNULL:!eNULL:!LOW:!EXPORT40");
/* Need to do this if we want to have the option of dealing
* with self-signed certs
diff --git a/dw/Makefile.am b/dw/Makefile.am
index d0d56d2a..0b4b7bb4 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 \
@@ -57,6 +57,8 @@ libDw_fltk_a_SOURCES = \
libDw_fltk_a_CXXFLAGS = @LIBFLTK_CXXFLAGS@
libDw_widgets_a_SOURCES = \
+ alignedtablecell.cc \
+ alignedtablecell.hh \
alignedtextblock.cc \
alignedtextblock.hh \
bullet.cc \
@@ -67,9 +69,14 @@ libDw_widgets_a_SOURCES = \
image.hh \
listitem.cc \
listitem.hh \
+ outofflowmgr.cc \
+ outofflowmgr.hh \
ruler.cc \
ruler.hh \
+ simpletablecell.cc \
+ simpletablecell.hh \
table.cc \
+ table_iterator.cc \
table.hh \
tablecell.cc \
tablecell.hh \
diff --git a/dw/alignedtablecell.cc b/dw/alignedtablecell.cc
new file mode 100644
index 00000000..633c4e68
--- /dev/null
+++ b/dw/alignedtablecell.cc
@@ -0,0 +1,197 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2005-2007 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 "alignedtablecell.hh"
+#include "table.hh"
+#include "tablecell.hh"
+#include "../lout/debug.hh"
+#include <stdio.h>
+
+namespace dw {
+
+int AlignedTableCell::CLASS_ID = -1;
+
+AlignedTableCell::AlignedTableCell (AlignedTableCell *ref, bool limitTextWidth):
+ AlignedTextblock (limitTextWidth)
+{
+ DBG_OBJ_CREATE ("dw::AlignedTableCell");
+ registerName ("dw::AlignedTableCell", &CLASS_ID);
+
+ /** \bug ignoreLine1OffsetSometimes does not work? */
+ //ignoreLine1OffsetSometimes = true;
+ charWordIndex = -1;
+ setRefTextblock (ref);
+ setButtonSensitive(true);
+}
+
+AlignedTableCell::~AlignedTableCell()
+{
+ DBG_OBJ_DELETE ();
+}
+
+
+bool AlignedTableCell::getAdjustMinWidth ()
+{
+ return tablecell::getAdjustMinWidth ();
+}
+
+bool AlignedTableCell::isBlockLevel ()
+{
+ return tablecell::isBlockLevel ();
+}
+
+int AlignedTableCell::getAvailWidthOfChild (Widget *child, bool forceValue)
+{
+ DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/getAvailWidthOfChild",
+ "%p, %s", child, forceValue ? "true" : "false");
+
+ int width = tablecell::correctAvailWidthOfChild
+ (this, child, Textblock::getAvailWidthOfChild (child, forceValue),
+ forceValue);
+
+ DBG_OBJ_LEAVE ();
+ return width;
+}
+
+int AlignedTableCell::getAvailHeightOfChild (Widget *child, bool forceValue)
+{
+ DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/getAvailHeightOfChild",
+ "%p, %s", child, forceValue ? "true" : "false");
+
+ int height = tablecell::correctAvailHeightOfChild
+ (this, child, Textblock::getAvailHeightOfChild (child, forceValue),
+ forceValue);
+
+ DBG_OBJ_LEAVE ();
+ return height;
+}
+
+void AlignedTableCell::correctRequisitionOfChild (Widget *child,
+ core::Requisition
+ *requisition,
+ void (*splitHeightFun) (int,
+ int*,
+ int*))
+{
+ DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/correctRequisitionOfChild",
+ "%p, %d * (%d + %d), ...", child, requisition->width,
+ requisition->ascent, requisition->descent);
+
+ AlignedTextblock::correctRequisitionOfChild (child, requisition,
+ splitHeightFun);
+ tablecell::correctCorrectedRequisitionOfChild (this, child, requisition,
+ splitHeightFun);
+
+ DBG_OBJ_LEAVE ();
+}
+
+void AlignedTableCell::correctExtremesOfChild (Widget *child,
+ core::Extremes *extremes)
+{
+ DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/correctExtremesOfChild",
+ "%p, %d (%d) / %d (%d)",
+ child, extremes->minWidth, extremes->minWidthIntrinsic,
+ extremes->maxWidth, extremes->maxWidthIntrinsic);
+
+ AlignedTextblock::correctExtremesOfChild (child, extremes);
+ tablecell::correctCorrectedExtremesOfChild (this, child, extremes);
+
+ DBG_OBJ_LEAVE ();
+}
+
+int AlignedTableCell::applyPerWidth (int containerWidth,
+ core::style::Length perWidth)
+{
+ return tablecell::applyPerWidth (this, containerWidth, perWidth);
+}
+
+int AlignedTableCell::applyPerHeight (int containerHeight,
+ core::style::Length perHeight)
+{
+ return tablecell::applyPerHeight (this, containerHeight, perHeight);
+}
+
+int AlignedTableCell::wordWrap(int wordIndex, bool wrapAll)
+{
+ Textblock::Word *word;
+ const char *p;
+
+ int ret = Textblock::wordWrap (wordIndex, wrapAll);
+
+ if (charWordIndex == -1) {
+ word = words->getRef (wordIndex);
+ if (word->content.type == core::Content::TEXT) {
+ if ((p = strchr (word->content.text,
+ word->style->textAlignChar))) {
+ charWordIndex = wordIndex;
+ charWordPos = p - word->content.text + 1;
+ } else if (word->style->textAlignChar == ' ' &&
+ word->content.space) {
+ charWordIndex = wordIndex + 1;
+ charWordPos = 0;
+ }
+ }
+ }
+
+ if (wordIndex == charWordIndex)
+ updateValue ();
+
+ return ret;
+}
+
+int AlignedTableCell::getValue ()
+{
+ Textblock::Word *word;
+ int i, wordIndex;
+ int w;
+
+ if (charWordIndex == -1)
+ wordIndex = words->size () -1;
+ else
+ wordIndex = charWordIndex;
+
+ w = 0;
+ for (i = 0; i < wordIndex; i++) {
+ word = words->getRef (i);
+ w += word->size.width + word->origSpace;
+ }
+
+ if (charWordIndex == -1) {
+ if (words->size () > 0) {
+ word = words->getRef (words->size () - 1);
+ w += word->size.width;
+ }
+ } else {
+ word = words->getRef (charWordIndex);
+ w += layout->textWidth (word->style->font, word->content.text,
+ charWordPos);
+ }
+
+ return w;
+}
+
+void AlignedTableCell::setMaxValue (int maxValue, int value)
+{
+ line1Offset = maxValue - value;
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), true);
+}
+
+} // namespace dw
diff --git a/dw/alignedtablecell.hh b/dw/alignedtablecell.hh
new file mode 100644
index 00000000..5ea606d7
--- /dev/null
+++ b/dw/alignedtablecell.hh
@@ -0,0 +1,44 @@
+#ifndef __DW_ALIGNEDTABLECELL_HH__
+#define __DW_ALIGNEDTABLECELL_HH__
+
+#include "core.hh"
+#include "alignedtextblock.hh"
+
+namespace dw {
+
+class AlignedTableCell: public AlignedTextblock
+{
+private:
+ int charWordIndex, charWordPos;
+
+protected:
+ int getAvailWidthOfChild (Widget *child, bool forceValue);
+ int getAvailHeightOfChild (Widget *child, bool forceValue);
+
+ void correctRequisitionOfChild (Widget *child,
+ core::Requisition *requisition,
+ void (*splitHeightFun) (int, int*, int*));
+ void correctExtremesOfChild (Widget *child, core::Extremes *extremes);
+
+ bool getAdjustMinWidth ();
+
+ int wordWrap (int wordIndex, bool wrapAll);
+
+ int getValue ();
+ void setMaxValue (int maxValue, int value);
+
+public:
+ static int CLASS_ID;
+
+ AlignedTableCell(AlignedTableCell *ref, bool limitTextWidth);
+ ~AlignedTableCell();
+
+ int applyPerWidth (int containerWidth, core::style::Length perWidth);
+ int applyPerHeight (int containerHeight, core::style::Length perHeight);
+
+ bool isBlockLevel ();
+};
+
+} // namespace dw
+
+#endif // __DW_ALIGNEDTABLECELL_HH__
diff --git a/dw/bullet.cc b/dw/bullet.cc
index af7f5451..acaf81cc 100644
--- a/dw/bullet.cc
+++ b/dw/bullet.cc
@@ -27,6 +27,12 @@ namespace dw {
Bullet::Bullet ()
{
+ DBG_OBJ_CREATE ("dw::Bullet");
+}
+
+Bullet::~Bullet ()
+{
+ DBG_OBJ_DELETE ();
}
void Bullet::sizeRequestImpl (core::Requisition *requisition)
@@ -36,6 +42,21 @@ void Bullet::sizeRequestImpl (core::Requisition *requisition)
requisition->descent = 0;
}
+void Bullet::getExtremesImpl (core::Extremes *extremes)
+{
+ extremes->minWidth = extremes->maxWidth =
+ lout::misc::max (getStyle()->font->xHeight * 4 / 5, 1);
+ extremes->minWidthIntrinsic = extremes->minWidth;
+ extremes->maxWidthIntrinsic = extremes->maxWidth;
+}
+
+void Bullet::containerSizeChangedForChildren ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
+ // Nothing to do.
+ DBG_OBJ_LEAVE ();
+}
+
void Bullet::draw (core::View *view, core::Rectangle *area)
{
int x, y, l;
diff --git a/dw/bullet.hh b/dw/bullet.hh
index 98854abb..004187cd 100644
--- a/dw/bullet.hh
+++ b/dw/bullet.hh
@@ -15,11 +15,14 @@ class Bullet: public core::Widget
{
protected:
void sizeRequestImpl (core::Requisition *requisition);
+ void getExtremesImpl (core::Extremes *extremes);
+ void containerSizeChangedForChildren ();
void draw (core::View *view, core::Rectangle *area);
core::Iterator *iterator (core::Content::Type mask, bool atEnd);
public:
Bullet ();
+ ~Bullet ();
};
} // namespace dw
diff --git a/dw/findtext.cc b/dw/findtext.cc
index cc57e991..94b963ea 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 ()) ;
@@ -221,7 +221,7 @@ bool FindtextState::unhighlight ()
return false;
}
-bool FindtextState::search0 (bool backwards, bool firstTrial)
+bool FindtextState::search0 (bool backwards, bool firstTrial)
{
if (iterator->getChar () == CharIterator::END)
return false;
diff --git a/dw/fltkimgbuf.cc b/dw/fltkimgbuf.cc
index 26b46969..01bce455 100644
--- a/dw/fltkimgbuf.cc
+++ b/dw/fltkimgbuf.cc
@@ -80,7 +80,7 @@ FltkImgbuf::FltkImgbuf (Type type, int width, int height, double gamma)
{
DBG_OBJ_CREATE ("dw::fltk::FltkImgbuf");
- _MSG("FltkImgbuf: new root %p\n", this);
+ _MSG ("FltkImgbuf::FltkImgbuf: new root %p\n", this);
init (type, width, height, gamma, NULL);
}
@@ -89,7 +89,7 @@ FltkImgbuf::FltkImgbuf (Type type, int width, int height, double gamma,
{
DBG_OBJ_CREATE ("dw::fltk::FltkImgbuf");
- _MSG("FltkImgbuf: new scaled %p, root is %p\n", this, root);
+ _MSG ("FltkImgbuf::FltkImgbuf: new scaled %p, root is %p\n", this, root);
init (type, width, height, gamma, root);
}
@@ -135,8 +135,8 @@ void FltkImgbuf::init (Type type, int width, int height, double gamma,
case RGB: bpp = 3; break;
default: bpp = 1; break;
}
- _MSG("FltkImgbuf::init width=%d height=%d bpp=%d gamma=%g\n",
- width, height, bpp, gamma);
+ _MSG("FltkImgbuf::init this=%p width=%d height=%d bpp=%d gamma=%g\n",
+ this, width, height, bpp, gamma);
rawdata = new uchar[bpp * width * height];
// Set light-gray as interim background color.
memset(rawdata, 222, width*height*bpp);
@@ -163,6 +163,8 @@ void FltkImgbuf::init (Type type, int width, int height, double gamma,
FltkImgbuf::~FltkImgbuf ()
{
+ _MSG ("FltkImgbuf::~FltkImgbuf\n");
+
if (!isRoot())
root->detachScaledBuf (this);
diff --git a/dw/fltkplatform.hh b/dw/fltkplatform.hh
index 2fb95633..cbf3c6f9 100644
--- a/dw/fltkplatform.hh
+++ b/dw/fltkplatform.hh
@@ -149,7 +149,7 @@ public:
void attachView (core::View *view);
- void detachView (core::View *view);
+ void detachView (core::View *view);
int textWidth (core::style::Font *font, const char *text, int len);
char *textToUpper (const char *text, int len);
diff --git a/dw/fltkui.cc b/dw/fltkui.cc
index 58bb2c6f..29008055 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,8 +213,14 @@ void FltkResource::detachView (FltkView *view)
void FltkResource::sizeAllocate (core::Allocation *allocation)
{
+ DBG_OBJ_ENTER ("resize", 0, "sizeAllocate", "%d, %d; %d * (%d + %d)",
+ allocation->x, allocation->y, allocation->width,
+ allocation->ascent, allocation->descent);
+
this->allocation = *allocation;
view->allocateFltkWidget (widget, allocation);
+
+ DBG_OBJ_LEAVE ();
}
void FltkResource::draw (core::View *view, core::Rectangle *area)
@@ -293,6 +303,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 +391,8 @@ Fl_Widget *FltkLabelButtonResource::createNewWidget (core::Allocation
void FltkLabelButtonResource::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest");
+
if (style) {
FltkFont *font = (FltkFont*)style->font;
fl_font(font->font,font->size);
@@ -380,6 +406,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_LEAVE ();
}
/*
@@ -513,6 +543,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 +675,8 @@ void FltkEntryResource::setDisplayed(bool displayed)
void FltkEntryResource::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest");
+
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_LEAVE ();
}
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,8 @@ void FltkMultiLineTextResource::setWidgetStyle (Fl_Widget *widget,
void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest");
+
if (style) {
FltkFont *font = (FltkFont*)style->font;
fl_font(font->font,font->size);
@@ -797,6 +851,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_LEAVE ();
}
const char *FltkMultiLineTextResource::getText ()
@@ -864,6 +922,8 @@ void FltkToggleButtonResource<I>::setWidgetStyle (Fl_Widget *widget,
template <class I>
void FltkToggleButtonResource<I>::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest");
+
FltkFont *font = (FltkFont *)
(this->FltkResource::style ? this->FltkResource::style->font : NULL);
@@ -877,6 +937,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_LEAVE ();
}
@@ -1116,6 +1180,8 @@ int FltkOptionMenuResource::getMaxItemWidth()
void FltkOptionMenuResource::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest");
+
if (style) {
FltkFont *font = (FltkFont*)style->font;
fl_font(font->font, font->size);
@@ -1130,6 +1196,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_LEAVE ();
}
void FltkOptionMenuResource::enlargeMenu ()
@@ -1405,6 +1475,8 @@ int FltkListResource::getMaxItemWidth()
void FltkListResource::sizeRequest (core::Requisition *requisition)
{
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest");
+
if (style) {
CustBrowser *b = (CustBrowser *) widget;
int height = b->full_height();
@@ -1425,6 +1497,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_LEAVE ();
}
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/hyphenator.cc b/dw/hyphenator.cc
index 819cc9b0..2811a818 100644
--- a/dw/hyphenator.cc
+++ b/dw/hyphenator.cc
@@ -244,7 +244,7 @@ bool Hyphenator::isCharPartOfActualWord (char *s)
int *Hyphenator::hyphenateWord(core::Platform *platform,
const char *word, int *numBreaks)
{
- if ((trie == NULL && exceptions ==NULL) || !isHyphenationCandidate (word)) {
+ if ((trie == NULL && exceptions == NULL) || !isHyphenationCandidate (word)) {
*numBreaks = 0;
return NULL;
}
diff --git a/dw/image.cc b/dw/image.cc
index 74f96e57..f6bf6434 100644
--- a/dw/image.cc
+++ b/dw/image.cc
@@ -149,11 +149,15 @@ Image::Image(const char *altText)
this->altText = altText ? strdup (altText) : NULL;
altTextWidth = -1; // not yet calculated
buffer = NULL;
+ bufWidth = bufHeight = -1;
clicking = false;
currLink = -1;
mapList = NULL;
mapKey = NULL;
isMap = false;
+
+ DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
+ DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
}
Image::~Image()
@@ -170,25 +174,11 @@ Image::~Image()
void Image::sizeRequestImpl (core::Requisition *requisition)
{
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl");
+
if (buffer) {
- if (getStyle ()->height == core::style::LENGTH_AUTO &&
- core::style::isAbsLength (getStyle ()->width) &&
- buffer->getRootWidth () > 0) {
- // preserve aspect ratio when only width is given
- requisition->width = core::style::absLengthVal (getStyle ()->width);
- requisition->ascent = buffer->getRootHeight () *
- requisition->width / buffer->getRootWidth ();
- } else if (getStyle ()->width == core::style::LENGTH_AUTO &&
- core::style::isAbsLength (getStyle ()->height) &&
- buffer->getRootHeight () > 0) {
- // preserve aspect ratio when only height is given
- requisition->ascent = core::style::absLengthVal (getStyle ()->height);
- requisition->width = buffer->getRootWidth () *
- requisition->ascent / buffer->getRootHeight ();
- } else {
- requisition->width = buffer->getRootWidth ();
- requisition->ascent = buffer->getRootHeight ();
- }
+ requisition->width = buffer->getRootWidth ();
+ requisition->ascent = buffer->getRootHeight ();
requisition->descent = 0;
} else {
if (altText && altText[0]) {
@@ -206,45 +196,113 @@ void Image::sizeRequestImpl (core::Requisition *requisition)
}
}
- requisition->width += getStyle()->boxDiffWidth ();
- requisition->ascent += getStyle()->boxOffsetY ();
- requisition->descent += getStyle()->boxRestHeight ();
+ requisition->width += boxDiffWidth ();
+ requisition->ascent += boxOffsetY ();
+ requisition->descent += boxRestHeight ();
+
+ correctRequisition (requisition, core::splitHeightPreserveDescent);
+
+ if (buffer) {
+ // If one dimension is set, preserve the aspect ratio (without
+ // extraSpace/margin/border/padding). Notice that
+ // requisition->descent could have been changed in
+ // core::splitHeightPreserveDescent, so we do not make any
+ // assumtions here about it (and requisition->ascent).
+
+ // TODO Check again possible overflows. (Aren't buffer
+ // dimensions limited to 2^15?)
+
+ bool widthSpecified = getStyle()->width != core::style::LENGTH_AUTO ||
+ getStyle()->minWidth != core::style::LENGTH_AUTO ||
+ getStyle()->maxWidth != core::style::LENGTH_AUTO;
+ bool heightSpecified = getStyle()->height != core::style::LENGTH_AUTO ||
+ getStyle()->minHeight != core::style::LENGTH_AUTO ||
+ getStyle()->maxHeight != core::style::LENGTH_AUTO;
+
+ if (!widthSpecified && heightSpecified)
+ requisition->width =
+ (requisition->ascent + requisition->descent - boxDiffHeight ())
+ * buffer->getRootWidth () / buffer->getRootHeight ()
+ + boxDiffWidth ();
+ else if (widthSpecified && !heightSpecified) {
+ requisition->ascent = (requisition->width + boxDiffWidth ())
+ * buffer->getRootHeight () / buffer->getRootWidth ()
+ + boxOffsetY ();
+ requisition->descent = boxRestHeight ();
+ }
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+ DBG_OBJ_LEAVE ();
+}
+
+void Image::getExtremesImpl (core::Extremes *extremes)
+{
+ int contentWidth;
+ if (buffer)
+ contentWidth = buffer->getRootWidth ();
+ else {
+ if (altText && altText[0]) {
+ if (altTextWidth == -1)
+ altTextWidth =
+ layout->textWidth (getStyle()->font, altText, strlen (altText));
+ contentWidth = altTextWidth;
+ } else
+ contentWidth = 0;
+ }
+
+ int width = contentWidth + boxDiffWidth ();
+
+ // With percentage width, the image may be narrower than the buffer.
+ extremes->minWidth =
+ core::style::isPerLength (getStyle()->width) ? boxDiffWidth () : width;
+
+ // (We ignore the same effect for the maximal width.)
+ extremes->maxWidth = width;
+
+ extremes->minWidthIntrinsic = extremes->minWidth;
+ extremes->maxWidthIntrinsic = extremes->maxWidth;
+
+ correctExtremes (extremes);
}
void Image::sizeAllocateImpl (core::Allocation *allocation)
{
- core::Imgbuf *oldBuffer;
- int dx, dy;
+ DBG_OBJ_ENTER ("resize", 0, "sizeAllocateImpl", "%d, %d; %d * (%d + %d)",
+ allocation->x, allocation->y, allocation->width,
+ allocation->ascent, allocation->descent);
+
- /* if image is moved only */
- if (allocation->width == this->allocation.width &&
- allocation->ascent + allocation->descent == getHeight ())
- return;
-
- dx = getStyle()->boxDiffWidth ();
- dy = getStyle()->boxDiffHeight ();
-#if 0
- MSG("boxDiffHeight = %d + %d, buffer=%p\n",
- getStyle()->boxOffsetY(), getStyle()->boxRestHeight(), buffer);
- MSG("getContentWidth() = allocation.width - style->boxDiffWidth ()"
- " = %d - %d = %d\n",
- this->allocation.width, getStyle()->boxDiffWidth(),
- this->allocation.width - getStyle()->boxDiffWidth());
- MSG("getContentHeight() = getHeight() - style->boxDiffHeight ()"
- " = %d - %d = %d\n", this->getHeight(), getStyle()->boxDiffHeight(),
- this->getHeight() - getStyle()->boxDiffHeight());
-#endif
- if (buffer &&
- (allocation->width - dx > 0 ||
- allocation->ascent + allocation->descent - dy > 0)) {
- // Zero content size : simply wait...
- // Only one dimension: naturally scale
- oldBuffer = buffer;
- buffer = oldBuffer->getScaledBuf (allocation->width - dx,
- allocation->ascent
- + allocation->descent - dy);
+ int newBufWidth = allocation->width - boxDiffWidth ();
+ int newBufHeight =
+ allocation->ascent + allocation->descent - boxDiffHeight ();
+
+ if (buffer && newBufWidth > 0 && newBufHeight > 0 &&
+ // Save some time when size did not change:
+ (newBufWidth != bufWidth || newBufHeight != bufHeight)) {
+ DBG_OBJ_MSG ("resize", 1, "replacing buffer");
+
+ core::Imgbuf *oldBuffer = buffer;
+ buffer = oldBuffer->getScaledBuf (newBufWidth, newBufHeight);
oldBuffer->unref ();
+
+ bufWidth = newBufWidth;
+ bufHeight = newBufHeight;
+
+ DBG_OBJ_ASSOC_CHILD (this->buffer);
+ DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
+ DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
}
+
+ DBG_OBJ_LEAVE ();
+}
+
+void Image::containerSizeChangedForChildren ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
+ // Nothing to do.
+ DBG_OBJ_LEAVE ();
}
void Image::enterNotifyImpl (core::EventCrossing *event)
@@ -425,19 +483,25 @@ void Image::setBuffer (core::Imgbuf *buffer, bool resize)
{
core::Imgbuf *oldBuf = this->buffer;
- if (resize)
- queueResize (0, true);
+ if (wasAllocated () && needsResize () &&
+ getContentWidth () > 0 && getContentHeight () > 0) {
+ // Don't create a new buffer for the transition from alt text to img,
+ // and only scale when both dimensions are known.
- if (wasAllocated () && getContentWidth () > 0 && getContentHeight () > 0) {
- // Only scale when both dimensions are known.
- this->buffer =
- buffer->getScaledBuf (getContentWidth (), getContentHeight ());
+ bufWidth = getContentWidth ();
+ bufHeight = getContentHeight ();
+ this->buffer = buffer->getScaledBuf (bufWidth, bufHeight);
} else {
this->buffer = buffer;
+ bufWidth = buffer->getRootWidth ();
+ bufHeight = buffer->getRootHeight ();
buffer->ref ();
}
+ queueResize (0, true);
DBG_OBJ_ASSOC_CHILD (this->buffer);
+ DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
+ DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
if (oldBuf)
oldBuf->unref ();
diff --git a/dw/image.hh b/dw/image.hh
index a712936e..9adf7806 100644
--- a/dw/image.hh
+++ b/dw/image.hh
@@ -121,6 +121,7 @@ class Image: public core::Widget, public core::ImgRenderer
private:
char *altText;
core::Imgbuf *buffer;
+ int bufWidth, bufHeight;
int altTextWidth;
bool clicking;
int currLink;
@@ -130,7 +131,9 @@ private:
protected:
void sizeRequestImpl (core::Requisition *requisition);
+ void getExtremesImpl (core::Extremes *extremes);
void sizeAllocateImpl (core::Allocation *allocation);
+ void containerSizeChangedForChildren ();
void draw (core::View *view, core::Rectangle *area);
diff --git a/dw/imgrenderer.cc b/dw/imgrenderer.cc
index 285a8dcd..14806ea2 100644
--- a/dw/imgrenderer.cc
+++ b/dw/imgrenderer.cc
@@ -1,3 +1,22 @@
+/*
+ * 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 "core.hh"
namespace dw {
diff --git a/dw/iterator.cc b/dw/iterator.cc
index 18d7cd5a..0edb580b 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..c2a53d08 100644
--- a/dw/layout.cc
+++ b/dw/layout.cc
@@ -91,6 +91,10 @@ void Layout::LayoutImgRenderer::draw (int x, int y, int width, int height)
// ----------------------------------------------------------------------
+void Layout::Receiver::resizeQueued (bool extremesChanged)
+{
+}
+
void Layout::Receiver::canvasSizeChanged (int width, int ascent, int descent)
{
}
@@ -110,6 +114,10 @@ bool Layout::Emitter::emitToReceiver (lout::signal::Receiver *receiver,
((Integer*)argv[2])->getValue ());
break;
+ case RESIZE_QUEUED:
+ layoutReceiver->resizeQueued (((Boolean*)argv[0])->getValue ());
+ break;
+
default:
misc::assertNotReached ();
}
@@ -117,6 +125,13 @@ bool Layout::Emitter::emitToReceiver (lout::signal::Receiver *receiver,
return false;
}
+void Layout::Emitter::emitResizeQueued (bool extremesChanged)
+{
+ Boolean ec (extremesChanged);
+ Object *argv[1] = { &ec };
+ emitVoid (RESIZE_QUEUED, 1, argv);
+}
+
void Layout::Emitter::emitCanvasSizeChanged (int width,
int ascent, int descent)
{
@@ -245,6 +260,9 @@ Layout::Layout (Platform *platform)
topLevel = NULL;
widgetAtPoint = NULL;
+ queueQueueResizeList = new typed::Stack<QueueResizeItem> (true);
+ queueResizeList = new typed::Vector<Widget> (4, false);
+
DBG_OBJ_CREATE ("dw::core::Layout");
bgColor = NULL;
@@ -259,6 +277,11 @@ Layout::Layout (Platform *platform)
viewportWidth = viewportHeight = 0;
hScrollbarThickness = vScrollbarThickness = 0;
+ DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth);
+ DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight);
+ DBG_OBJ_SET_NUM ("hScrollbarThickness", hScrollbarThickness);
+ DBG_OBJ_SET_NUM ("vScrollbarThickness", vScrollbarThickness);
+
requestedAnchor = NULL;
scrollIdleId = -1;
scrollIdleNotInterrupted = false;
@@ -277,7 +300,13 @@ Layout::Layout (Platform *platform)
selectionState.setLayout(this);
+ queueResizeCounter = sizeAllocateCounter = sizeRequestCounter =
+ getExtremesCounter = 0;
+
layoutImgRenderer = NULL;
+
+ resizeIdleCounter = queueResizeCounter = sizeAllocateCounter
+ = sizeRequestCounter = getExtremesCounter = 0;
}
Layout::~Layout ()
@@ -299,18 +328,45 @@ 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;
delete textZone;
+ if (requestedAnchor)
+ free (requestedAnchor);
+
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 +376,22 @@ void Layout::addWidget (Widget *widget)
topLevel = widget;
widget->layout = this;
+ widget->container = NULL;
+ DBG_OBJ_SET_PTR_O (widget, "container", widget->container);
+
+ queueResizeList->clear ();
+ widget->notifySetAsTopLevel ();
findtextState.setWidget (widget);
canvasHeightGreater = false;
- setSizeHints ();
- queueResize ();
+ DBG_OBJ_SET_SYM ("canvasHeightGreater",
+ canvasHeightGreater ? "true" : "false");
+
+ // 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 +400,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;
@@ -413,6 +480,11 @@ void Layout::attachView (View *view)
hScrollbarThickness = view->getHScrollbarThickness ();
vScrollbarThickness = view->getVScrollbarThickness ();
}
+
+ DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth);
+ DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight);
+ DBG_OBJ_SET_NUM ("hScrollbarThickness", hScrollbarThickness);
+ DBG_OBJ_SET_NUM ("vScrollbarThickness", vScrollbarThickness);
}
/*
@@ -647,7 +719,7 @@ void Layout::setAnchor (const char *anchor)
_MSG("setAnchor (%s)\n", anchor);
if (requestedAnchor)
- free(requestedAnchor);
+ free (requestedAnchor);
requestedAnchor = anchor ? strdup (anchor) : NULL;
updateAnchor ();
}
@@ -776,20 +848,62 @@ void Layout::setBgImage (style::StyleImage *bgImage,
void Layout::resizeIdle ()
{
+ DBG_OBJ_ENTER0 ("resize", 0, "resizeIdle");
+
+ 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;
- if (topLevel) {
+ // If this method is triggered by a viewport change, we can save
+ // time when the toplevel widget is not affected (as for a toplevel
+ // image resource).
+ if (topLevel && (topLevel->needsResize () || topLevel->needsAllocate ())) {
Requisition requisition;
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. (Cases where
+ // this method is triggered by a viewport change, but the
+ // toplevel widget is not affected, are filtered out some lines
+ // above: "if (topLevel && topLevel->needsResize ())".)
+ assert (topLevel->needsAllocate ());
allocation.x = allocation.y = 0;
allocation.width = requisition.width;
@@ -812,12 +926,12 @@ void Layout::resizeIdle ()
int currVThickness = currVScrollbarThickness();
if (!canvasHeightGreater &&
- canvasAscent + canvasDescent
- > viewportHeight - currHThickness) {
+ canvasAscent + canvasDescent > viewportHeight - currHThickness) {
canvasHeightGreater = true;
- setSizeHints ();
- /* May queue a new resize. */
- }
+ DBG_OBJ_SET_SYM ("canvasHeightGreater",
+ canvasHeightGreater ? "true" : "false");
+ containerSizeChanged ();
+ }
// Set viewport sizes.
view->setViewportSize (viewportWidth, viewportHeight,
@@ -825,20 +939,15 @@ void Layout::resizeIdle ()
}
// views are redrawn via Widget::resizeDrawImpl ()
-
}
updateAnchor ();
-}
-void Layout::setSizeHints ()
-{
- if (topLevel) {
- topLevel->setWidth (viewportWidth
- - (canvasHeightGreater ? vScrollbarThickness : 0));
- topLevel->setAscent (viewportHeight - hScrollbarThickness);
- topLevel->setDescent (0);
- }
+ DBG_OBJ_MSGF ("resize", 1,
+ "after resizeIdle: resizeIdleId = %d", resizeIdleId);
+ DBG_OBJ_LEAVE ();
+
+ leaveResizeIdle ();
}
void Layout::queueDraw (int x, int y, int width, int height)
@@ -877,13 +986,21 @@ void Layout::queueDrawExcept (int x, int y, int width, int height,
queueDraw (ix2, iy1, x + width - ix2, iy2 - iy1);
}
-void Layout::queueResize ()
+void Layout::queueResize (bool extremesChanged)
{
+ DBG_OBJ_ENTER ("resize", 0, "queueResize", "%s",
+ extremesChanged ? "true" : "false");
+
if (resizeIdleId == -1) {
view->cancelQueueDraw ();
resizeIdleId = platform->addIdle (&Layout::resizeIdle);
+ DBG_OBJ_MSGF ("resize", 1, "setting resizeIdleId = %d", resizeIdleId);
}
+
+ emitter.emitResizeQueued (extremesChanged);
+
+ DBG_OBJ_LEAVE ();
}
@@ -912,7 +1029,7 @@ bool Layout::buttonEvent (ButtonEventType type, View *view, int numPressed,
*
* Arguments are similar to dw::core::Layout::buttonPress.
*/
-bool Layout::motionNotify (View *view, int x, int y, ButtonState state)
+bool Layout::motionNotify (View *view, int x, int y, ButtonState state)
{
EventButton event;
@@ -978,7 +1095,7 @@ Widget *Layout::getWidgetAtPoint (int x, int y)
{
_MSG ("------------------------------------------------------------\n");
_MSG ("widget at (%d, %d)\n", x, y);
- if (topLevel)
+ if (topLevel && topLevel->wasAllocated ())
return topLevel->getWidgetAtPoint (x, y, 0);
else
return NULL;
@@ -1154,23 +1271,48 @@ void Layout::scrollPosChanged (View *view, int x, int y)
*/
void Layout::viewportSizeChanged (View *view, int width, int height)
{
- _MSG("Layout::viewportSizeChanged w=%d h=%d new_w=%d new_h=%d\n",
- viewportWidth, viewportHeight, width, height);
+ DBG_OBJ_ENTER ("resize", 0, "viewportSizeChanged", "%p, %d, %d",
+ view, width, height);
/* If the width has become higher, we test again, whether the vertical
* scrollbar (so to speak) can be hidden again. */
- if (usesViewport && width > viewportWidth)
+ if (usesViewport && width > viewportWidth) {
canvasHeightGreater = false;
+ DBG_OBJ_SET_SYM ("canvasHeightGreater",
+ canvasHeightGreater ? "true" : "false");
+ }
/* 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 (false);
+ }
viewportWidth = width;
viewportHeight = height;
- setSizeHints ();
+ DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth);
+ DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight);
+
+ containerSizeChanged ();
+
+ DBG_OBJ_LEAVE ();
+}
+
+void Layout::containerSizeChanged ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChanged");
+
+ if (topLevel) {
+ topLevel->containerSizeChanged ();
+ queueResize (true);
+ }
+
+ DBG_OBJ_LEAVE ();
}
} // namespace core
diff --git a/dw/layout.hh b/dw/layout.hh
index 47554b42..32b9a134 100644
--- a/dw/layout.hh
+++ b/dw/layout.hh
@@ -42,11 +42,12 @@ public:
/**
* \brief Receiver interface different signals.
*
- * May be extended
+ * May be extended.
*/
class Receiver: public lout::signal::Receiver
{
public:
+ virtual void resizeQueued (bool extremesChanged);
virtual void canvasSizeChanged (int width, int ascent, int descent);
};
@@ -126,7 +127,7 @@ private:
class Emitter: public lout::signal::Emitter
{
private:
- enum { CANVAS_SIZE_CHANGED };
+ enum { RESIZE_QUEUED, CANVAS_SIZE_CHANGED };
protected:
bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
@@ -135,6 +136,7 @@ private:
public:
inline void connectLayout (Receiver *receiver) { connect (receiver); }
+ void emitResizeQueued (bool extremesChanged);
void emitCanvasSizeChanged (int width, int ascent, int descent);
};
@@ -150,9 +152,28 @@ private:
~Anchor ();
};
+ class QueueResizeItem: public lout::object::Object
+ {
+ public:
+ Widget *widget;
+ int ref;
+ bool extremesChanged, fast;
+
+ inline QueueResizeItem (Widget *widget, int ref, bool extremesChanged,
+ bool fast)
+ {
+ this->widget = widget;
+ this->ref = ref;
+ this->extremesChanged = extremesChanged;
+ this->fast = fast;
+ }
+ };
+
Platform *platform;
View *view;
Widget *topLevel, *widgetAtPoint;
+ lout::container::typed::Stack<QueueResizeItem> *queueQueueResizeList;
+ lout::container::typed::Vector<Widget> *queueResizeList;
/* The state, which must be projected into the view. */
style::Color *bgColor;
@@ -186,6 +207,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);
@@ -233,9 +256,19 @@ private:
void queueDraw (int x, int y, int width, int height);
void queueDrawExcept (int x, int y, int width, int height,
int ex, int ey, int ewidth, int eheight);
- void queueResize ();
+ void queueResize (bool extremesChanged);
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 ();
@@ -297,6 +330,8 @@ public:
return buttonEvent (BUTTON_PRESS, view, numPressed, x, y, state, button);
}
+ void containerSizeChanged ();
+
/**
* \brief This function is called by a view, to delegate a button press
* event.
@@ -310,7 +345,7 @@ public:
button);
}
- bool motionNotify (View *view, int x, int y, ButtonState state);
+ bool motionNotify (View *view, int x, int y, ButtonState state);
void enterNotify (View *view, int x, int y, ButtonState state);
void leaveNotify (View *view, ButtonState state);
diff --git a/dw/listitem.cc b/dw/listitem.cc
index 05344d79..65293d8d 100644
--- a/dw/listitem.cc
+++ b/dw/listitem.cc
@@ -69,7 +69,7 @@ int ListItem::getValue ()
void ListItem::setMaxValue (int maxValue, int value)
{
- innerPadding = maxValue;
+ leftInnerPadding = maxValue;
line1Offset = - value;
redrawY = 0;
queueResize (0, true);
diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc
new file mode 100644
index 00000000..6fc1c325
--- /dev/null
+++ b/dw/outofflowmgr.cc
@@ -0,0 +1,2292 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2013-2014 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_ENTER_O ("resize.oofm", 0, widget, "update", "%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);
+
+ DBG_OBJ_LEAVE_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;
+ indexGBList = indexCBList = -1;
+
+ // Sometimes a float with widget = NULL is created as a key; this
+ // is not interesting for RTFL.
+ if (widget) {
+ DBG_OBJ_SET_PTR_O (widget, "<Float>.generatingBlock", generatingBlock);
+ DBG_OBJ_SET_NUM_O (widget, "<Float>.externalIndex", externalIndex);
+ 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);
+ DBG_OBJ_SET_BOOL_O (widget, "<Float>.dirty", dirty);
+ DBG_OBJ_SET_BOOL_O (widget, "<Float>.sizeChangedSinceLastAllocation",
+ sizeChangedSinceLastAllocation);
+ }
+}
+
+void OutOfFlowMgr::Float::updateAllocation ()
+{
+ DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation");
+
+ update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (),
+ getNewHeight ());
+
+ DBG_OBJ_LEAVE_O (getWidget ());
+}
+
+void OutOfFlowMgr::Float::intoStringBuffer(StringBuffer *sb)
+{
+ sb->append ("{ widget = ");
+ sb->appendPointer (getWidget ());
+
+ if (getWidget ()) {
+ sb->append (" (");
+ sb->append (getWidget()->getClassName ());
+ sb->append (")");
+ }
+
+ sb->append (", indexGBList = ");
+ sb->appendInt (indexGBList);
+ sb->append (", indexCBList = ");
+ sb->appendInt (indexCBList);
+ 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 (" }");
+}
+
+bool OutOfFlowMgr::Float::covers (Textblock *textblock, int y, int h)
+{
+ DBG_OBJ_ENTER_O ("border", 0, getOutOfFlowMgr (), "covers",
+ "%p, %d, %d [vloat: %p]",
+ textblock, y, h, getWidget ());
+
+ 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 {
+ // (If the textblock were not allocated, the GB list would have
+ // been choosen instead of the CB list, and so this else-branch
+ // would not have been not executed.)
+ 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),
+ *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_LEAVE_O (getOutOfFlowMgr ());
+
+ return b;
+}
+
+int OutOfFlowMgr::Float::ComparePosition::compare (Object *o1, Object *o2)
+{
+ Float *fl1 = (Float*)o1, *fl2 = (Float*)o2;
+ int r;
+
+ DBG_OBJ_ENTER_O ("border", 1, oofm,
+ "ComparePosition/compare", "(#%d, #%d) [refTB = %p]",
+ fl1->getIndex (type), fl2->getIndex (type), refTB);
+
+ 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);
+
+ DBG_OBJ_MSGF_O ("border", 2, oofm, "generators are %p and %p",
+ fl1->generatingBlock, fl2->generatingBlock);
+
+ // (i) Floats may not yet been allocated. Non-allocated floats
+ // do not have an effect yet, they are considered "at the end"
+ // of the list.
+
+ // (ii) Float::widget is NULL for the key used for binary
+ // search. In this case, Float::yReal is used instead (which is
+ // set in SortedFloatsVector::find, too). The generator is the
+ // textblock, and should be allocated. (If not, the GB list
+ // would have been choosen instead of the CB list, and so this
+ // else-branch would not have been not executed.)
+
+ 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 (%p) allocated: %s; float 2 (%p) allocated: %s",
+ fl1->getWidget (), a1 ? "yes" : "no", fl2->getWidget (),
+ a2 ? "yes" : "no");
+
+ if (a1 && a2) {
+ int fly1, fly2;
+
+ if (fl1->getWidget()) {
+ fly1 = fl1->getWidget()->getAllocation()->y;
+ DBG_OBJ_MSGF_O ("border", 2, oofm, "fly1 = %d", fly1);
+ } else {
+ assert (oofm->wasAllocated (fl1->generatingBlock));
+ fly1 = oofm->getAllocation(fl1->generatingBlock)->y + fl1->yReal;
+ DBG_OBJ_MSGF_O ("border", 2, oofm, "fly1 = %d + %d = %d",
+ oofm->getAllocation(fl1->generatingBlock)->y,
+ fl1->yReal, fly1);
+ }
+
+ if (fl2->getWidget()) {
+ fly2 = fl2->getWidget()->getAllocation()->y;
+ DBG_OBJ_MSGF_O ("border", 2, oofm, "fly2 = %d", fly2);
+ } else {
+ assert (oofm->wasAllocated (fl2->generatingBlock));
+ fly2 = oofm->getAllocation(fl2->generatingBlock)->y + fl2->yReal;
+ DBG_OBJ_MSGF_O ("border", 2, oofm, "fly2 = %d + %d = %d",
+ oofm->getAllocation(fl2->generatingBlock)->y,
+ fl2->yReal, fly2);
+ }
+
+ r = fly1 - fly2;
+
+ DBG_OBJ_MSGF_O ("border", 2, oofm, "r = %d - %d = %d", fly1, fly2, r);
+ } 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_LEAVE_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_ENTER_O ("border", 1, oofm, "CompareGBAndExtIndex/compare",
+ "#%d -> %p/%d, #%d -> %p/#%d",
+ f1->getIndex (type), f1->generatingBlock, f1->externalIndex,
+ f2->getIndex (type), f2->generatingBlock,
+ f2->externalIndex);
+
+ 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_LEAVE_O (oofm);
+ return r;
+}
+
+int OutOfFlowMgr::SortedFloatsVector::findFloatIndex (Textblock *lastGB,
+ int lastExtIndex)
+{
+ DBG_OBJ_ENTER_O ("border", 0, oofm, "findFloatIndex", "%p, %d",
+ lastGB, lastExtIndex);
+
+ Float key (oofm, NULL, lastGB, lastExtIndex);
+ key.setIndex (type, -1); // for debugging
+ Float::CompareGBAndExtIndex comparator (oofm, type);
+ 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_LEAVE_O (oofm);
+ return r;
+}
+
+int OutOfFlowMgr::SortedFloatsVector::find (Textblock *textblock, int y,
+ int start, int end)
+{
+ DBG_OBJ_ENTER_O ("border", 0, oofm, "find", "%p, %d, %d, %d",
+ textblock, y, start, end);
+
+ Float key (oofm, NULL, NULL, 0);
+ key.generatingBlock = textblock;
+ key.yReal = y;
+ key.setIndex (type, -1); // for debugging
+ Float::ComparePosition comparator (oofm, textblock, type);
+ int result = bsearch (&key, false, start, end, &comparator);
+
+ DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result);
+ DBG_OBJ_LEAVE_O (oofm);
+ return result;
+}
+
+int OutOfFlowMgr::SortedFloatsVector::findFirst (Textblock *textblock,
+ int y, int h,
+ Textblock *lastGB,
+ int lastExtIndex,
+ int *lastReturn)
+{
+ DBG_OBJ_ENTER_O ("border", 0, oofm, "findFirst", "%p, %d, %d, %p, %d",
+ textblock, y, h, lastGB, lastExtIndex);
+
+ 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++) {
+ 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, get(i)->getWidget (), get(i)->getIndex (type),
+ get(i)->sideSpanningIndex, get(i)->yReq, get(i)->yReal,
+ get(i)->size.width, get(i)->size.ascent,
+ get(i)->size.descent,
+ get(i)->dirty ? "dirty" : "clean",
+ get(i)->sizeChangedSinceLastAllocation ? "scsla"
+ : "sNcsla",
+ get(i)->externalIndex, get(i)->generatingBlock,
+ get(i)->getWidget()->getAllocation()->x,
+ get(i)->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());
+
+ // If the caller wants to reuse this value:
+ if (lastReturn)
+ *lastReturn = last;
+
+ 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_LEAVE_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->setIndex (type, size() - 1);
+}
+
+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, GB);
+ rightFloatsGB = new SortedFloatsVector (oofm, RIGHT, GB);
+
+ wasAllocated = getWidget()->wasAllocated ();
+ allocation = *(getWidget()->getAllocation ());
+ clearPosition = 0;
+}
+
+OutOfFlowMgr::TBInfo::~TBInfo ()
+{
+ delete leftFloatsGB;
+ delete rightFloatsGB;
+}
+
+void OutOfFlowMgr::TBInfo::updateAllocation ()
+{
+ DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation");
+
+ update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (),
+ getNewHeight ());
+
+ DBG_OBJ_LEAVE_O (getWidget ());
+}
+
+OutOfFlowMgr::OutOfFlowMgr (Textblock *containingBlock)
+{
+ DBG_OBJ_CREATE ("dw::OutOfFlowMgr");
+
+ this->containingBlock = containingBlock;
+
+ leftFloatsCB = new SortedFloatsVector (this, LEFT, CB);
+ rightFloatsCB = new SortedFloatsVector (this, RIGHT, CB);
+
+ DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size());
+ DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size());
+
+ 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;
+
+ containingBlockWasAllocated = containingBlock->wasAllocated ();
+ 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;
+
+ DBG_OBJ_DELETE ();
+}
+
+void OutOfFlowMgr::sizeAllocateStart (Textblock *caller, Allocation *allocation)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateStart",
+ "%p, (%d, %d, %d * (%d + %d))",
+ caller, allocation->x, allocation->y, allocation->width,
+ allocation->ascent, allocation->descent);
+
+ getTextblock(caller)->allocation = *allocation;
+ getTextblock(caller)->wasAllocated = true;
+
+ if (caller == containingBlock) {
+ // In the size allocation process, the *first* OOFM method
+ // called is sizeAllocateStart, with the containing block as an
+ // argument. So this is the correct point to initialize size
+ // allocation.
+
+ containingBlockAllocation = *allocation;
+ containingBlockWasAllocated = true;
+
+ // Move floats from GB lists to the one CB list.
+ moveFromGBToCB (LEFT);
+ moveFromGBToCB (RIGHT);
+
+ // These attributes are used to keep track which floats have
+ // been allocated (referring to leftFloatsCB and rightFloatsCB).
+ lastAllocatedLeftFloat = lastAllocatedRightFloat = -1;
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+void OutOfFlowMgr::sizeAllocateEnd (Textblock *caller)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateEnd", "%p", caller);
+
+ // (Later, absolutely positioned blocks have to be allocated.)
+
+ if (caller != containingBlock) {
+ // Allocate all floats "before" this textblock.
+ sizeAllocateFloats (LEFT, leftFloatsCB->findFloatIndex (caller, -1));
+ sizeAllocateFloats (RIGHT, rightFloatsCB->findFloatIndex (caller, -1));
+ }
+
+ // The checks below do not cover "clear position" in all cases, so
+ // this is done here separately. This position is stored in TBInfo
+ // and calculated at this points; changes will be noticed to the
+ // textblock.
+ TBInfo *tbInfo = getTextblock (caller);
+ int newClearPosition = calcClearPosition (caller);
+ if (newClearPosition != tbInfo->clearPosition) {
+ tbInfo->clearPosition = newClearPosition;
+ caller->clearPositionChanged ();
+ }
+
+ if (caller == containingBlock) {
+ // In the size allocation process, the *last* OOFM method called
+ // is sizeAllocateEnd, with the containing block as an
+ // argument. So this is the correct point to finish size
+ // allocation.
+
+ // Allocate all remaining floats.
+ sizeAllocateFloats (LEFT, leftFloatsCB->size () - 1);
+ sizeAllocateFloats (RIGHT, rightFloatsCB->size () - 1);
+
+ // 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);
+ }
+
+ checkAllocatedFloatCollisions (LEFT);
+ checkAllocatedFloatCollisions (RIGHT);
+
+ // 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->lineBreakWidth = tb->getLineBreakWidth ();
+ }
+
+ // 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_LEAVE ();
+}
+
+void OutOfFlowMgr::containerSizeChangedForChildren ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
+
+ DBG_OBJ_MSGF ("resize", 0, "%d left floats, %d right floats",
+ leftFloatsAll->size (), rightFloatsAll->size ());
+
+ for (int i = 0; i < leftFloatsAll->size (); i++)
+ leftFloatsAll->get(i)->getWidget()->containerSizeChanged ();
+ for (int i = 0; i < rightFloatsAll->size (); i++)
+ rightFloatsAll->get(i)->getWidget()->containerSizeChanged ();
+
+ DBG_OBJ_LEAVE ();
+}
+
+bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, int *minFloatPos,
+ Widget **minFloat)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged",
+ "<i>widget:</i> %p, ...", tbInfo->getWidget ());
+
+ 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_LEAVE ();
+ return c1 || c2;
+}
+
+bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, Side side,
+ int *minFloatPos, Widget **minFloat)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged",
+ "<i>widget:</i> %p, %s, ...",
+ tbInfo->getWidget (), side == LEFT ? "LEFT" : "RIGHT");
+
+ 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->getLineBreakWidth ());
+ 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->getNewWidth (),
+ tbInfo->getNewHeight (),
+ 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_LEAVE ();
+ 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_ENTER ("resize.oofm", 0, "hasRelationChanged",
+ "<i>see below</i>, %s, ...", side == LEFT ? "LEFT" : "RIGHT");
+
+ 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_LEAVE ();
+
+ return result;
+}
+
+void OutOfFlowMgr::checkAllocatedFloatCollisions (Side side)
+{
+ // In some cases, the collision detection in tellPosition() is
+ // based on the wrong allocations. Here (just after all Floats have
+ // been allocated), we correct this.
+
+ // TODO In some cases this approach is rather slow, causing a too
+ // long queueResize() cascade.
+
+ DBG_OBJ_ENTER ("resize.oofm", 0, "checkAllocatedFloatCollisions", "%s",
+ side == LEFT ? "LEFT" : "RIGHT");
+
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+ SortedFloatsVector *oppList = side == LEFT ? rightFloatsCB : leftFloatsCB;
+
+ // While iterating through the list of floats to be checked, we
+ // iterate equally through the list of the opposite floats, using
+ // this index:
+ int oppIndex = 0;
+
+ for (int index = 0; index < list->size (); index++) {
+ Float *vloat = list->get(index);
+ bool needsChange = false;
+ int yRealNew = INT_MAX;
+
+ // Same side.
+ if (index >= 1) {
+ Float *other = list->get(index - 1);
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "same side: checking %p (#%d, GB: %p) against "
+ "%p (#%d, GB: %p)",
+ vloat->getWidget (), index, vloat->generatingBlock,
+ other->getWidget (), index - 1, other->generatingBlock);
+
+ if (vloat->generatingBlock != other->generatingBlock) {
+ int yRealNewSame;
+ if (collidesV (vloat, other, CB, &yRealNewSame, true)) {
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "=> collides, new yReal = %d (old: %d)",
+ yRealNewSame, vloat->yReal);
+ if (vloat->yReal != yRealNewSame) {
+ needsChange = true;
+ yRealNew = min (yRealNew, yRealNewSame);
+ }
+ } else
+ DBG_OBJ_MSG ("resize.oofm", 1, "=> no collision");
+ }
+ }
+
+ if (oppList->size () > 0) {
+ // Other side. Iterate to next float on the other side,
+ // before this float.
+ while (oppIndex + 1 < oppList->size () &&
+ oppList->get(oppIndex + 1)->sideSpanningIndex
+ < vloat->sideSpanningIndex)
+ oppIndex++;
+
+ if (oppList->get(oppIndex)->sideSpanningIndex
+ < vloat->sideSpanningIndex) {
+ int oppIndexTmp = oppIndex, yRealNewOpp;
+
+ // Aproach is similar to tellPosition(); see comments
+ // there. Again, loop as long as the vertical dimensions test
+ // is positive (and, of course, there are floats), ...
+ for (bool foundColl = false;
+ !foundColl && oppIndexTmp >= 0 &&
+ collidesV (vloat, oppList->get (oppIndexTmp), CB,
+ &yRealNewOpp, true);
+ oppIndexTmp--) {
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "opposite side (after collision (v) test): "
+ "checking %p (#%d/%d, GB: %p) against "
+ "%p (#%d/%d, GB: %p)",
+ vloat->getWidget (), index,
+ vloat->sideSpanningIndex,
+ vloat->generatingBlock,
+ oppList->get(oppIndexTmp)->getWidget (),
+ oppList->get(oppIndexTmp)->getIndex (CB),
+ oppList->get(oppIndexTmp)->sideSpanningIndex,
+ oppList->get(oppIndexTmp)->generatingBlock);
+
+ // ... but stop the loop as soon as the horizontal dimensions
+ // test is positive.
+ if (collidesH (vloat, oppList->get (oppIndexTmp), CB)) {
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "=> collides (h), new yReal = %d (old: %d)",
+ yRealNewOpp, vloat->yReal);
+ foundColl = true;
+ if (vloat->yReal != yRealNewOpp) {
+ needsChange = true;
+ yRealNew = min (yRealNew, yRealNewOpp);
+ }
+ } else
+ DBG_OBJ_MSG ("resize.oofm", 1, "=> no collision (h)");
+ }
+ }
+ }
+
+ if (needsChange)
+ vloat->generatingBlock->borderChanged (min (vloat->yReal, yRealNew),
+ vloat->getWidget ());
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+bool OutOfFlowMgr::doFloatsExceedCB (Side side)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "doFloatsExceedCB", "%s",
+ side == LEFT ? "LEFT" : "RIGHT");
+
+ // This method is called to determine whether the *requisition* of
+ // the CB must be recalculated. So, we check the float allocations
+ // against the *requisition* of the CB, which may (e. g. within
+ // tables) differ from the new allocation. (Generally, a widget may
+ // allocated at a different size.)
+ core::Requisition cbReq;
+ containingBlock->sizeRequest (&cbReq);
+
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+ bool exceeds = false;
+
+ DBG_OBJ_MSG_START ();
+
+ for (int i = 0; i < list->size () && !exceeds; i++) {
+ Float *vloat = list->get (i);
+ if (vloat->getWidget()->wasAllocated ()) {
+ Allocation *fla = vloat->getWidget()->getAllocation ();
+ DBG_OBJ_MSGF ("resize.oofm", 2,
+ "Does FlA = (%d, %d, %d * %d) exceed CBA = "
+ "(%d, %d, %d * %d)?",
+ fla->x, fla->y, fla->width, fla->ascent + fla->descent,
+ containingBlockAllocation.x, containingBlockAllocation.y,
+ cbReq.width, cbReq.ascent + cbReq.descent);
+ if (fla->x + fla->width > containingBlockAllocation.x + cbReq.width ||
+ fla->y + fla->ascent + fla->descent
+ > containingBlockAllocation.y + cbReq.ascent + cbReq.descent) {
+ exceeds = true;
+ DBG_OBJ_MSG ("resize.oofm", 2, "Yes.");
+ } else
+ DBG_OBJ_MSG ("resize.oofm", 2, "No.");
+ }
+ }
+
+ DBG_OBJ_MSG_END ();
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", exceeds ? "true" : "false");
+ DBG_OBJ_LEAVE ();
+
+ return exceeds;
+}
+
+bool OutOfFlowMgr::haveExtremesChanged (Side side)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "haveExtremesChanged", "%s",
+ side == LEFT ? "LEFT" : "RIGHT");
+
+ // 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;
+ }
+ }
+ }
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", changed ? "true" : "false");
+ DBG_OBJ_LEAVE ();
+
+ return changed;
+}
+
+void OutOfFlowMgr::moveFromGBToCB (Side side)
+{
+ DBG_OBJ_ENTER ("oofm.resize", 0, "moveFromGBToCB", "%s",
+ side == LEFT ? "LEFT" : "RIGHT");
+
+ 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);
+ // "vloat->indexCBList == -1": prevent copying the vloat twice.
+ if (vloat->indexCBList == -1 && vloat->mark == mark) {
+ dest->put (vloat);
+ DBG_OBJ_MSGF ("oofm.resize", 1,
+ "moving float %p (mark %d) to CB list\n",
+ vloat->getWidget (), vloat->mark);
+ DBG_OBJ_SET_NUM (side == LEFT ?
+ "leftFloatsCB.size" : "rightFloatsCB.size",
+ dest->size());
+ DBG_OBJ_ARRATTRSET_PTR (side == LEFT ?
+ "leftFloatsCB" : "rightFloatsCB",
+ dest->size() - 1, "widget",
+ vloat->getWidget ());
+
+ }
+ }
+ }
+
+ *floatsMark = 0;
+
+ DBG_OBJ_LEAVE ();
+}
+
+void OutOfFlowMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat)
+{
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+ int *lastAllocatedFloat =
+ side == LEFT ? &lastAllocatedLeftFloat : &lastAllocatedRightFloat;
+
+ DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateFloats",
+ "%s, [%d ->] %d [size = %d]",
+ side == LEFT ? "LEFT" : "RIGHT", *lastAllocatedFloat,
+ newLastAllocatedFloat, list->size ());
+
+ Allocation *cba = &containingBlockAllocation;
+
+ for (int i = *lastAllocatedFloat + 1; i <= newLastAllocatedFloat; i++) {
+ Float *vloat = list->get(i);
+ ensureFloatSize (vloat);
+
+ Allocation *gba = getAllocation (vloat->generatingBlock);
+ int lineBreakWidth = vloat->generatingBlock->getLineBreakWidth();
+
+ Allocation childAllocation;
+ childAllocation.x = cba->x +
+ calcFloatX (vloat, side, gba->x - cba->x, gba->width, lineBreakWidth);
+ childAllocation.y = gba->y + vloat->yReal;
+ childAllocation.width = vloat->size.width;
+ childAllocation.ascent = vloat->size.ascent;
+ childAllocation.descent = vloat->size.descent;
+
+ vloat->getWidget()->sizeAllocate (&childAllocation);
+ }
+
+ *lastAllocatedFloat = newLastAllocatedFloat;
+
+ DBG_OBJ_LEAVE ();
+}
+
+
+/**
+ * \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 gbLineBreakWidth)
+{
+ DBG_OBJ_ENTER ("resize.common", 0, "calcFloatX", "%p, %s, %d, %d, %d",
+ vloat->getWidget (), side == LEFT ? "LEFT" : "RIGHT", gbX,
+ gbWidth, gbLineBreakWidth);
+ int x;
+
+ switch (side) {
+ case LEFT:
+ // Left floats are always aligned on the left side of the
+ // generator (content, not allocation) ...
+ x = gbX + vloat->generatingBlock->getStyle()->boxOffsetX();
+ DBG_OBJ_MSGF ("resize.common", 1, "left: x = %d + %d = %d",
+ gbX, vloat->generatingBlock->getStyle()->boxOffsetX(), x);
+ // ... but when the float exceeds the line break width of the
+ // container, it is corrected (but not left of the container).
+ // This way, we save space and, especially within tables, avoid
+ // some problems.
+ if (wasAllocated (containingBlock) &&
+ x + vloat->size.width > containingBlock->getLineBreakWidth ()) {
+ x = max (0, containingBlock->getLineBreakWidth () - vloat->size.width);
+ DBG_OBJ_MSGF ("resize.common", 1,
+ "corrected to: max (0, %d - %d) = %d",
+ containingBlock->getLineBreakWidth (), vloat->size.width,
+ x);
+ }
+ break;
+
+ case RIGHT:
+ // Similar for right floats, but in this case, floats are
+ // shifted to the right when they are too big (instead of
+ // shifting the generator to the right).
+
+ // Notice that not the actual width, but the line break width is
+ // used. (This changed for GROWS, where the width of a textblock
+ // is often smaller that the line break.)
+
+ x = max (gbX + gbLineBreakWidth - vloat->size.width
+ - vloat->generatingBlock->getStyle()->boxRestWidth(),
+ // Do not exceed CB allocation:
+ 0);
+ DBG_OBJ_MSGF ("resize.common", 1, "x = max (%d + %d - %d - %d, 0) = %d",
+ gbX, gbLineBreakWidth, vloat->size.width,
+ vloat->generatingBlock->getStyle()->boxRestWidth(), x);
+ break;
+
+ default:
+ assertNotReached ();
+ x = 0;
+ break;
+ }
+
+ DBG_OBJ_LEAVE ();
+ return x;
+}
+
+
+void OutOfFlowMgr::draw (View *view, Rectangle *area)
+{
+ drawFloats (leftFloatsCB, view, area);
+ drawFloats (rightFloatsCB, 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::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_ENTER ("construct.oofm", 0, "addWidgetOOF", "%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());
+ DBG_OBJ_ARRATTRSET_PTR ("leftFloatsAll", leftFloatsAll->size() - 1,
+ "widget", vloat->getWidget ());
+
+ widget->parentRef = createRefLeftFloat (leftFloatsAll->size() - 1);
+ DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef);
+ tbInfo->leftFloatsGB->put (vloat);
+
+ if (wasAllocated (generatingBlock)) {
+ leftFloatsCB->put (vloat);
+ DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size());
+ DBG_OBJ_ARRATTRSET_PTR ("leftFloatsCB", leftFloatsCB->size() - 1,
+ "widget", vloat->getWidget ());
+ } 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());
+ DBG_OBJ_ARRATTRSET_PTR ("rightFloatsAll", rightFloatsAll->size() - 1,
+ "widget", vloat->getWidget ());
+
+ widget->parentRef = createRefRightFloat (rightFloatsAll->size() - 1);
+ DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef);
+ tbInfo->rightFloatsGB->put (vloat);
+
+ if (wasAllocated (generatingBlock)) {
+ rightFloatsCB->put (vloat);
+ DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size());
+ DBG_OBJ_ARRATTRSET_PTR ("rightFloatsCB", rightFloatsCB->size() - 1,
+ "widget", vloat->getWidget ());
+ } 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
+ // May be extended.
+ assertNotReached();
+
+ DBG_OBJ_LEAVE ();
+}
+
+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;
+ DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.externalIndex",
+ vloat->externalIndex);
+ }
+ }
+}
+
+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_ENTER ("resize.oofm", 0, "markSizeChange", "%d", ref);
+
+ if (isRefFloat (ref)) {
+ Float *vloat;
+
+ if (isRefLeftFloat (ref)) {
+ int i = getFloatIndexFromRef (ref);
+ vloat = leftFloatsAll->get (i);
+ //printf (" => left float %d\n", i);
+ } else if (isRefRightFloat (ref)) {
+ int i = getFloatIndexFromRef (ref);
+ vloat = rightFloatsAll->get (i);
+ //printf (" => right float %d\n", i);
+ } else {
+ assertNotReached();
+ vloat = NULL; // compiler happiness
+ }
+
+ vloat->dirty = vloat->sizeChangedSinceLastAllocation = true;
+
+ DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.dirty", vloat->dirty);
+ DBG_OBJ_SET_BOOL_O (vloat->getWidget (),
+ "<Float>.sizeChangedSinceLastAllocation",
+ vloat->sizeChangedSinceLastAllocation);
+
+ // 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
+ assertNotReached();
+
+ DBG_OBJ_LEAVE ();
+}
+
+
+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);
+ 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);
+ if (vloat->getWidget()->wasAllocated ()) {
+ Widget *childAtPoint =
+ vloat->getWidget()->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_ENTER ("resize.oofm", 0, "tellFloatPosition", "%p, %d",
+ widget, yReq);
+
+ 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). Although there are (rare) cases
+ // where it could make sense, the horizontal dimensions are not
+ // tested; especially since searching and border calculation would
+ // be confused. For this reaspn, only the previous float is
+ // relevant. (Cf. below, collisions on the other side.)
+ int index = vloat->getIndex (listSame->type), yRealNew;
+ if (index >= 1 &&
+ collidesV (vloat, listSame->get (index - 1), listSame->type,
+ &yRealNew, false)) {
+ vloat->yReal = yRealNew;
+ DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal);
+ }
+
+ // Test collisions (on the opposite side). There are cases when
+ // more than one float has to be tested. Consider the following
+ // HTML snippet ("id" attribute only used for simple reference
+ // below, as #f1, #f2, and #f3):
+ //
+ // <div style="float:left" id="f1">
+ // Left left left left left left left left left left.
+ // </div>
+ // <div style="float:left" id="f2">Also left.</div>
+ // <div style="float:right" id="f3">Right.</div>
+ //
+ // When displayed with a suitable window width (only slightly wider
+ // than the text within #f1), this should look like this:
+ //
+ // ---------------------------------------------------------
+ // | Left left left left left left left left left left. |
+ // | Also left. Right. |
+ // ---------------------------------------------------------
+ //
+ // Consider float #f3: a collision test with #f2, considering
+ // vertical dimensions, is positive, but not the test with
+ // horizontal dimensions (because #f2 and #f3 are too
+ // narrow). However, a collision has to be tested with #f1;
+ // otherwise #f3 and #f1 would overlap.
+
+ int oppFloatIndex =
+ listOpp->findLastBeforeSideSpanningIndex (vloat->sideSpanningIndex);
+ // Generally, the rules are simple: loop as long as the vertical
+ // dimensions test is positive (and, of course, there are floats),
+ // ...
+ for (bool foundColl = false;
+ !foundColl && oppFloatIndex >= 0 &&
+ collidesV (vloat, listOpp->get (oppFloatIndex), listSame->type,
+ &yRealNew, false);
+ oppFloatIndex--) {
+ // ... but stop the loop as soon as the horizontal dimensions
+ // test is positive.
+ if (collidesH (vloat, listOpp->get (oppFloatIndex), listSame->type)) {
+ vloat->yReal = yRealNew;
+ DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal);
+ foundColl = true;
+ }
+ }
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "vloat->yReq = %d, vloat->yReal = %d",
+ vloat->yReq, vloat->yReal);
+
+ DBG_OBJ_LEAVE ();
+}
+
+bool OutOfFlowMgr::collidesV (Float *vloat, Float *other, SFVType type,
+ int *yReal, bool useAllocation)
+{
+ // 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_ENTER ("resize.oofm", 0, "collidesV", "#%d [%p], #%d [%p], ...",
+ vloat->getIndex (type), vloat->getWidget (),
+ other->getIndex (type), other->getWidget ());
+
+ 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 {
+ // 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 {
+ assert (wasAllocated (vloat->generatingBlock));
+ Allocation *gba = getAllocation (vloat->generatingBlock),
+ *flaOther = other->getWidget()->getAllocation ();
+
+ // We distinguish two cases (by different values of useAllocation):
+ // (i) within tellPosition, GB allocation + yReal is used for the
+ // y position of the other float, while (ii) in checkAllocatedFloat-
+ // Collisions, the float allocation is used. The latter is necessary
+ // by the definition of this method, the former increases performance,
+ // as compared to using the float allocation, in some cases, as in
+ // this:
+ //
+ // When '<div><div style="float:left">[Some text]</div></div>' is
+ // repeated n times, the resize idle function (Layout::resizeIdle)
+ // would be repeated roughly n times, when also in case (i) the float
+ // allocation is used, since for the collision test of float n with
+ // float n - 1, the allocation of float n - 1 does not yet reflect the
+ // collision test between n - 1 and n - 2, but yReal does for n - 1.
+ //
+ // On the other hand, the GB allocations will most likely more stable
+ // than the float allocations.
+ //
+ // Cases where this is incorrect will hopefully be rare, and, in any
+ // case, corrected in sizeAllocateEnd, either because hasRelation-
+ // Changed returns true, or in checkAllocatedFloatCollisions.
+
+ int otherFloatY = useAllocation ? flaOther->y :
+ getAllocation(other->generatingBlock)->y + other->yReal;
+ int otherBottomGB =
+ otherFloatY + flaOther->ascent + flaOther->descent - gba->y;
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "different generators: "
+ "otherBottomGB = %d + (%d + %d) - %d = %d",
+ otherFloatY, 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_LEAVE ();
+ return result;
+}
+
+
+bool OutOfFlowMgr::collidesH (Float *vloat, Float *other, SFVType type)
+{
+ // Only checks horizontal collision. For a complete test, use
+ // collidesV (...) && collidesH (...).
+ bool collidesH;
+
+ if (vloat->generatingBlock == other->generatingBlock)
+ collidesH = vloat->size.width + other->size.width
+ + vloat->generatingBlock->getStyle()->boxDiffWidth()
+ > vloat->generatingBlock->getLineBreakWidth();
+ else {
+ // 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 {
+ assert (wasAllocated (vloat->generatingBlock));
+ Allocation *gba = getAllocation (vloat->generatingBlock);
+ int vloatX =
+ calcFloatX (vloat,
+ vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT ?
+ LEFT : RIGHT,
+ gba->x, gba->width,
+ vloat->generatingBlock->getLineBreakWidth ());
+
+ // 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_ENTER0 ("resize.oofm", 0, "getSize");
+
+ int oofWidthtLeft, oofWidthRight, oofHeightLeft, oofHeightRight;
+ getFloatsSize (cbReq, LEFT, &oofWidthtLeft, &oofHeightLeft);
+ getFloatsSize (cbReq, RIGHT, &oofWidthRight, &oofHeightRight);
+
+ *oofWidth = max (oofWidthtLeft, oofWidthRight);
+ *oofHeight = max (oofHeightLeft, oofHeightRight);
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "=> (l: %d, r: %d => %d) * (l: %d, r: %d => %d)",
+ oofWidthtLeft, oofWidthRight, *oofWidth,
+ oofHeightLeft, oofHeightRight, *oofHeight);
+ DBG_OBJ_LEAVE ();
+}
+
+void OutOfFlowMgr::getFloatsSize (Requisition *cbReq, Side side, int *width,
+ int *height)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "getFloatsSize", "(%d * (%d + %d), %s, ...",
+ cbReq->width, cbReq->ascent, cbReq->descent,
+ side == LEFT ? "LEFT" : "RIGHT");
+
+ SortedFloatsVector *list = getFloatsListForTextblock (containingBlock, side);
+
+ *width = *height = 0;
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "%d floats on this side", list->size());
+
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "float %p has generator %p (container is %p)",
+ vloat->getWidget (), vloat->generatingBlock,
+ containingBlock);
+
+ 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->getLineBreakWidth ());
+ y = vloat->yReal;
+ } else {
+ Allocation *gba = getAllocation(vloat->generatingBlock);
+ x = calcFloatX (vloat, side,
+ gba->x - containingBlockAllocation.x, gba->width,
+ vloat->generatingBlock->getLineBreakWidth ());
+ 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_LEAVE ();
+}
+
+void OutOfFlowMgr::getExtremes (Extremes *cbExtr, int *oofMinWidth,
+ int *oofMaxWidth)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "getExtremes", "(%d / %d), ...",
+ cbExtr->minWidth, cbExtr->maxWidth);
+
+ int oofMinWidthtLeft, oofMinWidthRight, oofMaxWidthLeft, oofMaxWidthRight;
+ getFloatsExtremes (cbExtr, LEFT, &oofMinWidthtLeft, &oofMaxWidthLeft);
+ getFloatsExtremes (cbExtr, RIGHT, &oofMinWidthRight, &oofMaxWidthRight);
+
+ *oofMinWidth = max (oofMinWidthtLeft, oofMinWidthRight);
+ *oofMaxWidth = max (oofMaxWidthLeft, oofMaxWidthRight);
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "=> (l: %d, r: %d => %d) / (l: %d, r: %d => %d)",
+ oofMinWidthtLeft, oofMinWidthRight, *oofMinWidth,
+ oofMaxWidthLeft, oofMaxWidthRight, *oofMaxWidth);
+ DBG_OBJ_LEAVE ();
+}
+
+void OutOfFlowMgr::getFloatsExtremes (Extremes *cbExtr, Side side,
+ int *minWidth, int *maxWidth)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "getFloatsExtremes", "(%d / %d), %s, ...",
+ cbExtr->minWidth, cbExtr->maxWidth,
+ side == LEFT ? "LEFT" : "RIGHT");
+
+ *minWidth = *maxWidth = 0;
+
+ SortedFloatsVector *list = getFloatsListForTextblock (containingBlock, side);
+ DBG_OBJ_MSGF ("resize.oofm", 1, "%d floats to be examined", list->size());
+
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ int leftDiff, rightDiff;
+
+ if (getFloatDiffToCB (vloat, &leftDiff, &rightDiff)) {
+ Extremes extr;
+ vloat->getWidget()->getExtremes (&extr);
+
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "considering float %p generated by %p: %d / %d",
+ vloat->getWidget (), vloat->generatingBlock,
+ extr.minWidth, extr.maxWidth);
+
+ // 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_LEAVE ();
+}
+
+// Returns "false" when borders cannot yet determined; *leftDiff and
+// *rightDiff are undefined in this case.
+bool OutOfFlowMgr::getFloatDiffToCB (Float *vloat, int *leftDiff,
+ int *rightDiff)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "getDiffToCB",
+ "float %p [generated by %p], ...",
+ vloat->getWidget (), vloat->generatingBlock);
+
+ bool result;
+
+ if (vloat->generatingBlock == containingBlock) {
+ *leftDiff = vloat->generatingBlock->getStyle()->boxOffsetX();
+ *rightDiff = vloat->generatingBlock->getStyle()->boxRestWidth();
+ result = true;
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "GB == CB => leftDiff = %d, rightDiff = %d",
+ *leftDiff, *rightDiff);
+ } else if (wasAllocated (vloat->generatingBlock)) {
+ 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();
+ result = true;
+ 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);
+ } else {
+ DBG_OBJ_MSG ("resize.oofm", 1, "GB != CB, and float not allocated");
+ result = false;
+ }
+
+ DBG_OBJ_LEAVE ();
+ return result;
+}
+
+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_ENTER ("border", 0, "getBorder", "%p, %s, %d, %d, %p, %d",
+ textblock, side == LEFT ? "LEFT" : "RIGHT", y, h,
+ lastGB, lastExtIndex);
+
+ SortedFloatsVector *list = getFloatsListForTextblock (textblock, side);
+ int last;
+ int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, &last);
+
+ DBG_OBJ_MSGF ("border", 1, "first = %d", first);
+
+ if (first == -1) {
+ // No float.
+ DBG_OBJ_LEAVE ();
+ 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;
+
+ // We are not searching until the end of the list, but until the
+ // float defined by lastGB and lastExtIndex.
+ for (int i = first; covers && i <= last; 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.
+ thisBorder =
+ tba->x + textblock->getLineBreakWidth () - fla->x;
+ DBG_OBJ_MSGF ("border", 1,
+ "not GB: thisBorder = %d + %d - %d "
+ "= %d",
+ tba->x, textblock->getLineBreakWidth (), fla->x,
+ thisBorder);
+ }
+ }
+
+ border = max (border, thisBorder);
+ DBG_OBJ_MSGF ("border", 1, "=> border = %d", border);
+ }
+ }
+
+ DBG_OBJ_LEAVE ();
+ return border;
+ }
+}
+
+
+OutOfFlowMgr::SortedFloatsVector *OutOfFlowMgr::getFloatsListForTextblock
+ (Textblock *textblock, Side side)
+{
+ DBG_OBJ_ENTER ("oofm.common", 1, "getFloatsListForTextblock", "%p, %s",
+ textblock, side == LEFT ? "LEFT" : "RIGHT");
+
+ OutOfFlowMgr::SortedFloatsVector *list;
+
+ if (wasAllocated (textblock)) {
+ DBG_OBJ_MSG ("oofm.common", 2, "returning <b>CB</b> list");
+ list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+ } else {
+ DBG_OBJ_MSG ("oofm.common", 2, "returning <b>GB</b> list");
+ TBInfo *tbInfo = getTextblock (textblock);
+ list = side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB;
+ }
+
+ DBG_OBJ_LEAVE ();
+ return list;
+}
+
+
+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_ENTER ("border", 0, "hasFloat", "%p, %s, %d, %d, %p, %d",
+ textblock, side == LEFT ? "LEFT" : "RIGHT", y, h,
+ lastGB, lastExtIndex);
+
+ SortedFloatsVector *list = getFloatsListForTextblock (textblock, side);
+ int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, NULL);
+
+ DBG_OBJ_MSGF ("border", 1, "first = %d", first);
+ DBG_OBJ_LEAVE ();
+ return first != -1;
+}
+
+int OutOfFlowMgr::getLeftFloatHeight (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ return getFloatHeight (textblock, LEFT, y, h, lastGB, lastExtIndex);
+}
+
+int OutOfFlowMgr::getRightFloatHeight (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ return getFloatHeight (textblock, RIGHT, y, h, lastGB, lastExtIndex);
+}
+
+// Calculate height from the position *y*.
+int OutOfFlowMgr::getFloatHeight (Textblock *textblock, Side side, int y, int h,
+ Textblock *lastGB, int lastExtIndex)
+{
+ DBG_OBJ_ENTER ("border", 0, "getFloatHeight", "%p, %s, %d, %d, %p, %d",
+ textblock, side == LEFT ? "LEFT" : "RIGHT", y, h,
+ lastGB, lastExtIndex);
+
+ SortedFloatsVector *list = getFloatsListForTextblock (textblock, side);
+ int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, NULL);
+ assert (first != -1); /* This method must not be called when there is no
+ float on the respective side. */
+
+ Float *vloat = list->get(first);
+ int yRelToFloat;
+
+ if (vloat->generatingBlock == textblock) {
+ yRelToFloat = y - vloat->yReal;
+ DBG_OBJ_MSGF ("border", 1, "caller is CB: yRelToFloat = %d - %d = %d",
+ y, vloat->yReal, yRelToFloat);
+ } else {
+ // The respective widgets are allocated; otherwise, hasFloat() would have
+ // returned false.
+ assert (wasAllocated (textblock));
+ assert (vloat->getWidget()->wasAllocated ());
+
+ Allocation *tba = getAllocation(textblock),
+ *fla = vloat->getWidget()->getAllocation ();
+ yRelToFloat = tba->y + y - fla->y;
+
+ DBG_OBJ_MSGF ("border", 1,
+ "caller is not CB: yRelToFloat = %d + %d - %d = %d",
+ tba->y, y, fla->y, yRelToFloat);
+ }
+
+ ensureFloatSize (vloat);
+ int height = vloat->size.ascent + vloat->size.descent - yRelToFloat;
+
+ DBG_OBJ_MSGF ("border", 1, "=> (%d + %d) - %d = %d",
+ vloat->size.ascent, vloat->size.descent, yRelToFloat, height);
+ DBG_OBJ_LEAVE ();
+ return height;
+}
+
+/**
+ * Returns position relative to the textblock "tb".
+ */
+int OutOfFlowMgr::getClearPosition (Textblock *tb)
+{
+ return getTextblock(tb)->clearPosition;
+}
+
+int OutOfFlowMgr::calcClearPosition (Textblock *tb)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p", tb);
+
+ int pos;
+
+ 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 ();
+ }
+
+ pos = max (left ? calcClearPosition (tb, LEFT) : 0,
+ right ? calcClearPosition (tb, RIGHT) : 0);
+ } else
+ pos = 0;
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos);
+ DBG_OBJ_LEAVE ();
+
+ return pos;
+}
+
+int OutOfFlowMgr::calcClearPosition (Textblock *tb, Side side)
+{
+ DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p, %s",
+ tb, side == LEFT ? "LEFT" : "RIGHT");
+
+ int pos;
+
+ if (!wasAllocated (tb))
+ // There is no relation yet to floats generated by other
+ // textblocks, and this textblocks floats are unimportant for
+ // the "clear" property.
+ pos = 0;
+ else {
+ SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB;
+
+ // Search the last float before (therfore -1) this textblock.
+ int i = list->findFloatIndex (tb, -1);
+ if (i < 0) {
+ pos = 0;
+ DBG_OBJ_MSG ("resize.oofm", 1, "no float");
+ } else {
+ Float *vloat = list->get(i);
+ assert (vloat->generatingBlock != tb);
+ if (!wasAllocated (vloat->generatingBlock))
+ pos = 0; // See above.
+ else {
+ ensureFloatSize (vloat);
+ pos = max (getAllocation(vloat->generatingBlock)->y + vloat->yReal
+ + vloat->size.ascent + vloat->size.descent
+ - getAllocation(tb)->y,
+ 0);
+ DBG_OBJ_MSGF ("resize.oofm", 1,
+ "float %p => max (%d + %d + (%d + %d) - %d, 0)",
+ vloat->getWidget (),
+ getAllocation(vloat->generatingBlock)->y,
+ vloat->yReal, vloat->size.ascent, vloat->size.descent,
+ getAllocation(tb)->y);
+ }
+ }
+ }
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos);
+ DBG_OBJ_LEAVE ();
+
+ return pos;
+}
+
+void OutOfFlowMgr::ensureFloatSize (Float *vloat)
+{
+ // Historical note: relative sizes (e. g. percentages) are already
+ // handled by (at this time) Layout::containerSizeChanged, so
+ // Float::dirty will be set.
+
+ DBG_OBJ_ENTER ("resize.oofm", 0, "ensureFloatSize", "%p",
+ vloat->getWidget ());
+
+ if (vloat->dirty) {
+ DBG_OBJ_MSG ("resize.oofm", 1, "dirty: recalculation");
+
+ vloat->getWidget()->sizeRequest (&vloat->size);
+ vloat->cbLineBreakWidth = containingBlock->getLineBreakWidth ();
+ vloat->dirty = false;
+ DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.dirty", vloat->dirty);
+
+ DBG_OBJ_MSGF ("resize.oofm", 1, "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_LEAVE ();
+}
+
+} // namespace dw
diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh
new file mode 100644
index 00000000..24607bad
--- /dev/null
+++ b/dw/outofflowmgr.hh
@@ -0,0 +1,436 @@
+#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 };
+ enum SFVType { GB, CB };
+
+ Textblock *containingBlock;
+
+ // These two values are redundant to TBInfo::wasAllocated and
+ // TBInfo::allocation, for some special cases.
+ bool containingBlockWasAllocated;
+ core::Allocation containingBlockAllocation;
+
+ 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; }
+
+
+ void update (bool wasAllocated, int xCB, int yCB, int width, int height);
+
+ inline core::Widget *getWidget () { return widget; }
+ };
+
+ class Float: public WidgetInfo
+ {
+ public:
+ class ComparePosition: public lout::object::Comparator
+ {
+ private:
+ OutOfFlowMgr *oofm;
+ Textblock *refTB;
+ SFVType type; // actually only used for debugging
+
+ public:
+ ComparePosition (OutOfFlowMgr *oofm, Textblock *refTB, SFVType type)
+ { this->oofm = oofm; this->refTB = refTB; this->type = type; }
+ 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;
+ SFVType type; // actually only used for debugging
+
+ public:
+ CompareGBAndExtIndex (OutOfFlowMgr *oofm, SFVType type)
+ { this->oofm = oofm; this->type = type; }
+ int compare(Object *o1, Object *o2);
+ };
+
+ Textblock *generatingBlock;
+ int externalIndex;
+ int yReq, yReal; // relative to generator, not container
+ int indexGBList; /* Refers to TBInfo::leftFloatsGB or
+ TBInfo::rightFloatsGB, respectively. -1
+ initially. */
+ int indexCBList; /* Refers to leftFloatsCB or rightFloatsCB,
+ respectively. -1 initially. */
+ int sideSpanningIndex, mark;
+ core::Requisition size;
+ int cbLineBreakWidth; /* On which the calculation of relative sizes
+ is based. Height not yet used, and probably
+ not added before size redesign. */
+ bool dirty, sizeChangedSinceLastAllocation;
+
+ Float (OutOfFlowMgr *oofm, core::Widget *widget,
+ Textblock *generatingBlock, int externalIndex);
+
+ inline bool isNowAllocated () { return getWidget()->wasAllocated (); }
+ inline int getNewXCB () { return getWidget()->getAllocation()->x -
+ getOutOfFlowMgr()->containingBlockAllocation.x; }
+ inline int getNewYCB () { return getWidget()->getAllocation()->y -
+ getOutOfFlowMgr()->containingBlockAllocation.y; }
+ inline int getNewWidth () { return getWidget()->getAllocation()->width; }
+ inline int getNewHeight () { return getWidget()->getAllocation()->ascent +
+ getWidget()->getAllocation()->descent; }
+ void updateAllocation ();
+
+ inline int *getIndexRef (SFVType type) {
+ return type == GB ? &indexGBList : &indexCBList; }
+ inline int getIndex (SFVType type) { return *(getIndexRef (type)); }
+ inline void setIndex (SFVType type, int value) {
+ *(getIndexRef (type)) = value; }
+
+ 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:
+ SFVType type;
+
+ private:
+ OutOfFlowMgr *oofm;
+ Side side;
+
+ public:
+ inline SortedFloatsVector (OutOfFlowMgr *oofm, Side side, SFVType 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 *lastReturn);
+ 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 lineBreakWidth;
+ int index; // position within "tbInfos"
+
+ TBInfo *parent;
+ int parentExtIndex;
+
+ // These two values are set by sizeAllocateStart(), and they are
+ // accessable also within sizeAllocateEnd() for the same
+ // textblock, for which allocation and WAS_ALLOCATED is set
+ // *after* sizeAllocateEnd(). See the two functions
+ // wasAllocated(Widget*) and getAllocation(Widget*) (further
+ // down) for usage.
+ bool wasAllocated;
+ core::Allocation allocation;
+ int clearPosition;
+
+ // 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 bool isNowAllocated () {
+ return getOutOfFlowMgr()->wasAllocated (getTextblock ()); }
+ inline int getNewXCB () {
+ return getOutOfFlowMgr()->getAllocation (getTextblock ())->x -
+ getOutOfFlowMgr()->containingBlockAllocation.x; }
+ inline int getNewYCB () {
+ return getOutOfFlowMgr()->getAllocation (getTextblock ())->y -
+ getOutOfFlowMgr()->containingBlockAllocation.y; }
+ inline int getNewWidth () {
+ return getOutOfFlowMgr()->getAllocation (getTextblock ())->width; }
+ inline int getNewHeight () {
+ core::Allocation *allocation =
+ getOutOfFlowMgr()->getAllocation (getTextblock ());
+ return allocation->ascent + allocation->descent; }
+ void updateAllocation ();
+
+ inline Textblock *getTextblock () { return (Textblock*)getWidget (); }
+ };
+
+ // 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;
+
+ // These two attributes are used in the size allocation process;
+ // see sizeAllocateStart and sizeAllocateEnd.
+ int lastAllocatedLeftFloat, lastAllocatedRightFloat;
+
+ 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;
+
+ int lastLeftTBIndex, lastRightTBIndex, leftFloatsMark, rightFloatsMark;
+
+ /**
+ * Variant of Widget::wasAllocated(), which can also be used within
+ * OOFM::sizeAllocateEnd().
+ */
+ inline bool wasAllocated (Textblock *textblock) {
+ return getTextblock(textblock)->wasAllocated;
+ }
+
+ /**
+ * Variant of Widget::getAllocation(), which can also be used
+ * within OOFM::sizeAllocateEnd().
+ */
+ inline core::Allocation *getAllocation (Textblock *textblock) {
+ return &(getTextblock(textblock)->allocation);
+ }
+
+ void moveExternalIndices (SortedFloatsVector *list, int oldStartIndex,
+ int diff);
+ Float *findFloatByWidget (core::Widget *widget);
+
+ void moveFromGBToCB (Side side);
+ void sizeAllocateFloats (Side side, int newLastAllocatedFloat);
+ int calcFloatX (Float *vloat, Side side, int gbX, int gbWidth,
+ int gbLineBreakWidth);
+
+ 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);
+
+ void checkAllocatedFloatCollisions (Side side);
+
+ bool doFloatsExceedCB (Side side);
+ bool haveExtremesChanged (Side side);
+
+ void drawFloats (SortedFloatsVector *list, core::View *view,
+ core::Rectangle *area);
+ core::Widget *getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y,
+ int level);
+
+ bool collidesV (Float *vloat, Float *other, SFVType type, int *yReal,
+ bool useAllocation);
+ bool collidesH (Float *vloat, Float *other, SFVType type);
+
+ 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);
+ bool getFloatDiffToCB (Float *vloat, int *leftDiff, int *rightDiff);
+
+ 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 getFloatHeight (Textblock *textblock, Side side, int y, int h,
+ Textblock *lastGB, int lastExtIndex);
+
+ int calcClearPosition (Textblock *tb, Side side);
+ int calcClearPosition (Textblock *tb);
+
+ void ensureFloatSize (Float *vloat);
+
+ void tellFloatPosition (core::Widget *widget, int yReq);
+
+ static inline bool isStyleFloat (core::style::Style *style)
+ { return style->vloat != core::style::FLOAT_NONE; }
+ static inline bool isWidgetFloat (core::Widget *widget)
+ { return isStyleFloat (widget->getStyle()); }
+
+ /*
+ * 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: solved differently in the
+ * "dillo_grows" repository.
+ */
+
+ 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 int createRefLeftFloat (int index)
+ { return (index << 3) | 1; }
+ inline static int createRefRightFloat (int index)
+ { return (index << 3) | 5; }
+
+ inline static int getFloatIndexFromRef (int ref)
+ { return ref == -1 ? ref : (ref >> 3); }
+
+public:
+ OutOfFlowMgr (Textblock *containingBlock);
+ ~OutOfFlowMgr ();
+
+ void sizeAllocateStart (Textblock *caller, core::Allocation *allocation);
+ void sizeAllocateEnd (Textblock *caller);
+ void containerSizeChangedForChildren ();
+ 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 isStyleOutOfFlow (core::style::Style *style)
+ { return isStyleFloat (style); }
+ static inline bool isWidgetOutOfFlow (core::Widget *widget)
+ { return isStyleOutOfFlow (widget->getStyle()); }
+
+ 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 getLeftFloatHeight (Textblock *textblock, int y, int h,
+ Textblock *lastGB, int lastExtIndex);
+ int getRightFloatHeight (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(); }
+
+ inline core::Widget *getWidget (int i) {
+ if (i < leftFloatsAll->size())
+ return leftFloatsAll->get(i)->getWidget ();
+ else
+ return rightFloatsAll->get(i - leftFloatsAll->size())->getWidget ();
+ }
+
+ 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..3fdbfb6d 100644
--- a/dw/ruler.cc
+++ b/dw/ruler.cc
@@ -28,17 +28,41 @@ namespace dw {
Ruler::Ruler ()
{
- setFlags (BLOCK_LEVEL);
- unsetFlags (HAS_CONTENTS);
}
void Ruler::sizeRequestImpl (core::Requisition *requisition)
{
- requisition->width = getStyle()->boxDiffWidth ();
+ requisition->width =
+ lout::misc::max (getAvailWidth (true), getStyle()->boxDiffWidth ());
requisition->ascent = getStyle()->boxOffsetY ();
requisition->descent = getStyle()->boxRestHeight ();
}
+void Ruler::getExtremesImpl (core::Extremes *extremes)
+{
+ extremes->minWidth = extremes->maxWidth = getStyle()->boxDiffWidth ();
+ extremes->minWidthIntrinsic = extremes->minWidth;
+ extremes->maxWidthIntrinsic = extremes->maxWidth;
+ correctExtremes (extremes);
+}
+
+bool Ruler::isBlockLevel ()
+{
+ return true;
+}
+
+void Ruler::containerSizeChangedForChildren ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
+ // Nothing to do.
+ DBG_OBJ_LEAVE ();
+}
+
+bool Ruler::usesAvailWidth ()
+{
+ return true;
+}
+
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..1f3491bc 100644
--- a/dw/ruler.hh
+++ b/dw/ruler.hh
@@ -17,11 +17,16 @@ class Ruler: public core::Widget
{
protected:
void sizeRequestImpl (core::Requisition *requisition);
+ void getExtremesImpl (core::Extremes *extremes);
+ void containerSizeChangedForChildren ();
+ bool usesAvailWidth ();
void draw (core::View *view, core::Rectangle *area);
public:
Ruler ();
+ bool isBlockLevel ();
+
core::Iterator *iterator (core::Content::Type mask, bool atEnd);
};
diff --git a/dw/selection.hh b/dw/selection.hh
index ef9df0e0..3004d2d4 100644
--- a/dw/selection.hh
+++ b/dw/selection.hh
@@ -178,7 +178,7 @@ namespace core {
class SelectionState
{
public:
- enum { END_OF_WORD = 1 << 30 };
+ enum { END_OF_WORD = 1 << 30 };
private:
Layout *layout;
diff --git a/dw/simpletablecell.cc b/dw/simpletablecell.cc
new file mode 100644
index 00000000..02f92db6
--- /dev/null
+++ b/dw/simpletablecell.cc
@@ -0,0 +1,122 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2014 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 "simpletablecell.hh"
+#include "tablecell.hh"
+#include "../lout/misc.hh"
+#include "../lout/debug.hh"
+
+namespace dw {
+
+int SimpleTableCell::CLASS_ID = -1;
+
+SimpleTableCell::SimpleTableCell (bool limitTextWidth):
+ Textblock (limitTextWidth)
+{
+ DBG_OBJ_CREATE ("dw::SimpleTableCell");
+ registerName ("dw::SimpleTableCell", &CLASS_ID);
+}
+
+SimpleTableCell::~SimpleTableCell()
+{
+ DBG_OBJ_DELETE ();
+}
+
+bool SimpleTableCell::getAdjustMinWidth ()
+{
+ return tablecell::getAdjustMinWidth ();
+}
+
+bool SimpleTableCell::isBlockLevel ()
+{
+ return tablecell::isBlockLevel ();
+}
+
+int SimpleTableCell::getAvailWidthOfChild (Widget *child, bool forceValue)
+{
+ DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/getAvailWidthOfChild",
+ "%p, %s", child, forceValue ? "true" : "false");
+
+ int width = tablecell::correctAvailWidthOfChild
+ (this, child, Textblock::getAvailWidthOfChild (child, forceValue),
+ forceValue);
+
+ DBG_OBJ_LEAVE ();
+ return width;
+}
+
+int SimpleTableCell::getAvailHeightOfChild (Widget *child, bool forceValue)
+{
+ DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/getAvailHeightOfChild",
+ "%p, %s", child, forceValue ? "true" : "false");
+
+ int height = tablecell::correctAvailHeightOfChild
+ (this, child, Textblock::getAvailHeightOfChild (child, forceValue),
+ forceValue);
+
+ DBG_OBJ_LEAVE ();
+ return height;
+}
+
+void SimpleTableCell::correctRequisitionOfChild (Widget *child,
+ core::Requisition *requisition,
+ void (*splitHeightFun) (int,
+ int*,
+ int*))
+{
+ DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/correctRequisitionOfChild",
+ "%p, %d * (%d + %d), ...", child, requisition->width,
+ requisition->ascent, requisition->descent);
+
+ Textblock::correctRequisitionOfChild (child, requisition, splitHeightFun);
+ tablecell::correctCorrectedRequisitionOfChild (this, child, requisition,
+ splitHeightFun);
+
+ DBG_OBJ_LEAVE ();
+}
+
+void SimpleTableCell::correctExtremesOfChild (Widget *child,
+ core::Extremes *extremes)
+{
+ DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/correctExtremesOfChild",
+ "%p, %d (%d) / %d (%d)",
+ child, extremes->minWidth, extremes->minWidthIntrinsic,
+ extremes->maxWidth, extremes->maxWidthIntrinsic);
+
+ Textblock::correctExtremesOfChild (child, extremes);
+ tablecell::correctCorrectedExtremesOfChild (this, child, extremes);
+
+ DBG_OBJ_LEAVE ();
+}
+
+int SimpleTableCell::applyPerWidth (int containerWidth,
+ core::style::Length perWidth)
+{
+ return tablecell::applyPerWidth (this, containerWidth, perWidth);
+}
+
+int SimpleTableCell::applyPerHeight (int containerHeight,
+ core::style::Length perHeight)
+{
+ return tablecell::applyPerHeight (this, containerHeight, perHeight);
+}
+
+} // namespace dw
diff --git a/dw/simpletablecell.hh b/dw/simpletablecell.hh
new file mode 100644
index 00000000..4c18b454
--- /dev/null
+++ b/dw/simpletablecell.hh
@@ -0,0 +1,35 @@
+#ifndef __DW_SIMPLETABLECELL_HH__
+#define __DW_SIMPLETABLECELL_HH__
+
+#include "textblock.hh"
+
+namespace dw {
+
+class SimpleTableCell: public Textblock
+{
+protected:
+ int getAvailWidthOfChild (Widget *child, bool forceValue);
+ int getAvailHeightOfChild (Widget *child, bool forceValue);
+
+ void correctRequisitionOfChild (Widget *child,
+ core::Requisition *requisition,
+ void (*splitHeightFun) (int, int*, int*));
+ void correctExtremesOfChild (Widget *child, core::Extremes *extremes);
+
+ bool getAdjustMinWidth ();
+
+public:
+ static int CLASS_ID;
+
+ SimpleTableCell (bool limitTextWidth);
+ ~SimpleTableCell ();
+
+ int applyPerWidth (int containerWidth, core::style::Length perWidth);
+ int applyPerHeight (int containerHeight, core::style::Length perHeight);
+
+ bool isBlockLevel ();
+};
+
+} // namespace dw
+
+#endif // __DW_SIMPLETABLECELL_HH__
diff --git a/dw/style.cc b/dw/style.cc
index 8ec230a1..54d9af4b 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,12 @@ void StyleAttrs::initValues ()
backgroundPositionX = createPerLength (0);
backgroundPositionY = createPerLength (0);
width = height = lineHeight = LENGTH_AUTO;
+ minWidth = maxWidth = minHeight = maxHeight = LENGTH_AUTO;
+ vloat = FLOAT_NONE;
+ clear = CLEAR_NONE;
+ overflow = OVERFLOW_VISIBLE;
+ position = POSITION_STATIC;
+ top = bottom = left = right = LENGTH_AUTO;
textIndent = 0;
margin.setVal (0);
borderWidth.setVal (0);
@@ -101,6 +105,12 @@ void StyleAttrs::resetValues ()
valign = VALIGN_BASELINE;
textAlignChar = '.';
+ vloat = FLOAT_NONE; /** \todo Correct? Check specification. */
+ clear = CLEAR_NONE; /** \todo Correct? Check specification. */
+ overflow = OVERFLOW_VISIBLE;
+ position = POSITION_STATIC; /** \todo Correct? Check specification. */
+ top = bottom = left = right = LENGTH_AUTO; /** \todo Correct? Check
+ specification. */
backgroundColor = NULL;
backgroundImage = NULL;
backgroundRepeat = BACKGROUND_REPEAT;
@@ -109,6 +119,7 @@ void StyleAttrs::resetValues ()
backgroundPositionY = createPerLength (0);
width = LENGTH_AUTO;
height = LENGTH_AUTO;
+ minWidth = maxWidth = minHeight = maxHeight = LENGTH_AUTO;
margin.setVal (0);
borderWidth.setVal (0);
@@ -156,11 +167,23 @@ bool StyleAttrs::equals (object::Object *other) {
valign == otherAttrs->valign &&
textAlignChar == otherAttrs->textAlignChar &&
textTransform == otherAttrs->textTransform &&
+ vloat == otherAttrs->vloat &&
+ clear == otherAttrs->clear &&
+ overflow == otherAttrs->overflow &&
+ position == otherAttrs->position &&
+ top == otherAttrs->top &&
+ bottom == otherAttrs->bottom &&
+ left == otherAttrs->left &&
+ right == otherAttrs->right &&
hBorderSpacing == otherAttrs->hBorderSpacing &&
vBorderSpacing == otherAttrs->vBorderSpacing &&
wordSpacing == otherAttrs->wordSpacing &&
width == otherAttrs->width &&
height == otherAttrs->height &&
+ minWidth == otherAttrs->minWidth &&
+ maxWidth == otherAttrs->maxWidth &&
+ minHeight == otherAttrs->minHeight &&
+ maxHeight == otherAttrs->maxHeight &&
lineHeight == otherAttrs->lineHeight &&
textIndent == otherAttrs->textIndent &&
margin.equals (&otherAttrs->margin) &&
@@ -201,11 +224,23 @@ int StyleAttrs::hashValue () {
valign +
textAlignChar +
textTransform +
+ vloat +
+ clear +
+ overflow +
+ position +
+ top +
+ bottom +
+ left +
+ right +
hBorderSpacing +
vBorderSpacing +
wordSpacing +
width +
height +
+ minWidth +
+ maxWidth +
+ minHeight +
+ maxHeight +
lineHeight +
textIndent +
margin.hashValue () +
@@ -316,6 +351,14 @@ void Style::copyAttrs (StyleAttrs *attrs)
valign = attrs->valign;
textAlignChar = attrs->textAlignChar;
textTransform = attrs->textTransform;
+ vloat = attrs->vloat;
+ clear = attrs->clear;
+ overflow = attrs->overflow;
+ position = attrs->position;
+ top = attrs->top;
+ bottom = attrs->bottom;
+ left = attrs->left;
+ right = attrs->right;
hBorderSpacing = attrs->hBorderSpacing;
vBorderSpacing = attrs->vBorderSpacing;
wordSpacing = attrs->wordSpacing;
@@ -323,6 +366,10 @@ void Style::copyAttrs (StyleAttrs *attrs)
height = attrs->height;
lineHeight = attrs->lineHeight;
textIndent = attrs->textIndent;
+ minWidth = attrs->minWidth;
+ maxWidth = attrs->maxWidth;
+ minHeight = attrs->minHeight;
+ maxHeight = attrs->maxHeight;
margin = attrs->margin;
borderWidth = attrs->borderWidth;
padding = attrs->padding;
@@ -1157,18 +1204,26 @@ void drawBorder (View *view, Layout *layout, Rectangle *area,
*
* Otherwise, the caller should not try to increase the performance by
* doing some tests before; this is all done in this method.
+ *
+ * "bgColor" is passes implicitly. For non-inversed drawing,
+ * style->backgroundColor may simply used. However, when drawing is
+ * inversed, and style->backgroundColor is undefined (NULL), a
+ * background color defined higher in the hierarchy (which is not
+ * accessable here) must be used.
+ *
+ * (Background *images* are never drawn inverse.)
*/
void drawBackground (View *view, Layout *layout, Rectangle *area,
int x, int y, int width, int height,
int xRef, int yRef, int widthRef, int heightRef,
- Style *style, bool inverse, bool atTop)
+ Style *style, Color *bgColor, bool inverse, bool atTop)
{
- bool bgColor = style->backgroundColor != NULL &&
+ bool hasBgColor = bgColor != NULL &&
// The test for background colors is rather simple, since only the color
// has to be compared, ...
- (!atTop || layout->getBgColor () != style->backgroundColor);
- bool bgImage = (style->backgroundImage != NULL &&
- style->backgroundImage->getImgbufSrc() != NULL) &&
+ (!atTop || layout->getBgColor () != bgColor);
+ bool hasBgImage = (style->backgroundImage != NULL &&
+ style->backgroundImage->getImgbufSrc() != NULL) &&
// ... but for backgrounds, it would be rather complicated. To handle the
// two cases (normal HTML in a viewport, where the layout background
// image is set, and contents of <button> within a flat view, where the
@@ -1182,7 +1237,7 @@ void drawBackground (View *view, Layout *layout, Rectangle *area,
// necessary to draw the background if background color and image
// are not set (NULL), i. e. shining through.
- if (bgColor || bgImage) {
+ if (hasBgColor || hasBgImage) {
Rectangle bgArea, intersection;
bgArea.x = x;
bgArea.y = y;
@@ -1190,14 +1245,14 @@ void drawBackground (View *view, Layout *layout, Rectangle *area,
bgArea.height = height;
if (area->intersectsWith (&bgArea, &intersection)) {
- if (bgColor)
- view->drawRectangle (style->backgroundColor,
+ if (hasBgColor)
+ view->drawRectangle (bgColor,
inverse ?
Color::SHADING_INVERSE : Color::SHADING_NORMAL,
true, intersection.x, intersection.y,
intersection.width, intersection.height);
- if (bgImage)
+ if (hasBgImage)
drawBackgroundImage (view, style->backgroundImage,
style->backgroundRepeat,
style->backgroundAttachment,
diff --git a/dw/style.hh b/dw/style.hh
index e0ce9d89..230baa24 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,20 @@ enum FontVariant {
FONT_VARIANT_SMALL_CAPS
};
+enum Overflow {
+ OVERFLOW_VISIBLE,
+ OVERFLOW_HIDDEN,
+ OVERFLOW_SCROLL,
+ OVERFLOW_AUTO
+};
+
+enum Position {
+ POSITION_STATIC,
+ POSITION_RELATIVE,
+ POSITION_ABSOLUTE,
+ POSITION_FIXED,
+};
+
enum TextDecoration {
TEXT_DECORATION_NONE = 0,
TEXT_DECORATION_UNDERLINE = 1 << 0,
@@ -348,6 +361,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.
*
@@ -416,7 +442,8 @@ inline int absLengthVal(Length l) { return l >> 2; }
* When possible, do not use this function directly; it may be removed
* soon. Instead, use multiplyWithPerLength or multiplyWithPerLengthRounded.
*/
-inline double perLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); }
+inline double perLengthVal_useThisOnlyForDebugging(Length l)
+{ return (double)(l & ~3) / (1 << 18); }
/** \brief Returns the value of a relative length, as a float.
*
@@ -431,7 +458,7 @@ inline double relLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); }
* Use this instead of perLengthVal, when possible.
*/
inline int multiplyWithPerLength(int x, Length l) {
- return x * perLengthVal(l);
+ return x * perLengthVal_useThisOnlyForDebugging (l);
}
/**
@@ -440,8 +467,8 @@ inline int multiplyWithPerLength(int x, Length l) {
*
* (This function exists for backward compatibility.)
*/
-inline int multiplyWithPerLengthRounded (int x, Length l) {
- return lout::misc::roundInt (x * perLengthVal(l));
+inline int multiplyWithPerLengthRounded(int x, Length l) {
+ return lout::misc::roundInt (x * perLengthVal_useThisOnlyForDebugging (l));
}
inline int multiplyWithRelLength(int x, Length l) {
@@ -504,8 +531,17 @@ public:
char textAlignChar; /* In future, strings will be supported. */
TextTransform textTransform;
+ FloatType vloat; /* "float" is a keyword. */
+ ClearType clear;
+
+ Overflow overflow;
+
+ Position position;
+ Length top, bottom, left, right;
+
int hBorderSpacing, vBorderSpacing, wordSpacing;
Length width, height, lineHeight, textIndent;
+ Length minWidth, maxWidth, minHeight, maxHeight;
Box margin, borderWidth, padding;
BorderCollapse borderCollapse;
@@ -539,22 +575,14 @@ public:
= borderStyle.left = val; }
inline int boxOffsetX ()
- {
- return margin.left + borderWidth.left + padding.left;
- }
+ { return margin.left + borderWidth.left + padding.left; }
inline int boxRestWidth ()
- {
- return margin.right + borderWidth.right + padding.right;
- }
+ { return margin.right + borderWidth.right + padding.right; }
inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); }
inline int boxOffsetY ()
- {
- return margin.top + borderWidth.top + padding.top;
- }
+ { return margin.top + borderWidth.top + padding.top; }
inline int boxRestHeight ()
- {
- return margin.bottom + borderWidth.bottom + padding.bottom;
- }
+ { return margin.bottom + borderWidth.bottom + padding.bottom; }
inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); }
inline bool hasBackground ()
@@ -861,7 +889,7 @@ void drawBorder (View *view, Layout *layout, Rectangle *area,
void drawBackground (View *view, Layout *layout, Rectangle *area,
int x, int y, int width, int height,
int xRef, int yRef, int widthRef, int heightRef,
- Style *style, bool inverse, bool atTop);
+ Style *style, Color *bgColor, bool inverse, bool atTop);
void drawBackgroundImage (View *view, StyleImage *backgroundImage,
BackgroundRepeat backgroundRepeat,
BackgroundAttachment backgroundAttachment,
diff --git a/dw/table.cc b/dw/table.cc
index b6f7209b..30ee99c7 100644
--- a/dw/table.cc
+++ b/dw/table.cc
@@ -1,7 +1,7 @@
/*
* Dillo Widget
*
- * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
+ * Copyright 2005-2007, 2014 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
@@ -28,47 +28,48 @@ using namespace lout;
namespace dw {
+bool Table::adjustTableMinWidth = true;
int Table::CLASS_ID = -1;
Table::Table(bool limitTextWidth)
{
DBG_OBJ_CREATE ("dw::Table");
registerName ("dw::Table", &CLASS_ID);
- setFlags (BLOCK_LEVEL);
- setFlags (USES_HINTS);
setButtonSensitive(false);
this->limitTextWidth = limitTextWidth;
rowClosed = false;
- // random values
- availWidth = 100;
- availAscent = 100;
- availDescent = 0;
-
numRows = 0;
numCols = 0;
curRow = -1;
curCol = 0;
+ DBG_OBJ_SET_NUM ("numCols", numCols);
+ DBG_OBJ_SET_NUM ("numRows", numCols);
+
children = new misc::SimpleVector <Child*> (16);
colExtremes = new misc::SimpleVector<core::Extremes> (8);
+ colWidthSpecified = new misc::SimpleVector<bool> (8);
+ colWidthPercentage = new misc::SimpleVector<bool> (8);
colWidths = new misc::SimpleVector <int> (8);
cumHeight = new misc::SimpleVector <int> (8);
rowSpanCells = new misc::SimpleVector <int> (8);
- colSpanCells = new misc::SimpleVector <int> (8);
baseline = new misc::SimpleVector <int> (8);
rowStyle = new misc::SimpleVector <core::style::Style*> (8);
- hasColPercent = 0;
- colPercents = new misc::SimpleVector <core::style::Length> (8);
+ colWidthsUpToDateWidthColExtremes = true;
+ DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes",
+ colWidthsUpToDateWidthColExtremes);
+
+ numColWidthSpecified = 0;
+ numColWidthPercentage = 0;
redrawX = 0;
redrawY = 0;
}
-
Table::~Table()
{
for (int i = 0; i < children->size (); i++) {
@@ -91,20 +92,22 @@ Table::~Table()
delete children;
delete colExtremes;
+ delete colWidthSpecified;
+ delete colWidthPercentage;
delete colWidths;
delete cumHeight;
delete rowSpanCells;
- delete colSpanCells;
delete baseline;
delete rowStyle;
- delete colPercents;
DBG_OBJ_DELETE ();
}
void Table::sizeRequestImpl (core::Requisition *requisition)
{
- forceCalcCellSizes ();
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl");
+
+ forceCalcCellSizes (true);
/**
* \bug Baselines are not regarded here.
@@ -119,40 +122,46 @@ void Table::sizeRequestImpl (core::Requisition *requisition)
+ getStyle()->vBorderSpacing;
requisition->descent = 0;
+ correctRequisition (requisition, core::splitHeightPreserveDescent);
+
+ DBG_OBJ_LEAVE ();
}
void Table::getExtremesImpl (core::Extremes *extremes)
{
- if (numCols == 0) {
- extremes->minWidth = extremes->maxWidth = 0;
- return;
- }
+ DBG_OBJ_ENTER0 ("resize", 0, "getExtremesImpl");
- forceCalcColumnExtremes ();
+ if (numCols == 0)
+ extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth =
+ extremes->maxWidthIntrinsic = boxDiffWidth ();
+ else {
+ forceCalcColumnExtremes ();
- extremes->minWidth = extremes->maxWidth =
- (numCols + 1) * getStyle()->hBorderSpacing
- + getStyle()->boxDiffWidth ();
- for (int col = 0; col < numCols; col++) {
- extremes->minWidth += colExtremes->getRef(col)->minWidth;
- extremes->maxWidth += colExtremes->getRef(col)->maxWidth;
- }
- if (core::style::isAbsLength (getStyle()->width)) {
- extremes->minWidth =
- misc::max (extremes->minWidth,
- core::style::absLengthVal(getStyle()->width));
- extremes->maxWidth =
- misc::max (extremes->maxWidth,
- core::style::absLengthVal(getStyle()->width));
+ extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth =
+ extremes->maxWidthIntrinsic =
+ (numCols + 1) * getStyle()->hBorderSpacing + boxDiffWidth ();
+ for (int col = 0; col < numCols; col++) {
+ extremes->minWidth += colExtremes->getRef(col)->minWidth;
+ extremes->minWidthIntrinsic +=
+ colExtremes->getRef(col)->minWidthIntrinsic;
+ extremes->maxWidth += colExtremes->getRef(col)->maxWidth;
+ extremes->maxWidthIntrinsic +=
+ colExtremes->getRef(col)->maxWidthIntrinsic;
+ }
}
- _MSG(" Table::getExtremesImpl, {%d, %d} numCols=%d\n",
- extremes->minWidth, extremes->maxWidth, numCols);
+ correctExtremes (extremes);
+
+ DBG_OBJ_LEAVE ();
}
void Table::sizeAllocateImpl (core::Allocation *allocation)
{
- calcCellSizes ();
+ DBG_OBJ_ENTER ("resize", 0, "sizeAllocateImpl", "%d, %d; %d * (%d + %d)",
+ allocation->x, allocation->y, allocation->width,
+ allocation->ascent, allocation->descent);
+
+ calcCellSizes (true);
/**
* \bug Baselines are not regarded here.
@@ -167,8 +176,7 @@ void Table::sizeAllocateImpl (core::Allocation *allocation)
for (int row = 0; row < numRows; row++) {
int n = row * numCols + col;
if (childDefined (n)) {
- int width =
- (children->get(n)->cell.colspanEff - 1)
+ int width = (children->get(n)->cell.colspanEff - 1)
* getStyle()->hBorderSpacing;
for (int i = 0; i < children->get(n)->cell.colspanEff; i++)
width += colWidths->get (col + i);
@@ -192,6 +200,8 @@ void Table::sizeAllocateImpl (core::Allocation *allocation)
x += colWidths->get (col) + getStyle()->hBorderSpacing;
}
+
+ DBG_OBJ_LEAVE ();
}
void Table::resizeDrawImpl ()
@@ -202,30 +212,133 @@ void Table::resizeDrawImpl ()
redrawY = getHeight ();
}
-void Table::setWidth (int width)
+int Table::getAvailWidthOfChild (Widget *child, bool forceValue)
{
- // If limitTextWidth is set, a queueResize may also be necessary.
- if (availWidth != width || limitTextWidth) {
- _MSG(" Table::setWidth %d\n", width);
- availWidth = width;
- queueResize (0, false);
- }
+ DBG_OBJ_ENTER ("resize", 0, "getAvailWidthOfChild", "%p, %s",
+ child, forceValue ? "true" : "false");
+
+ int width;
+
+ // Unlike other containers, the table widget sometimes narrows
+ // columns to a width less than specified by CSS (see
+ // forceCalcCellSizes). For this reason, the column widths have to
+ // be calculated in all cases.
+ if (forceValue) {
+ calcCellSizes (false);
+ width = calcAvailWidthForDescendant (child);
+ } else
+ width = -1;
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", width);
+ DBG_OBJ_LEAVE ();
+ return width;
}
-void Table::setAscent (int ascent)
+int Table::calcAvailWidthForDescendant (Widget *child)
{
- if (availAscent != ascent) {
- availAscent = ascent;
- queueResize (0, false);
+ DBG_OBJ_ENTER ("resize", 0, "calcAvailWidthForDescendant", "%p", child);
+
+ // "child" is not a direct child, but a direct descendant. Search
+ // for the actual childs.
+ Widget *actualChild = child;
+ while (actualChild != NULL && actualChild->getParent () != this)
+ actualChild = actualChild->getParent ();
+
+ assert (actualChild != NULL);
+
+ // TODO This is inefficient. (Use parentRef?)
+ int width = -1;
+ for (int row = numRows - 1; width == -1 && row >= 0; row--) {
+ for (int col = 0; width == -1 && col < numCols; col++) {
+ int n = row * numCols + col;
+ if (childDefined (n) &&
+ children->get(n)->cell.widget == actualChild) {
+ DBG_OBJ_MSGF ("resize", 1, "calculated from column %d", col);
+ width = (children->get(n)->cell.colspanEff - 1)
+ * getStyle()->hBorderSpacing;
+ for (int i = 0; i < children->get(n)->cell.colspanEff; i++)
+ width += colWidths->get (col + i);
+ width = misc::max (width, 0);
+
+ if (child != actualChild) {
+ // For table cells (direct children: child == actualChild),
+ // CSS 'width' is already regarded in the column calculation.
+ // However, for children of the table cells, CSS 'width' must
+ // be regarded here.
+
+ int corrWidth = width;
+ child->calcFinalWidth (child->getStyle(), -1, this, 0, true,
+ &corrWidth);
+
+ // But better not exceed it ... (TODO: Only here?)
+ width = misc::min (width, corrWidth);
+ }
+ }
+ }
}
+
+ assert (width != -1);
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", width);
+ DBG_OBJ_LEAVE ();
+ return width;
+}
+
+int Table::applyPerWidth (int containerWidth, core::style::Length perWidth)
+{
+ return core::style::multiplyWithPerLength (containerWidth, perWidth);
}
-void Table::setDescent (int descent)
+int Table::applyPerHeight (int containerHeight, core::style::Length perHeight)
{
- if (availDescent != descent) {
- availDescent = descent;
- queueResize (0, false);
+ return core::style::multiplyWithPerLength (containerHeight, perHeight);
+}
+
+void Table::containerSizeChangedForChildren ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
+
+ for (int col = 0; col < numCols; col++) {
+ for (int row = 0; row < numRows; row++) {
+ int n = row * numCols + col;
+ if (childDefined (n))
+ children->get(n)->cell.widget->containerSizeChanged ();
+ }
}
+
+ DBG_OBJ_LEAVE ();
+}
+
+bool Table::affectsSizeChangeContainerChild (core::Widget *child)
+{
+ DBG_OBJ_ENTER ("resize", 0, "affectsSizeChangeContainerChild", "%p", child);
+
+ bool ret;
+
+ // This is a bit more complicated, as compared to the standard
+ // implementation (Widget::affectsSizeChangeContainerChild).
+ // Height would handled the same way, but width is more
+ // complicated: we would have to track numerous values here. Always
+ // returning true is correct in all cases, but generally
+ // inefficient.
+
+ // TODO Better solution?
+
+ ret = true;
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false");
+ DBG_OBJ_LEAVE ();
+ return ret;
+}
+
+bool Table::usesAvailWidth ()
+{
+ return true;
+}
+
+bool Table::isBlockLevel ()
+{
+ return true;
}
void Table::draw (core::View *view, core::Rectangle *area)
@@ -272,6 +385,9 @@ core::Iterator *Table::iterator (core::Content::Type mask, bool atEnd)
void Table::addCell (Widget *widget, int colspan, int rowspan)
{
+ DBG_OBJ_ENTER ("resize", 0, "addCell", "%p, %d, %d",
+ widget, colspan, rowspan);
+
const int maxspan = 100;
Child *child;
int colspanEff;
@@ -373,6 +489,8 @@ void Table::addCell (Widget *widget, int colspan, int rowspan)
}
MSG("\n");
#endif
+
+ DBG_OBJ_LEAVE ();
}
void Table::addRow (core::style::Style *style)
@@ -393,7 +511,7 @@ void Table::addRow (core::style::Style *style)
rowClosed = false;
}
-TableCell *Table::getCellRef ()
+AlignedTableCell *Table::getCellRef ()
{
core::Widget *child;
@@ -401,14 +519,119 @@ TableCell *Table::getCellRef ()
int n = curCol + row * numCols;
if (childDefined (n)) {
child = children->get(n)->cell.widget;
- if (child->instanceOf (TableCell::CLASS_ID))
- return (TableCell*)child;
+ if (child->instanceOf (AlignedTableCell::CLASS_ID))
+ return (AlignedTableCell*)child;
}
}
return NULL;
}
+const char *Table::getExtrModName (ExtrMod mod)
+{
+ switch (mod) {
+ case MIN:
+ return "MIN";
+
+ case MIN_INTR:
+ return "MIN_INTR";
+
+ case MIN_MIN:
+ return "MIN_MIN";
+
+ case MAX_MIN:
+ return "MAX_MIN";
+
+ case MAX:
+ return "MAX";
+
+ case MAX_INTR:
+ return "MAX_INTR";
+
+ case DATA:
+ return "DATA";
+
+ default:
+ misc::assertNotReached ();
+ return NULL;
+ }
+}
+
+int Table::getExtreme (core::Extremes *extremes, ExtrMod mod)
+{
+ switch (mod) {
+ case MIN:
+ return extremes->minWidth;
+
+ case MIN_INTR:
+ return extremes->minWidthIntrinsic;
+
+ case MIN_MIN:
+ return misc::min (extremes->minWidth, extremes->minWidthIntrinsic);
+
+ case MAX_MIN:
+ return misc::max (extremes->minWidth, extremes->minWidthIntrinsic);
+
+ case MAX:
+ return extremes->maxWidth;
+
+ case MAX_INTR:
+ return extremes->maxWidthIntrinsic;
+
+ default:
+ misc::assertNotReached ();
+ return 0;
+ }
+}
+
+void Table::setExtreme (core::Extremes *extremes, ExtrMod mod, int value)
+{
+ switch (mod) {
+ case MIN:
+ extremes->minWidth = value;
+ break;
+
+ case MIN_INTR:
+ extremes->minWidthIntrinsic = value;
+ break;
+
+ // MIN_MIN and MAX_MIN not supported here.
+
+ case MAX:
+ extremes->maxWidth = value;
+ break;
+
+ case MAX_INTR:
+ extremes->maxWidthIntrinsic = value;
+ break;
+
+ default:
+ misc::assertNotReached ();
+ }
+}
+
+int Table::getColExtreme (int col, ExtrMod mod, void *data)
+{
+ switch (mod) {
+ case DATA:
+ return ((misc::SimpleVector<int>*)data)->get (col);
+
+ default:
+ return getExtreme (colExtremes->getRef(col), mod);
+ }
+}
+
+void Table::setColExtreme (int col, ExtrMod mod, void *data, int value)
+{
+ switch (mod) {
+ case DATA:
+ ((misc::SimpleVector<int>*)data)->set (col, value);
+
+ default:
+ setExtreme (colExtremes->getRef(col), mod, value);
+ }
+}
+
void Table::reallocChildren (int newNumCols, int newNumRows)
{
assert (newNumCols >= numCols);
@@ -476,107 +699,357 @@ void Table::reallocChildren (int newNumCols, int newNumRows)
numCols = newNumCols;
numRows = newNumRows;
+
+ DBG_OBJ_SET_NUM ("numCols", numCols);
+ DBG_OBJ_SET_NUM ("numRows", numCols);
}
// ----------------------------------------------------------------------
-void Table::calcCellSizes ()
+void Table::calcCellSizes (bool calcHeights)
{
- if (needsResize ())
- forceCalcCellSizes ();
+ DBG_OBJ_ENTER ("resize", 0, "calcCellSizes", "%s",
+ calcHeights ? "true" : "false");
+
+ bool sizeChanged = needsResize () || resizeQueued ();
+ bool extremesChanges = extremesChanged () || extremesQueued ();
+
+ if (calcHeights ? (extremesChanges || sizeChanged) :
+ (extremesChanges || !colWidthsUpToDateWidthColExtremes))
+ forceCalcCellSizes (calcHeights);
+
+ DBG_OBJ_LEAVE ();
}
-void Table::forceCalcCellSizes ()
+void Table::forceCalcCellSizes (bool calcHeights)
{
- int totalWidth = 0, childHeight, forceTotalWidth = 1;
+ DBG_OBJ_ENTER0 ("resize", 0, "forceCalcCellSizes");
+
+ int childHeight;
core::Extremes extremes;
// Will also call calcColumnExtremes(), when needed.
getExtremes (&extremes);
- if (core::style::isAbsLength (getStyle()->width)) {
- totalWidth = core::style::absLengthVal (getStyle()->width);
- } else if (core::style::isPerLength (getStyle()->width)) {
- /*
- * If the width is > 100%, we use 100%, this prevents ugly
- * results. (May be changed in future, when a more powerful
- * rendering is implemented, to handle fixed positions etc.,
- * as defined by CSS2.)
- */
- totalWidth =
- misc::min (core::style::multiplyWithPerLength (availWidth,
- getStyle()->width),
- availWidth);
- } else if (getStyle()->width == core::style::LENGTH_AUTO) {
- totalWidth = availWidth;
- forceTotalWidth = 0;
+ int availWidth = getAvailWidth (true);
+ // When adjust_table_min_width is set, use the minimal (intrinsic)
+ // width for correction.
+ int corrWidth =
+ Table::getAdjustTableMinWidth () ? extremes.minWidthIntrinsic : 0;
+ int totalWidth = misc::max (availWidth, corrWidth)
+ - ((numCols + 1) * getStyle()->hBorderSpacing + boxDiffWidth ());
+
+ DBG_OBJ_MSGF ("resize", 1,
+ "totalWidth = max (%d, %d) - ((%d - 1) * %d + %d) = <b>%d</b>",
+ availWidth, corrWidth, numCols, getStyle()->hBorderSpacing,
+ boxDiffWidth (), totalWidth);
+
+ colWidths->setSize (numCols, 0);
+ cumHeight->setSize (numRows + 1, 0);
+ rowSpanCells->setSize (0);
+ baseline->setSize (numRows);
+
+ misc::SimpleVector<int> *oldColWidths = colWidths;
+ colWidths = new misc::SimpleVector <int> (8);
+
+ int minWidth = 0, minWidthIntrinsic = 0, maxWidth = 0;
+ for (int col = 0; col < colExtremes->size(); col++) {
+ minWidth += colExtremes->getRef(col)->minWidth;
+ minWidthIntrinsic += colExtremes->getRef(col)->minWidthIntrinsic;
+ maxWidth += colExtremes->getRef(col)->maxWidth;
}
- _MSG(" availWidth = %d\n", availWidth);
- _MSG(" totalWidth1 = %d\n", totalWidth);
+ // CSS 'width' defined and effective?
+ bool totalWidthSpecified = false;
+ if (getStyle()->width != core::style::LENGTH_AUTO) {
+ // Even if 'width' is defined, it may not have a defined value. We try
+ // this trick (should perhaps be replaced by a cleaner solution):
+ core::Requisition testReq = { -1, -1, -1 };
+ correctRequisition (&testReq, core::splitHeightPreserveDescent);
+ if (testReq.width != -1)
+ totalWidthSpecified = true;
+ }
- if (totalWidth < extremes.minWidth)
- totalWidth = extremes.minWidth;
- totalWidth = totalWidth
- - (numCols + 1) * getStyle()->hBorderSpacing
- - getStyle()->boxDiffWidth ();
+ DBG_OBJ_MSGF ("resize", 1,
+ "minWidth = %d, minWidthIntrinsic = %d, maxWidth %d, "
+ "totalWidth = %d, %s",
+ minWidth, minWidthIntrinsic, maxWidth, totalWidth,
+ totalWidthSpecified ? "specified" : "not specified");
+
+ if (minWidth > totalWidth) {
+ DBG_OBJ_MSG ("resize", 1, "case 1: minWidth > totalWidth");
+
+ // The sum of all column minima is larger than the available
+ // width, so we narrow the columns (see also CSS2 spec,
+ // section 17.5, #6). We use a similar apportioning, but not
+ // bases on minimal and maximal widths, but on intrinsic minimal
+ // widths and corrected minimal widths. This way, intrinsic
+ // extremes are preferred (so avoiding columns too narrow for
+ // the actual contents), at the expenses of corrected ones
+ // (which means that sometimes CSS values are handled
+ // incorrectly).
+
+ // A special case is a table with columns whose widths are
+ // defined by percentage values. In this case, all other columns
+ // are applied the intrinsic minimal width, while larger values
+ // are applied to the columns with percentage width (but not
+ // larger than the corrected width). The left columns are
+ // preferred, but it is ensured that no column is narrower than
+ // the intrinsic minimum.
+ //
+ // Example two columns with both "width: 70%" will be displayed like
+ // this:
+ //
+ // --------------------------------------------------
+ // | | |
+ // --------------------------------------------------
+ //
+ // The first gets indeed 70% of the total width, the second only
+ // the rest.
+ //
+ // This somewhat strange behaviour tries to mimic the somewhat
+ // strange behaviour of Firefox and Chromium.
+
+ if (numColWidthPercentage == 0 || minWidthIntrinsic >= totalWidth) {
+ // Latter case (minWidthIntrinsic >= totalWidth): special treating
+ // of percentage values would not make sense.
+
+ DBG_OBJ_MSG ("resize", 1, "case 1a: simple apportioning");
+
+ apportion2 (totalWidth, 0, colExtremes->size() - 1, MIN_MIN, MAX_MIN,
+ NULL, colWidths, 0);
+ } else {
+ DBG_OBJ_MSG ("resize", 1, "case 1b: treat percentages specially");
- _MSG(" totalWidth2 = %d curCol=%d\n", totalWidth,curCol);
+ colWidths->setSize (colExtremes->size(), 0);
+ // Keep track of the width which is apportioned to the rest
+ // of the columns with percentage width (widthPartPer), and
+ // the minimal width (intrinsic minimum) which is needed for
+ // the rest of these columns (minWidthIntrinsicPer).
- colWidths->setSize (numCols, 0);
- cumHeight->setSize (numRows + 1, 0);
- rowSpanCells->setSize (0);
- baseline->setSize (numRows);
+ int widthPartPer = totalWidth, minWidthIntrinsicPer = 0;
+ for (int col = 0; col < colExtremes->size(); col++)
+ if (colWidthPercentage->get (col))
+ minWidthIntrinsicPer +=
+ colExtremes->getRef(col)->minWidthIntrinsic;
+ else
+ // Columns without percentage width get only the
+ // intrinsic mininal, so subtract this from the width for the
+ // columns *with* percentage
+ widthPartPer -=
+ colExtremes->getRef(col)->minWidthIntrinsic;
- _MSG(" extremes = %d,%d\n", extremes.minWidth, extremes.maxWidth);
- _MSG(" getStyle()->boxDiffWidth() = %d\n", getStyle()->boxDiffWidth());
- _MSG(" getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing);
+ DBG_OBJ_MSGF ("resize", 1,
+ "widthPartPer = %d, minWidthIntrinsicPer = %d",
+ widthPartPer, minWidthIntrinsicPer);
+ for (int col = 0; col < colExtremes->size(); col++)
+ if (colWidthPercentage->get (col)) {
+ int colWidth = colExtremes->getRef(col)->minWidth;
+ int minIntr = colExtremes->getRef(col)->minWidthIntrinsic;
- apportion_percentages2 (totalWidth, forceTotalWidth);
- if (!hasColPercent)
- apportion2 (totalWidth, forceTotalWidth);
+ minWidthIntrinsicPer -= minIntr;
- setCumHeight (0, 0);
- for (int row = 0; row < numRows; row++) {
- /**
- * \bug dw::Table::baseline is not filled.
- */
- int rowHeight = 0;
+ if (colWidth > widthPartPer - minWidthIntrinsicPer)
+ colWidth = widthPartPer - minWidthIntrinsicPer;
- for (int col = 0; col < numCols; col++) {
- int n = row * numCols + col;
- if (childDefined (n)) {
- int width = (children->get(n)->cell.colspanEff - 1)
- * getStyle()->hBorderSpacing;
- for (int i = 0; i < children->get(n)->cell.colspanEff; i++)
- width += colWidths->get (col + i);
+ colWidths->set (col, colWidth);
+ widthPartPer -= colWidth;
- core::Requisition childRequisition;
- children->get(n)->cell.widget->setWidth (width);
- children->get(n)->cell.widget->sizeRequest (&childRequisition);
- childHeight = childRequisition.ascent + childRequisition.descent;
- if (children->get(n)->cell.rowspan == 1) {
- rowHeight = misc::max (rowHeight, childHeight);
+ DBG_OBJ_MSGF ("resize", 1,
+ "#%d: colWidth = %d ... widthPartPer = %d, "
+ "minWidthIntrinsicPer = %d",
+ col, colWidth, widthPartPer, minWidthIntrinsicPer);
+
+ } else
+ colWidths->set (col,
+ colExtremes->getRef(col)->minWidthIntrinsic);
+
+ }
+ } else if (totalWidthSpecified && totalWidth > maxWidth) {
+ DBG_OBJ_MSG ("resize", 1,
+ "case 2: totalWidthSpecified && totalWidth > maxWidth");
+
+ // The width is specified (and so enforced), but all maxima sum
+ // up to less than this specified width. The columns will have
+ // there maximal width, and the extra space is apportioned
+ // according to the column widths, and so to the column
+ // maxima. This is done by simply passing MAX twice to the
+ // apportioning function.
+
+ // When column widths are specified (numColWidthSpecified > 0,
+ // as calculated in forceCalcColumnExtremes()), they are treated
+ // specially and excluded from the apportioning, so that the
+ // specified column widths are enforced. An exception is when
+ // all columns are specified: in this case they must be
+ // enlargened to fill the whole table width.
+
+ if (numColWidthSpecified == 0 ||
+ numColWidthSpecified == colExtremes->size()) {
+ DBG_OBJ_MSG ("resize", 1,
+ "subcase 2a: no or all columns with specified width");
+ apportion2 (totalWidth, 0, colExtremes->size() - 1, MAX, MAX, NULL,
+ colWidths, 0);
+ } else {
+ DBG_OBJ_MSGF ("resize", 1,
+ "subcase 2b: %d column(s) with specified width",
+ numColWidthSpecified);
+
+ // Seperate columns with specified and unspecified width, and
+ // apply apportion2() only to the latter.
+
+ int numNotSpecified = colExtremes->size() - numColWidthSpecified;
+
+ misc::SimpleVector<int> widthsNotSpecified (numNotSpecified);
+ widthsNotSpecified.setSize (numNotSpecified);
+ misc::SimpleVector<int> apportionDest (numNotSpecified);
+
+ int totalWidthNotSpecified = totalWidth, indexNotSpecified = 0;
+ for (int col = 0; col < colExtremes->size(); col++)
+ if (colWidthSpecified->get (col))
+ totalWidthNotSpecified -= colExtremes->getRef(col)->maxWidth;
+ else {
+ widthsNotSpecified.set (indexNotSpecified,
+ colExtremes->getRef(col)->maxWidth);
+ indexNotSpecified++;
+ }
+
+ DBG_IF_RTFL {
+ DBG_OBJ_MSGF ("resize", 1, "totalWidthNotSpecified = %d",
+ totalWidthNotSpecified);
+
+ DBG_OBJ_MSG ("resize", 1, "widthsNotSpecified:");
+ DBG_OBJ_MSG_START ();
+
+ for (int i = 0; i < widthsNotSpecified.size (); i++)
+ DBG_OBJ_MSGF ("resize", 1, "#%d: %d",
+ i, widthsNotSpecified.get (i));
+
+ DBG_OBJ_MSG_END ();
+ }
+
+ apportion2 (totalWidthNotSpecified, 0, numNotSpecified - 1, DATA, DATA,
+ (void*)&widthsNotSpecified, &apportionDest, 0);
+
+ DBG_IF_RTFL {
+ DBG_OBJ_MSG ("resize", 1, "apportionDest:");
+ DBG_OBJ_MSG_START ();
+
+ for (int i = 0; i < apportionDest.size (); i++)
+ DBG_OBJ_MSGF ("resize", 1, "#%d: %d", i, apportionDest.get (i));
+
+ DBG_OBJ_MSG_END ();
+ }
+
+ DBG_OBJ_MSG ("resize", 1, "finally setting column widths:");
+ DBG_OBJ_MSG_START ();
+
+ colWidths->setSize (colExtremes->size());
+ indexNotSpecified = 0;
+ for (int col = 0; col < colExtremes->size(); col++)
+ if (colWidthSpecified->get (col)) {
+ DBG_OBJ_MSGF ("resize", 1, "#%d: specified, gets maximum %d",
+ col, colExtremes->getRef(col)->maxWidth);
+ colWidths->set (col, colExtremes->getRef(col)->maxWidth);
} else {
- rowSpanCells->increase();
- rowSpanCells->set(rowSpanCells->size()-1, n);
+ DBG_OBJ_MSGF ("resize", 1, "#%d: not specified, gets value %d "
+ "at position %d from temporary list",
+ col, apportionDest.get (indexNotSpecified),
+ indexNotSpecified);
+ colWidths->set (col, apportionDest.get (indexNotSpecified));
+ indexNotSpecified++;
}
+
+ DBG_OBJ_MSG_END ();
+ }
+ } else {
+ // Normal apportioning.
+ int width =
+ totalWidthSpecified ? totalWidth : misc::min (totalWidth, maxWidth);
+ DBG_OBJ_MSGF ("resize", 1, "case 3: else; width = %d", width);
+ apportion2 (width, 0, colExtremes->size() - 1, MIN, MAX, NULL, colWidths,
+ 0);
+ }
+
+ // TODO: Adapted from old inline function "setColWidth". But (i) is
+ // this anyway correct (col width is is not x)? And does the
+ // performance gain actually play a role?
+ for (int col = 0; col < colExtremes->size(); col++) {
+ if (colWidths->get (col) != oldColWidths->get (col))
+ redrawX = lout::misc::min (redrawX, colWidths->get (col));
+ }
+
+ DBG_IF_RTFL {
+ DBG_OBJ_SET_NUM ("colWidths.size", colWidths->size ());
+ for (int i = 0; i < colWidths->size (); i++)
+ DBG_OBJ_ARRSET_NUM ("colWidths", i, colWidths->get (i));
+ }
+
+ colWidthsUpToDateWidthColExtremes = true;
+ DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes",
+ colWidthsUpToDateWidthColExtremes);
+
+ for (int col = 0; col < numCols; col++) {
+ if (col >= oldColWidths->size () || col >= colWidths->size () ||
+ oldColWidths->get (col) != colWidths->get (col)) {
+ // Column width has changed, tell children about this.
+ for (int row = 0; row < numRows; row++) {
+ int n = row * numCols + col;
+ // TODO: Columns spanning several rows are only regarded
+ // when the first column is affected.
+ if (childDefined (n))
+ children->get(n)->cell.widget->containerSizeChanged ();
}
- }/*for col*/
+ }
+ }
+
+ delete oldColWidths;
- setCumHeight (row + 1,
- cumHeight->get (row) + rowHeight + getStyle()->vBorderSpacing);
+ if (calcHeights) {
+ setCumHeight (0, 0);
+ for (int row = 0; row < numRows; row++) {
+ /**
+ * \bug dw::Table::baseline is not filled.
+ */
+ int rowHeight = 0;
+
+ for (int col = 0; col < numCols; col++) {
+ int n = row * numCols + col;
+ if (childDefined (n)) {
+ int width = (children->get(n)->cell.colspanEff - 1)
+ * getStyle()->hBorderSpacing;
+ for (int i = 0; i < children->get(n)->cell.colspanEff; i++)
+ width += colWidths->get (col + i);
+
+ core::Requisition childRequisition;
+ //children->get(n)->cell.widget->setWidth (width);
+ children->get(n)->cell.widget->sizeRequest (&childRequisition);
+ childHeight = childRequisition.ascent + childRequisition.descent;
+ if (children->get(n)->cell.rowspan == 1) {
+ rowHeight = misc::max (rowHeight, childHeight);
+ } else {
+ rowSpanCells->increase();
+ rowSpanCells->set(rowSpanCells->size()-1, n);
+ }
+ }
+ } // for col
- }/*for row*/
+ setCumHeight (row + 1,
+ cumHeight->get (row) + rowHeight + getStyle()->vBorderSpacing);
+ } // for row
+
+ apportionRowSpan ();
+ }
- apportionRowSpan ();
+ DBG_OBJ_LEAVE ();
}
void Table::apportionRowSpan ()
{
+ DBG_OBJ_ENTER0 ("resize", 0, "apportionRowSpan");
+
int *rowHeight = NULL;
for (int c = 0; c < rowSpanCells->size(); ++c) {
@@ -630,6 +1103,8 @@ void Table::apportionRowSpan ()
setCumHeight (i+1, cumHeight->get(i) + rowHeight[i]);
}
delete[] rowHeight;
+
+ DBG_OBJ_LEAVE ();
}
@@ -640,8 +1115,12 @@ void Table::apportionRowSpan ()
*/
void Table::calcColumnExtremes ()
{
- if (extremesChanged ())
+ DBG_OBJ_ENTER0 ("resize", 0, "calcColumnExtremes");
+
+ if (extremesChanged () || extremesQueued ())
forceCalcColumnExtremes ();
+
+ DBG_OBJ_LEAVE ();
}
@@ -650,570 +1129,330 @@ void Table::calcColumnExtremes ()
*/
void Table::forceCalcColumnExtremes ()
{
- _MSG(" Table::forceCalcColumnExtremes numCols=%d\n", numCols);
+ DBG_OBJ_ENTER0 ("resize", 0, "forceCalcColumnExtremes");
- if (numCols == 0)
- return;
+ if (numCols > 0) {
+ lout::misc::SimpleVector<int> colSpanCells (8);
+ colExtremes->setSize (numCols);
+ colWidthSpecified->setSize (numCols);
+ colWidthPercentage->setSize (numCols);
- colExtremes->setSize (numCols);
- colPercents->setSize (numCols);
- colSpanCells->setSize (0);
- /* 1. cells with colspan = 1 */
- for (int col = 0; col < numCols; col++) {
- colExtremes->getRef(col)->minWidth = 0;
- colExtremes->getRef(col)->maxWidth = 0;
- colPercents->set(col, core::style::LENGTH_AUTO);
+ // 1. cells with colspan = 1
+ for (int col = 0; col < numCols; col++) {
+ DBG_OBJ_MSGF ("resize", 1, "column %d", col);
+ DBG_OBJ_MSG_START ();
+
+ colWidthSpecified->set (col, false);
+ colWidthPercentage->set (col, false);
+
+ colExtremes->getRef(col)->minWidth = 0;
+ colExtremes->getRef(col)->minWidthIntrinsic = 0;
+ colExtremes->getRef(col)->maxWidth = 0;
+ colExtremes->getRef(col)->maxWidthIntrinsic = 0;
+
+ for (int row = 0; row < numRows; row++) {
+ DBG_OBJ_MSGF ("resize", 1, "row %d", row);
+ DBG_OBJ_MSG_START ();
+
+ int n = row * numCols + col;
+
+ if (childDefined (n)) {
+ if (children->get(n)->cell.colspanEff == 1) {
+ core::Extremes cellExtremes;
+ children->get(n)->cell.widget->getExtremes (&cellExtremes);
+
+ DBG_OBJ_MSGF ("resize", 1, "child: %d / %d",
+ cellExtremes.minWidth, cellExtremes.maxWidth);
+
+ colExtremes->getRef(col)->minWidthIntrinsic =
+ misc::max (colExtremes->getRef(col)->minWidthIntrinsic,
+ cellExtremes.minWidthIntrinsic);
+ colExtremes->getRef(col)->maxWidthIntrinsic =
+ misc::max (colExtremes->getRef(col)->minWidthIntrinsic,
+ colExtremes->getRef(col)->maxWidthIntrinsic,
+ cellExtremes.maxWidthIntrinsic);
+
+ colExtremes->getRef(col)->minWidth =
+ misc::max (colExtremes->getRef(col)->minWidth,
+ cellExtremes.minWidth);
+ colExtremes->getRef(col)->maxWidth =
+ misc::max (colExtremes->getRef(col)->minWidth,
+ colExtremes->getRef(col)->maxWidth,
+ cellExtremes.maxWidth);
+
+ core::style::Length childWidth =
+ children->get(n)->cell.widget->getStyle()->width;
+ if (childWidth != core::style::LENGTH_AUTO) {
+ colWidthSpecified->set (col, true);
+ if (core::style::isPerLength (childWidth))
+ colWidthPercentage->set (col, true);
+ }
- for (int row = 0; row < numRows; row++) {
- int n = row * numCols + col;
- if (!childDefined (n))
- continue;
- if (children->get(n)->cell.colspanEff == 1) {
- core::Extremes cellExtremes;
- int cellMinW, cellMaxW, pbm;
- core::style::Length width =
- children->get(n)->cell.widget->getStyle()->width;
- pbm = (numCols + 1) * getStyle()->hBorderSpacing
- + children->get(n)->cell.widget->getStyle()->boxDiffWidth ();
- children->get(n)->cell.widget->getExtremes (&cellExtremes);
- if (core::style::isAbsLength (width)) {
- // Fixed lengths include table padding, border and margin.
- cellMinW = cellExtremes.minWidth;
- cellMaxW = misc::max (cellMinW,
- core::style::absLengthVal(width) - pbm);
- } else {
- cellMinW = cellExtremes.minWidth;
- cellMaxW = cellExtremes.maxWidth;
+ DBG_OBJ_MSGF ("resize", 1, "column: %d / %d (%d / %d)",
+ colExtremes->getRef(col)->minWidth,
+ colExtremes->getRef(col)->maxWidth,
+ colExtremes->getRef(col)->minWidthIntrinsic,
+ colExtremes->getRef(col)->maxWidthIntrinsic);
+ } else {
+ colSpanCells.increase ();
+ colSpanCells.setLast (n);
+ }
}
- _MSG("FCCE, col%d colMin,colMax,cellMin,cellMax = %d,%d,%d,%d\n",
- col,
- colExtremes->getRef(col)->minWidth,
- colExtremes->getRef(col)->maxWidth,
- cellMinW, cellMaxW);
-
- colExtremes->getRef(col)->minWidth =
- misc::max (colExtremes->getRef(col)->minWidth, cellMinW);
- colExtremes->getRef(col)->maxWidth =
- misc::max (colExtremes->getRef(col)->minWidth, misc::max (
- colExtremes->getRef(col)->maxWidth,
- cellMaxW));
-
- // Also fill the colPercents array in this pass
- if (core::style::isPerLength (width)) {
- hasColPercent = 1;
- if (colPercents->get(col) == core::style::LENGTH_AUTO)
- colPercents->set(col, width);
- } else if (core::style::isAbsLength (width)) {
- // We treat LEN_ABS as a special case of LEN_AUTO.
- /*
- * if (colPercents->get(col) == LEN_AUTO)
- * colPercents->set(col, LEN_ABS);
- *
- * (Hint: that's old code!)
- */
- }
- } else {
- colSpanCells->increase();
- colSpanCells->set(colSpanCells->size()-1, n);
+ DBG_OBJ_MSG_END ();
}
- }
- }
-
- /* 2. cells with colspan > 1 */
- /* If needed, here we set proportionally apportioned col maximums */
- for (int c = 0; c < colSpanCells->size(); ++c) {
- core::Extremes cellExtremes;
- int cellMinW, cellMaxW, pbm;
- int n = colSpanCells->get(c);
- int col = n % numCols;
- int cs = children->get(n)->cell.colspanEff;
- core::style::Length width =
- children->get(n)->cell.widget->getStyle()->width;
- pbm = (numCols + 1) * getStyle()->hBorderSpacing
- + children->get(n)->cell.widget->getStyle()->boxDiffWidth ();
- children->get(n)->cell.widget->getExtremes (&cellExtremes);
- if (core::style::isAbsLength (width)) {
- // Fixed lengths include table padding, border and margin.
- cellMinW = cellExtremes.minWidth;
- cellMaxW =
- misc::max (cellMinW, core::style::absLengthVal(width) - pbm);
- } else {
- cellMinW = cellExtremes.minWidth;
- cellMaxW = cellExtremes.maxWidth;
- }
- int minSumCols = 0, maxSumCols = 0;
- for (int i = 0; i < cs; ++i) {
- minSumCols += colExtremes->getRef(col+i)->minWidth;
- maxSumCols += colExtremes->getRef(col+i)->maxWidth;
- }
- _MSG("cs=%d spanWidth=%d,%d sumCols=%d,%d\n",
- cs,cellMinW,cellMaxW,minSumCols,maxSumCols);
-
- if (minSumCols >= cellMinW && maxSumCols >= cellMaxW)
- continue;
-
- // Cell size is too small; apportion {min,max} for this colspan.
- int spanMinW = misc::max (misc::max (cs, minSumCols),
- cellMinW - (cs-1) * getStyle()->hBorderSpacing),
- spanMaxW = misc::max (misc::max (cs, maxSumCols),
- cellMaxW - (cs-1) * getStyle()->hBorderSpacing);
-
- if (minSumCols == 0) {
- // No single cells defined for this span => pre-apportion equally
- minSumCols = spanMinW; maxSumCols = spanMaxW;
- int minW = spanMinW, maxW = spanMaxW;
- for (int i = 0; i < cs; ++i) {
- colExtremes->getRef(col+i)->minWidth = minW / (cs - i);
- colExtremes->getRef(col+i)->maxWidth = maxW / (cs - i);
- minW -= colExtremes->getRef(col+i)->minWidth;
- maxW -= colExtremes->getRef(col+i)->maxWidth;
- }
+ DBG_OBJ_MSG_END ();
}
- // These values will help if the span has percents.
- int spanHasColPercent = 0;
- int availSpanMinW = spanMinW;
- float cumSpanPercent = 0.0f;
- for (int i = col; i < col + cs; ++i) {
- if (core::style::isPerLength (colPercents->get(i))) {
- cumSpanPercent += core::style::perLengthVal (colPercents->get(i));
- ++spanHasColPercent;
- } else
- availSpanMinW -= colExtremes->getRef(i)->minWidth;
- }
+ // 2. cells with colspan > 1
- // Calculate weighted-apportion columns for this span.
- int wMin = 0, wMax;
- int cumMaxWnew = 0, cumMaxWold = 0, goalMaxW = spanMaxW;
- int curAppW = maxSumCols;
- int curExtraW = spanMinW - minSumCols;
- for (int i = col; i < col + cs; ++i) {
-
- if (!spanHasColPercent) {
- int d_a = colExtremes->getRef(i)->maxWidth;
- int d_w = curAppW > 0 ? (int)((float)curExtraW * d_a/curAppW) : 0;
- if (d_a < 0||d_w < 0) {
- MSG("d_a=%d d_w=%d\n",d_a,d_w);
- exit(1);
- }
- wMin = colExtremes->getRef(i)->minWidth + d_w;
- colExtremes->getRef(i)->minWidth = wMin;
- curExtraW -= d_w;
- curAppW -= d_a;
- } else {
- if (core::style::isPerLength (colPercents->get(i))) {
- // multiplyWithPerLength would cause rounding errors,
- // therefore the deprecated way, using perLengthVal:
- wMin = misc::max (colExtremes->getRef(i)->minWidth,
- (int)(availSpanMinW *
- core::style::perLengthVal
- (colPercents->get (i))
- / cumSpanPercent));
- colExtremes->getRef(i)->minWidth = wMin;
- }
- }
+ // TODO: Is this old comment still relevant? "If needed, here we
+ // set proportionally apportioned col maximums."
- wMax = (goalMaxW-cumMaxWnew <= 0) ? 0 :
- (int)((float)(goalMaxW-cumMaxWnew)
- * colExtremes->getRef(i)->maxWidth
- / (maxSumCols-cumMaxWold));
- wMax = misc::max (wMin, wMax);
- cumMaxWnew += wMax;
- cumMaxWold += colExtremes->getRef(i)->maxWidth;
- colExtremes->getRef(i)->maxWidth = wMax;
+ for (int i = 0; i < colSpanCells.size(); i++) {
+ int n = colSpanCells.get (i);
+ int col = n % numCols;
+ int cs = children->get(n)->cell.colspanEff;
- _MSG("i=%d, wMin=%d wMax=%d cumMaxWold=%d\n",
- i,wMin,wMax,cumMaxWold);
+ core::Extremes cellExtremes;
+ children->get(n)->cell.widget->getExtremes (&cellExtremes);
- }
-#ifdef DBG
- MSG("col min,max: [");
- for (int i = 0; i < numCols; i++)
- MSG("%d,%d ",
- colExtremes->getRef(i)->minWidth,
- colExtremes->getRef(i)->maxWidth);
- MSG("]\n");
- MSG("getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing);
-#endif
- }
-}
+ calcExtremesSpanMulteCols (col, cs, &cellExtremes, MIN, MAX, NULL);
+ calcExtremesSpanMulteCols (col, cs, &cellExtremes, MIN_INTR, MAX_INTR,
+ NULL);
-/**
- * \brief Apportionment function for AUTO-length columns.
- * 'extremes' comes filled, 'result' comes defined for percentage columns.
- */
-void Table::apportion2 (int totalWidth, int forceTotalWidth)
-{
- if (colExtremes->size() == 0)
- return;
-#ifdef DBG
- MSG("app2, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n",
- availWidth, totalWidth, forceTotalWidth);
- MSG("app2, extremes: ( ");
- for (int i = 0; i < colExtremes->size (); i++)
- MSG("%d,%d ",
- colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth);
- MSG(")\n");
-#endif
- int minAutoWidth = 0, maxAutoWidth = 0, availAutoWidth = totalWidth;
- for (int col = 0; col < numCols; col++) {
- if (core::style::isAbsLength (colPercents->get(col))) {
- // set absolute lengths
- setColWidth (col, colExtremes->get(col).minWidth);
+ core::style::Length childWidth =
+ children->get(n)->cell.widget->getStyle()->width;
+ if (childWidth != core::style::LENGTH_AUTO) {
+ for (int j = 0; j < cs; j++)
+ colWidthSpecified->set (col + j, true);
+ if (core::style::isPerLength (childWidth))
+ for (int j = 0; j < cs; j++)
+ colWidthPercentage->set (col + j, true);
+ }
}
- if (colPercents->get(col) == core::style::LENGTH_AUTO) {
- maxAutoWidth += colExtremes->get(col).maxWidth;
- minAutoWidth += colExtremes->get(col).minWidth;
- } else
- availAutoWidth -= colWidths->get(col);
}
- if (!maxAutoWidth) // no core::style::LENGTH_AUTO cols!
- return;
-
- colWidths->setSize (colExtremes->size (), 0);
-
- if (!forceTotalWidth && maxAutoWidth < availAutoWidth) {
- // Enough space for the maximum table, don't widen past max.
- availAutoWidth = maxAutoWidth;
+ numColWidthSpecified = 0;
+ numColWidthSpecified = 0;
+ for (int i = 0; i < colExtremes->size (); i++) {
+ if (colWidthSpecified->get (i))
+ numColWidthSpecified++;
+ if (colWidthPercentage->get (i))
+ numColWidthPercentage++;
}
- // General case.
- int curTargetWidth = misc::max (availAutoWidth, minAutoWidth);
- int curExtraWidth = curTargetWidth - minAutoWidth;
- int curMaxWidth = maxAutoWidth;
- int curNewWidth = minAutoWidth;
- for (int col = 0; col < numCols; col++) {
- _MSG("app2, col %d, minWidth=%d maxWidth=%d\n",
- col, colExtremes->getRef(col)->minWidth,
- colExtremes->get(col).maxWidth);
-
- if (colPercents->get(col) != core::style::LENGTH_AUTO)
- continue;
-
- int colMinWidth = colExtremes->getRef(col)->minWidth;
- int colMaxWidth = colExtremes->getRef(col)->maxWidth;
- int w = (curMaxWidth <= 0) ? 0 :
- (int)((float)curTargetWidth * colMaxWidth/curMaxWidth);
-
- _MSG("app2, curTargetWidth=%d colMaxWidth=%d curMaxWidth=%d "
- "curNewWidth=%d ",
- curTargetWidth, colMaxWidth,curMaxWidth,curNewWidth);
- _MSG("w = %d, ", w);
-
- if (w <= colMinWidth)
- w = colMinWidth;
- else if (curNewWidth - colMinWidth + w > curTargetWidth)
- w = colMinWidth + curExtraWidth;
-
- _MSG("w = %d\n", w);
-
- curNewWidth -= colMinWidth;
- curMaxWidth -= colMaxWidth;
- curExtraWidth -= (w - colMinWidth);
- curTargetWidth -= w;
- setColWidth (col, w);
- }
-#ifdef DBG
- MSG("app2, result: ( ");
- for (int i = 0; i < colWidths->size (); i++)
- MSG("%d ", colWidths->get (i));
- MSG(")\n");
-#endif
-}
-
-void Table::apportion_percentages2(int totalWidth, int forceTotalWidth)
-{
- int hasTablePercent = core::style::isPerLength (getStyle()->width) ? 1 : 0;
-
- if (colExtremes->size() == 0 || (!hasTablePercent && !hasColPercent))
- return;
-
- // If there's a table-wide percentage, totalWidth comes already scaled.
- _MSG("APP_P, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n",
- availWidth, totalWidth, forceTotalWidth);
-
- if (!hasColPercent) {
-#ifdef DBG
- MSG("APP_P, only a table-wide percentage\n");
- MSG("APP_P, extremes = { ");
- for (int col = 0; col < numCols; col++)
- MSG("%d,%d ", colExtremes->getRef(col)->minWidth,
- colExtremes->getRef(col)->maxWidth);
- MSG("}\n");
-#endif
- // It has only a table-wide percentage. Apportion non-absolute widths.
- int sumMaxWidth = 0, perAvailWidth = totalWidth;
- for (int col = 0; col < numCols; col++) {
- if (core::style::isAbsLength (colPercents->get(col)))
- perAvailWidth -= colExtremes->getRef(col)->maxWidth;
- else
- sumMaxWidth += colExtremes->getRef(col)->maxWidth;
- }
-
- _MSG("APP_P, perAvailWidth=%d, sumMaxWidth=%d\n",
- perAvailWidth, sumMaxWidth);
-
- for (int col = 0; col < numCols; col++) {
- int max_wi = colExtremes->getRef(col)->maxWidth, new_wi;
- if (!core::style::isAbsLength (colPercents->get(col))) {
- new_wi =
- misc::max (colExtremes->getRef(col)->minWidth,
- (int)((float)max_wi * perAvailWidth/sumMaxWidth));
- setColWidth (col, new_wi);
- perAvailWidth -= new_wi;
- sumMaxWidth -= max_wi;
- }
- }
-#ifdef DBG
- MSG("APP_P, result = { ");
- for (int col = 0; col < numCols; col++)
- MSG("%d ", colWidths->get(col));
- MSG("}\n");
-#endif
-
- } else {
- // we'll have to apportion...
- _MSG("APP_P, we'll have to apportion...\n");
-
- // Calculate cumPercent and available space
- float cumPercent = 0.0f;
- int hasAutoCol = 0;
- int sumMinWidth = 0, sumMaxWidth = 0, sumMinNonPer = 0, sumMaxNonPer = 0;
- for (int col = 0; col < numCols; col++) {
- if (core::style::isPerLength (colPercents->get(col))) {
- cumPercent += core::style::perLengthVal (colPercents->get(col));
- } else {
- sumMinNonPer += colExtremes->getRef(col)->minWidth;
- sumMaxNonPer += colExtremes->getRef(col)->maxWidth;
- if (colPercents->get(col) == core::style::LENGTH_AUTO)
- hasAutoCol++;
- }
- sumMinWidth += colExtremes->getRef(col)->minWidth;
- sumMaxWidth += colExtremes->getRef(col)->maxWidth;
-
- _MSG("APP_P, col %d minWidth=%d maxWidth=%d\n", col,
- colExtremes->getRef(col)->minWidth,
- colExtremes->getRef(col)->maxWidth);
- }
- int oldTotalWidth = totalWidth;
- if (!forceTotalWidth) {
- if (sumMaxNonPer == 0 || cumPercent < 0.99f) {
- // only percentage columns, or cumPercent < 100% => restrict width
- int totW = (int)(sumMaxNonPer / (1.0f - cumPercent));
- for (int col = 0; col < numCols; col++) {
- totW = misc::max
- (totW,
- (int)(colExtremes->getRef(col)->maxWidth
- / core::style::perLengthVal (colPercents->get(col))));
- }
- totalWidth = misc::min (totW, totalWidth);
- }
+ DBG_IF_RTFL {
+ DBG_OBJ_SET_NUM ("colExtremes.size", colExtremes->size ());
+ for (int i = 0; i < colExtremes->size (); i++) {
+ DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "minWidth",
+ colExtremes->get(i).minWidth);
+ DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "minWidthIntrinsic",
+ colExtremes->get(i).minWidthIntrinsic);
+ DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "maxWidth",
+ colExtremes->get(i).maxWidth);
+ DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "maxWidthIntrinsic",
+ colExtremes->get(i).maxWidthIntrinsic);
}
- // make sure there's enough space
- totalWidth = misc::max (totalWidth, sumMinWidth);
- // extraWidth is always >= 0
- int extraWidth = totalWidth - sumMinWidth;
- int sumMinWidthPer = sumMinWidth - sumMinNonPer;
- int curPerWidth = sumMinWidthPer;
- // percentages refer to workingWidth
- int workingWidth = totalWidth - sumMinNonPer;
- if (cumPercent < 0.99f) {
- // In this case, use the whole table width
- workingWidth = totalWidth;
- curPerWidth = sumMinWidth;
- }
-
- _MSG("APP_P, oldTotalWidth=%d totalWidth=%d"
- " workingWidth=%d extraWidth=%d sumMinNonPer=%d\n",
- oldTotalWidth,totalWidth,workingWidth,extraWidth,sumMinNonPer);
-
- for (int col = 0; col < numCols; col++) {
- int colMinWidth = colExtremes->getRef(col)->minWidth;
- if (core::style::isPerLength (colPercents->get(col))) {
- int w = core::style::multiplyWithPerLength (workingWidth,
- colPercents->get(col));
- if (w < colMinWidth)
- w = colMinWidth;
- else if (curPerWidth - colMinWidth + w > workingWidth)
- w = colMinWidth + extraWidth;
- extraWidth -= (w - colMinWidth);
- curPerWidth += (w - colMinWidth);
- setColWidth (col, w);
- } else {
- setColWidth (col, colMinWidth);
- }
- }
-
- if (cumPercent < 0.99f) {
- // Will have to apportion the other columns
-#ifdef DBG
- MSG("APP_P, extremes: ( ");
- for (int i = 0; i < colExtremes->size (); i++)
- MSG("%d,%d ",
- colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth);
- MSG(")\n");
-#endif
- curPerWidth -= sumMinNonPer;
- int perWidth = (int)(curPerWidth/cumPercent);
- totalWidth = misc::max (totalWidth, perWidth);
- totalWidth = misc::min (totalWidth, oldTotalWidth);
-
- _MSG("APP_P, curPerWidth=%d perWidth=%d, totalWidth=%d\n",
- curPerWidth, perWidth, totalWidth);
-
- if (hasAutoCol == 0) {
- // Special case, cumPercent < 100% and no other columns to expand.
- // We'll honor totalWidth by expanding the percentage cols.
- int extraWidth = totalWidth - curPerWidth - sumMinNonPer;
- for (int col = 0; col < numCols; col++) {
- if (core::style::isPerLength (colPercents->get(col))) {
- // This could cause rounding errors:
- //
- // int d =
- // core::dw::multiplyWithPerLength (extraWidth,
- // colPercents->get(col))
- // / cumPercent;
- //
- // Thus the "old" way:
- int d =
- (int)(extraWidth *
- core::style::perLengthVal (colPercents->get(col))
- / cumPercent);
- setColWidth (col, colWidths->get(col) + d);
- }
- }
- }
- }
-#ifdef DBG
- MSG("APP_P, result ={ ");
- for (int col = 0; col < numCols; col++)
- MSG("%d ", colWidths->get(col));
- MSG("}\n");
-#endif
- apportion2 (totalWidth, 2);
-
-#ifdef DBG
- MSG("APP_P, percent={");
- for (int col = 0; col < numCols; col++)
- MSG("%f ", core::dw::perLengthVal (colPercents->get(col)));
- MSG("}\n");
- MSG("APP_P, result ={ ");
- for (int col = 0; col < numCols; col++)
- MSG("%d ", colWidths->get(col));
- MSG("}\n");
-#endif
+ DBG_OBJ_SET_NUM ("colWidthSpecified.size", colWidthSpecified->size ());
+ for (int i = 0; i < colWidthSpecified->size (); i++)
+ DBG_OBJ_ARRSET_BOOL ("colWidthSpecified", i,
+ colWidthSpecified->get(i));
+ DBG_OBJ_SET_NUM ("numColWidthSpecified", numColWidthSpecified);
+
+ DBG_OBJ_SET_NUM ("colWidthPercentage.size", colWidthPercentage->size ());
+ for (int i = 0; i < colWidthPercentage->size (); i++)
+ DBG_OBJ_ARRSET_BOOL ("colWidthPercentage", i,
+ colWidthPercentage->get(i));
+ DBG_OBJ_SET_NUM ("numColWidthPercentage", numColWidthPercentage);
}
-}
-// ----------------------------------------------------------------------
+ colWidthsUpToDateWidthColExtremes = false;
+ DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes",
+ colWidthsUpToDateWidthColExtremes);
-Table::TableIterator::TableIterator (Table *table,
- core::Content::Type mask, bool atEnd):
- core::Iterator (table, mask, atEnd)
-{
- index = atEnd ? table->children->size () : -1;
- content.type = atEnd ? core::Content::END : core::Content::START;
+ DBG_OBJ_LEAVE ();
}
-Table::TableIterator::TableIterator (Table *table,
- core::Content::Type mask, int index):
- core::Iterator (table, mask, false)
+void Table::calcExtremesSpanMulteCols (int col, int cs,
+ core::Extremes *cellExtremes,
+ ExtrMod minExtrMod, ExtrMod maxExtrMod,
+ void *extrData)
{
- this->index = index;
+ DBG_OBJ_ENTER ("resize", 0, "calcExtremesSpanMulteCols",
+ "%d, %d, ..., %s, %s, ...",
+ col, cs, getExtrModName (minExtrMod),
+ getExtrModName (maxExtrMod));
- if (index < 0)
- content.type = core::Content::START;
- else if (index >= table->children->size ())
- content.type = core::Content::END;
- else {
- content.type = core::Content::WIDGET;
- content.widget = table->children->get(index)->cell.widget;
- }
-}
+ int cellMin = getExtreme (cellExtremes, minExtrMod);
+ int cellMax = getExtreme (cellExtremes, maxExtrMod);
-object::Object *Table::TableIterator::clone()
-{
- return new TableIterator ((Table*)getWidget(), getMask(), index);
-}
+ int minSumCols = 0, maxSumCols = 0;
-int Table::TableIterator::compareTo(object::Comparable *other)
-{
- return index - ((TableIterator*)other)->index;
-}
-
-bool Table::TableIterator::next ()
-{
- Table *table = (Table*)getWidget();
-
- if (content.type == core::Content::END)
- return false;
-
- // tables only contain widgets:
- if ((getMask() & core::Content::WIDGET) == 0) {
- content.type = core::Content::END;
- return false;
+ for (int j = 0; j < cs; j++) {
+ minSumCols += getColExtreme (col + j, minExtrMod, extrData);
+ maxSumCols += getColExtreme (col + j, maxExtrMod, extrData);
}
- do {
- index++;
- if (index >= table->children->size ()) {
- content.type = core::Content::END;
- return false;
+ DBG_OBJ_MSGF ("resize", 1, "cs = %d, cell: %d / %d, sum: %d / %d\n",
+ cs, cellMin, cellMax, minSumCols, maxSumCols);
+
+ bool changeMin = cellMin > minSumCols;
+ bool changeMax = cellMax > maxSumCols;
+ if (changeMin || changeMax) {
+ // TODO This differs from the documentation? Should work, anyway.
+ misc::SimpleVector<int> newMin, newMax;
+ if (changeMin)
+ apportion2 (cellMin, col, col + cs - 1, MIN, MAX, NULL, &newMin, 0);
+ if (changeMax)
+ apportion2 (cellMax, col, col + cs - 1, MIN, MAX, NULL, &newMax, 0);
+
+ for (int j = 0; j < cs; j++) {
+ if (changeMin)
+ setColExtreme (col + j, minExtrMod, extrData, newMin.get (j));
+ if (changeMax)
+ setColExtreme (col + j, maxExtrMod, extrData, newMax.get (j));
+
+ // For cases where min and max are somewhat confused:
+ setColExtreme (col + j, maxExtrMod, extrData,
+ misc::max (getColExtreme (col + j, minExtrMod,
+ extrData),
+ getColExtreme (col + j, maxExtrMod,
+ extrData)));
}
- } while (table->children->get(index) == NULL ||
- table->children->get(index)->type != Child::CELL);
+ }
- content.type = core::Content::WIDGET;
- content.widget = table->children->get(index)->cell.widget;
- return true;
+ DBG_OBJ_LEAVE ();
}
-bool Table::TableIterator::prev ()
+/**
+ * \brief Actual apportionment function.
+ */
+void Table::apportion2 (int totalWidth, int firstCol, int lastCol,
+ ExtrMod minExtrMod, ExtrMod maxExtrMod, void *extrData,
+ misc::SimpleVector<int> *dest, int destOffset)
{
- Table *table = (Table*)getWidget();
+ DBG_OBJ_ENTER ("resize", 0, "apportion2", "%d, %d, %d, %s, %s, ..., %d",
+ totalWidth, firstCol, lastCol, getExtrModName (minExtrMod),
+ getExtrModName (maxExtrMod), destOffset);
- if (content.type == core::Content::START)
- return false;
+ if (lastCol >= firstCol) {
+ dest->setSize (destOffset + lastCol - firstCol + 1, 0);
- // tables only contain widgets:
- if ((getMask() & core::Content::WIDGET) == 0) {
- content.type = core::Content::START;
- return false;
- }
-
- do {
- index--;
- if (index < 0) {
- content.type = core::Content::START;
- return false;
+ int totalMin = 0, totalMax = 0;
+ for (int col = firstCol; col <= lastCol; col++) {
+ totalMin += getColExtreme (col, minExtrMod, extrData);
+ totalMax += getColExtreme (col, maxExtrMod, extrData);
}
- } while (table->children->get(index) == NULL ||
- table->children->get(index)->type != Child::CELL);
-
- content.type = core::Content::WIDGET;
- content.widget = table->children->get(index)->cell.widget;
- return true;
-}
-
-void Table::TableIterator::highlight (int start, int end,
- core::HighlightLayer layer)
-{
- /** todo Needs this an implementation? */
-}
-void Table::TableIterator::unhighlight (int direction,
- core::HighlightLayer layer)
-{
-}
+ DBG_OBJ_MSGF ("resize", 1,
+ "totalWidth = %d, totalMin = %d, totalMax = %d",
+ totalWidth, totalMin, totalMax);
+
+ // The actual calculation is rather simple, the ith value is:
+ //
+ //
+ // (max[i] - min[i]) * (totalMax - totalMin)
+ // width[i] = min[i] + -----------------------------------------
+ // (totalWidth - totalMin)
+ //
+ // (Regard "total" as "sum".) With the following general
+ // definitions (for both the list and sums):
+ //
+ // diffExtr = max - min
+ // diffWidth = width - min
+ //
+ // it is simplified to:
+ //
+ // diffExtr[i] * totalDiffWidth
+ // diffWidth[i] = ----------------------------
+ // totalDiffExtr
+ //
+ // Of course, if totalDiffExtr is 0, this is not defined;
+ // instead, we apportion according to the minima:
+ //
+ // min[i] * totalWidth
+ // width[i] = -------------------
+ // totalMin
+ //
+ // Since min[i] <= max[i] for all i, totalMin == totalMax
+ // implies that min[i] == max[i] for all i.
+ //
+ // Third, it totalMin == 0 (which also implies min[i] = max[i] = 0),
+ // the result is
+ //
+ // width[i] = totalWidth / n
+
+ int totalDiffExtr = totalMax - totalMin;
+ if (totalDiffExtr != 0) {
+ // Normal case. The algorithm described in
+ // "rounding-errors.doc" is used, with:
+ //
+ // x[i] = diffExtr[i]
+ // y[i] = diffWidth[i]
+ // a = totalDiffWidth
+ // b = totalDiffExtr
+
+ DBG_OBJ_MSG ("resize", 1, "normal case");
+
+ int totalDiffWidth = totalWidth - totalMin;
+ int cumDiffExtr = 0, cumDiffWidth = 0;
+
+ for (int col = firstCol; col <= lastCol; col++) {
+ int min = getColExtreme (col, minExtrMod, extrData);
+ int max = getColExtreme (col, maxExtrMod, extrData);
+ int diffExtr = max - min;
+
+ cumDiffExtr += diffExtr;
+ int diffWidth =
+ (cumDiffExtr * totalDiffWidth) / totalDiffExtr - cumDiffWidth;
+ cumDiffWidth += diffWidth;
+
+ dest->set (destOffset - firstCol + col, diffWidth + min);
+ }
+ } else if (totalMin != 0) {
+ // Special case. Again, same algorithm, with
+ //
+ // x[i] = min[i]
+ // y[i] = width[i]
+ // a = totalWidth
+ // b = totalMin
+
+ DBG_OBJ_MSG ("resize", 1, "special case 1");
+
+ int cumMin = 0, cumWidth = 0;
+ for (int col = firstCol; col <= lastCol; col++) {
+ int min = getColExtreme (col, minExtrMod, extrData);
+ cumMin += min;
+ int width = (cumMin * totalWidth) / totalMin - cumWidth;
+ cumWidth += width;
+
+ dest->set (destOffset - firstCol + col, width);
+ }
+ } else { // if (totalMin == 0)
+ // Last special case. Ssame algorithm, with
+ //
+ // x[i] = 1 (so cumX = i = col - firstCol + 1)
+ // y[i] = width[i]
+ // a = totalWidth
+ // b = n = lastCol - firstCol + 1
+
+ DBG_OBJ_MSG ("resize", 1, "special case 2");
+
+ int cumWidth = 0, n = (lastCol - firstCol + 1);
+ for (int col = firstCol; col <= lastCol; col++) {
+ int i = (col - firstCol + 1);
+ int width = (i * totalWidth) / n - cumWidth;
+ cumWidth += width;
+
+ dest->set (destOffset - firstCol + col, width);
+ }
+ }
+ }
-void Table::TableIterator::getAllocation (int start, int end,
- core::Allocation *allocation)
-{
- /** \bug Not implemented. */
+ DBG_OBJ_LEAVE ();
}
} // namespace dw
diff --git a/dw/table.hh b/dw/table.hh
index 1d14ec07..1e7c5268 100644
--- a/dw/table.hh
+++ b/dw/table.hh
@@ -2,7 +2,7 @@
#define __DW_TABLE_HH__
#include "core.hh"
-#include "tablecell.hh"
+#include "alignedtablecell.hh"
#include "../lout/misc.hh"
namespace dw {
@@ -10,6 +10,11 @@ namespace dw {
/**
* \brief A Widget for rendering tables.
*
+ * <div style="border: 2px solid #ff0000; margin-top: 0.5em;
+ * margin-bottom: 0.5em; padding: 0.5em 1em;
+ * background-color: #ffefe0"><b>Warning:</b> Some parts of this
+ * description are outdated since \ref dw-grows.</div>
+ *
* <h3>Introduction</h3>
*
* The dw::Table widget is used to render HTML tables.
@@ -71,7 +76,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>
@@ -191,8 +196,9 @@ namespace dw {
*
* <ul>
* <li> the specified absolute width of the table, when given, or
- * <li> the available width (set by dw::Table::setWidth) times the specified
- * percentage width of t(at max 100%), if the latter is given, or
+ * <li> the available width (set by dw::Table::setWidth [TODO outdated]) times
+ * the specified percentage width of t(at max 100%), if the latter is
+ * given, or
* <li> otherwise the available width.
* </ul>
*
@@ -316,7 +322,6 @@ namespace dw {
class Table: public core::Widget
{
private:
-
struct Child
{
enum {
@@ -355,8 +360,9 @@ private:
friend class TableIterator;
+ static bool adjustTableMinWidth;
+
bool limitTextWidth, rowClosed;
- int availWidth, availAscent, availDescent; // set by set...
int numRows, numCols, curRow, curCol;
lout::misc::SimpleVector<Child*> *children;
@@ -369,6 +375,28 @@ private:
lout::misc::SimpleVector<core::Extremes> *colExtremes;
/**
+ * \brief Wether the column itself (in the future?) or at least one
+ * cell in this column or spanning over this column has CSS
+ * 'width' specified.
+ *
+ * Filled by forceCalcColumnExtremes(), since it is needed to
+ * calculate the column widths.
+ */
+ lout::misc::SimpleVector<bool> *colWidthSpecified;
+ int numColWidthSpecified;
+
+ /**
+ * \brief Wether the column itself (in the future?) or at least one
+ * cell in this column or spanning over this column has CSS
+ * 'width' specified *as percentage value*.
+ *
+ * Filled by forceCalcColumnExtremes(), since it is needed to
+ * calculate the column widths.
+ */
+ lout::misc::SimpleVector<bool> *colWidthPercentage;
+ int numColWidthPercentage;
+
+ /**
* \brief The widths of all columns.
*/
lout::misc::SimpleVector<int> *colWidths;
@@ -383,19 +411,19 @@ private:
* If a Cell has rowspan > 1, it goes into this array
*/
lout::misc::SimpleVector<int> *rowSpanCells;
- /**
- * If a Cell has colspan > 1, it goes into this array
- */
- lout::misc::SimpleVector<int> *colSpanCells;
lout::misc::SimpleVector<int> *baseline;
lout::misc::SimpleVector<core::style::Style*> *rowStyle;
- /**
- * hasColPercent becomes true when any cell specifies a percentage width.
- */
- int hasColPercent;
- lout::misc::SimpleVector<core::style::Length> *colPercents;
+ bool colWidthsUpToDateWidthColExtremes;
+
+ enum ExtrMod { MIN, MIN_INTR, MIN_MIN, MAX_MIN, MAX, MAX_INTR, DATA };
+
+ const char *getExtrModName (ExtrMod mod);
+ int getExtreme (core::Extremes *extremes, ExtrMod mod);
+ void setExtreme (core::Extremes *extremes, ExtrMod mod, int value);
+ int getColExtreme (int col, ExtrMod mod, void *data);
+ inline void setColExtreme (int col, ExtrMod mod, void *data, int value);
inline bool childDefined(int n)
{
@@ -403,17 +431,24 @@ private:
children->get(n)->type != Child::SPAN_SPACE;
}
+ int calcAvailWidthForDescendant (Widget *child);
+
void reallocChildren (int newNumCols, int newNumRows);
- void calcCellSizes ();
- void forceCalcCellSizes ();
+ void calcCellSizes (bool calcHeights);
+ void forceCalcCellSizes (bool calcHeights);
void apportionRowSpan ();
void calcColumnExtremes ();
void forceCalcColumnExtremes ();
+ void calcExtremesSpanMulteCols (int col, int cs,
+ core::Extremes *cellExtremes,
+ ExtrMod minExtrMod, ExtrMod maxExtrMod,
+ void *extrData);
- void apportion2 (int totalWidth, int forceTotalWidth);
- void apportion_percentages2 (int totalWidth, int forceTotalWidth);
+ void apportion2 (int totalWidth, int firstCol, int lastCol,
+ ExtrMod minExtrMod, ExtrMod maxExtrMod, void *extrData,
+ lout::misc::SimpleVector<int> *dest, int destOffset);
void setCumHeight (int row, int value)
{
@@ -423,23 +458,22 @@ private:
}
}
- inline void setColWidth (int col, int value)
- {
- if (value != colWidths->get (col)) {
- redrawX = lout::misc::min (redrawX, value);
- colWidths->set (col, value);
- }
- }
-
protected:
void sizeRequestImpl (core::Requisition *requisition);
void getExtremesImpl (core::Extremes *extremes);
void sizeAllocateImpl (core::Allocation *allocation);
void resizeDrawImpl ();
- void setWidth (int width);
- void setAscent (int ascent);
- void setDescent (int descent);
+ bool getAdjustMinWidth () { return Table::adjustTableMinWidth; }
+
+ int getAvailWidthOfChild (Widget *child, bool forceValue);
+
+ void containerSizeChangedForChildren ();
+ bool affectsSizeChangeContainerChild (Widget *child);
+ bool usesAvailWidth ();
+
+ bool isBlockLevel ();
+
void draw (core::View *view, core::Rectangle *area);
//bool buttonPressImpl (core::EventButton *event);
@@ -451,14 +485,23 @@ protected:
public:
static int CLASS_ID;
+ inline static void setAdjustTableMinWidth (bool adjustTableMinWidth)
+ { Table::adjustTableMinWidth = adjustTableMinWidth; }
+
+ inline static bool getAdjustTableMinWidth ()
+ { return Table::adjustTableMinWidth; }
+
Table(bool limitTextWidth);
~Table();
+ int applyPerWidth (int containerWidth, core::style::Length perWidth);
+ int applyPerHeight (int containerHeight, core::style::Length perHeight);
+
core::Iterator *iterator (core::Content::Type mask, bool atEnd);
void addCell (Widget *widget, int colspan, int rowspan);
void addRow (core::style::Style *style);
- TableCell *getCellRef ();
+ AlignedTableCell *getCellRef ();
};
} // namespace dw
diff --git a/dw/table_iterator.cc b/dw/table_iterator.cc
new file mode 100644
index 00000000..4da0ef4f
--- /dev/null
+++ b/dw/table_iterator.cc
@@ -0,0 +1,134 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2005-2007, 2014 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * (This file was originally part of textblock.cc.)
+ *
+ * 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 "table.hh"
+
+using namespace lout;
+
+namespace dw {
+
+Table::TableIterator::TableIterator (Table *table,
+ core::Content::Type mask, bool atEnd):
+ core::Iterator (table, mask, atEnd)
+{
+ index = atEnd ? table->children->size () : -1;
+ content.type = atEnd ? core::Content::END : core::Content::START;
+}
+
+Table::TableIterator::TableIterator (Table *table,
+ core::Content::Type mask, int index):
+ core::Iterator (table, mask, false)
+{
+ this->index = index;
+
+ if (index < 0)
+ content.type = core::Content::START;
+ else if (index >= table->children->size ())
+ content.type = core::Content::END;
+ else {
+ content.type = core::Content::WIDGET_IN_FLOW;
+ content.widget = table->children->get(index)->cell.widget;
+ }
+}
+
+object::Object *Table::TableIterator::clone()
+{
+ return new TableIterator ((Table*)getWidget(), getMask(), index);
+}
+
+int Table::TableIterator::compareTo(object::Comparable *other)
+{
+ return index - ((TableIterator*)other)->index;
+}
+
+bool Table::TableIterator::next ()
+{
+ Table *table = (Table*)getWidget();
+
+ if (content.type == core::Content::END)
+ return false;
+
+ // tables only contain widgets (in flow):
+ if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) {
+ content.type = core::Content::END;
+ return false;
+ }
+
+ do {
+ index++;
+ if (index >= table->children->size ()) {
+ content.type = core::Content::END;
+ return false;
+ }
+ } while (table->children->get(index) == NULL ||
+ table->children->get(index)->type != Child::CELL);
+
+ content.type = core::Content::WIDGET_IN_FLOW;
+ content.widget = table->children->get(index)->cell.widget;
+ return true;
+}
+
+bool Table::TableIterator::prev ()
+{
+ Table *table = (Table*)getWidget();
+
+ if (content.type == core::Content::START)
+ return false;
+
+ // tables only contain widgets (in flow):
+ if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) {
+ content.type = core::Content::START;
+ return false;
+ }
+
+ do {
+ index--;
+ if (index < 0) {
+ content.type = core::Content::START;
+ return false;
+ }
+ } while (table->children->get(index) == NULL ||
+ table->children->get(index)->type != Child::CELL);
+
+ content.type = core::Content::WIDGET_IN_FLOW;
+ content.widget = table->children->get(index)->cell.widget;
+ return true;
+}
+
+void Table::TableIterator::highlight (int start, int end,
+ core::HighlightLayer layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void Table::TableIterator::unhighlight (int direction,
+ core::HighlightLayer layer)
+{
+}
+
+void Table::TableIterator::getAllocation (int start, int end,
+ core::Allocation *allocation)
+{
+ /** \bug Not implemented. */
+}
+
+} // namespace dw
diff --git a/dw/tablecell.cc b/dw/tablecell.cc
index 8612f620..5c34c781 100644
--- a/dw/tablecell.cc
+++ b/dw/tablecell.cc
@@ -1,7 +1,7 @@
/*
* Dillo Widget
*
- * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
+ * Copyright 2014 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
@@ -17,97 +17,104 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-
#include "tablecell.hh"
-#include "../lout/debug.hh"
-#include <stdio.h>
+#include "table.hh"
namespace dw {
-int TableCell::CLASS_ID = -1;
+/**
+ * \brief Provided some common implementations of virtual widget
+ * methods.
+ *
+ * Once I understand how diamond inheritance works, a class TableCell
+ * will be provided, from which SimpleTableCell and AlignedTableCell
+ * will inherit, additionaly (in a multiple way).
+ */
+namespace tablecell {
-TableCell::TableCell (TableCell *ref, bool limitTextWidth):
- AlignedTextblock (limitTextWidth)
+bool getAdjustMinWidth ()
{
- DBG_OBJ_CREATE ("dw::TableCell");
- registerName ("dw::TableCell", &CLASS_ID);
-
- /** \bug ignoreLine1OffsetSometimes does not work? */
- //ignoreLine1OffsetSometimes = true;
- charWordIndex = -1;
- setRefTextblock (ref);
- setButtonSensitive(true);
+ return Table::getAdjustTableMinWidth ();
}
-TableCell::~TableCell()
+bool isBlockLevel ()
{
- DBG_OBJ_DELETE ();
+ return false;
}
-bool TableCell::wordWrap(int wordIndex, bool wrapAll)
+int correctAvailWidthOfChild (core::Widget *widget, core::Widget *child,
+ int width, bool forceValue)
{
- Textblock::Word *word;
- const char *p;
-
- bool ret = Textblock::wordWrap (wordIndex, wrapAll);
-
- if (charWordIndex == -1) {
- word = words->getRef (wordIndex);
- if (word->content.type == core::Content::TEXT) {
- if ((p = strchr (word->content.text,
- word->style->textAlignChar))) {
- charWordIndex = wordIndex;
- charWordPos = p - word->content.text + 1;
- } else if (word->style->textAlignChar == ' ' &&
- word->content.space) {
- charWordIndex = wordIndex + 1;
- charWordPos = 0;
- }
- }
+ DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell/correctAvailWidthOfChild",
+ "%p, %d, %s", child, width, forceValue ? "true" : "false");
+
+ // Make sure that this width does not exceed the width of the table
+ // cell (minus margin/border/padding).
+
+ if (width != -1) {
+ int thisWidth = widget->getAvailWidth (forceValue);
+ DBG_OBJ_MSGF_O ("resize", 1, widget, "thisWidth = %d", thisWidth);
+ if (thisWidth != -1)
+ width =
+ lout::misc::max (lout::misc::min (width,
+ thisWidth
+ - widget->boxDiffWidth ()),
+ 0);
}
- if (wordIndex == charWordIndex)
- updateValue ();
+ DBG_OBJ_MSGF_O ("resize", 1, widget, "=> %d", width);
+ DBG_OBJ_LEAVE_O (widget);
+ return width;
+}
- return ret;
+int correctAvailHeightOfChild (core::Widget *widget, core::Widget *child,
+ int height, bool forceValue)
+{
+ // Something to do?
+ return height;
}
-int TableCell::getValue ()
+void correctCorrectedRequisitionOfChild (core::Widget *widget,
+ core::Widget *child,
+ core::Requisition *requisition,
+ void (*splitHeightFun) (int, int*,
+ int*))
{
- Textblock::Word *word;
- int i, wordIndex;
- int w;
-
- if (charWordIndex == -1)
- wordIndex = words->size () -1;
- else
- wordIndex = charWordIndex;
-
- w = 0;
- for (i = 0; i < wordIndex; i++) {
- word = words->getRef (i);
- w += word->size.width + word->origSpace;
- }
+ DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell/correctRequisitionOfChild",
+ "%p, %d * (%d + %d), ...", child, requisition->width,
+ requisition->ascent, requisition->descent);
- if (charWordIndex == -1) {
- if (words->size () > 0) {
- word = words->getRef (words->size () - 1);
- w += word->size.width;
- }
- } else {
- word = words->getRef (charWordIndex);
- w += layout->textWidth (word->style->font, word->content.text,
- charWordPos);
- }
+ // Make sure that this width does not exceed the width of the table
+ // cell (minus margin/border/padding).
+
+ int thisWidth = widget->getAvailWidth (true);
+ DBG_OBJ_MSGF_O ("resize", 1, widget, "thisWidth = %d", thisWidth);
+ requisition->width =
+ lout::misc::max (lout::misc::min (requisition->width,
+ thisWidth - widget->boxDiffWidth ()),
+ 0);
- return w;
+ DBG_OBJ_LEAVE_O (widget);
}
-void TableCell::setMaxValue (int maxValue, int value)
+void correctCorrectedExtremesOfChild (core::Widget *widget, core::Widget *child,
+ core::Extremes *extremes)
{
- line1Offset = maxValue - value;
- queueResize (0, true);
+ // Something to do?
}
+int applyPerWidth (core::Widget *widget, int containerWidth,
+ core::style::Length perWidth)
+{
+ return core::style::multiplyWithPerLength (containerWidth, perWidth);
+}
+
+int applyPerHeight (core::Widget *widget, int containerHeight,
+ core::style::Length perHeight)
+{
+ return core::style::multiplyWithPerLength (containerHeight, perHeight);
+}
+
+} // namespace dw
+
} // namespace dw
diff --git a/dw/tablecell.hh b/dw/tablecell.hh
index f7c6042e..2e26c8e8 100644
--- a/dw/tablecell.hh
+++ b/dw/tablecell.hh
@@ -2,27 +2,33 @@
#define __DW_TABLECELL_HH__
#include "core.hh"
-#include "alignedtextblock.hh"
namespace dw {
-class TableCell: public AlignedTextblock
-{
-private:
- int charWordIndex, charWordPos;
+namespace tablecell {
-protected:
- bool wordWrap (int wordIndex, bool wrapAll);
+bool getAdjustMinWidth ();
+bool isBlockLevel ();
- int getValue ();
- void setMaxValue (int maxValue, int value);
+int correctAvailWidthOfChild (core::Widget *widget, core::Widget *child,
+ int width, bool forceValue);
+int correctAvailHeightOfChild (core::Widget *widget, core::Widget *child,
+ int height, bool forceValue);
-public:
- static int CLASS_ID;
+void correctCorrectedRequisitionOfChild (core::Widget *widget,
+ core::Widget *child,
+ core::Requisition *requisition,
+ void (*splitHeightFun) (int, int*,
+ int*));
+void correctCorrectedExtremesOfChild (core::Widget *widget, core::Widget *child,
+ core::Extremes *extremes);
- TableCell(TableCell *ref, bool limitTextWidth);
- ~TableCell();
-};
+int applyPerWidth (core::Widget *widget, int containerWidth,
+ core::style::Length perWidth);
+int applyPerHeight (core::Widget *widget, int containerHeight,
+ core::style::Length perHeight);
+
+} // namespace dw
} // namespace dw
diff --git a/dw/textblock.cc b/dw/textblock.cc
index 2c4ca20b..9b4d5380 100644
--- a/dw/textblock.cc
+++ b/dw/textblock.cc
@@ -1,7 +1,7 @@
/*
* Dillo Widget
*
- * Copyright 2005-2007, 2012-2013 Sebastian Geerken <sgeerken@dillo.org>
+ * Copyright 2005-2007, 2012-2014 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
@@ -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;
@@ -82,7 +83,7 @@ void Textblock::WordImgRenderer::getBgArea (int *x, int *y, int *width,
*x = textblock->allocation.x + this->xWordWidget;
*y = textblock->lineYOffsetCanvas (line);
*width = textblock->words->getRef(wordNo)->size.width;
- *height = line->boxAscent + line->boxDescent;
+ *height = line->borderAscent + line->borderDescent;
}
void Textblock::WordImgRenderer::getRefArea (int *xRef, int *yRef,
@@ -193,7 +194,7 @@ void Textblock::setPenaltyHyphen (int penaltyHyphen)
{
penalties[PENALTY_HYPHEN][0] = penaltyHyphen;
}
-
+
void Textblock::setPenaltyHyphen2 (int penaltyHyphen2)
{
penalties[PENALTY_HYPHEN][1] = penaltyHyphen2;
@@ -224,17 +225,18 @@ Textblock::Textblock (bool limitTextWidth)
{
DBG_OBJ_CREATE ("dw::Textblock");
registerName ("dw::Textblock", &CLASS_ID);
- setFlags (BLOCK_LEVEL);
- setFlags (USES_HINTS);
setButtonSensitive(true);
+ containingBlock = NULL;
hasListitemValue = false;
- innerPadding = 0;
+ leftInnerPadding = 0;
line1Offset = 0;
ignoreLine1OffsetSometimes = false;
mustQueueResize = false;
redrawY = 0;
+ DBG_OBJ_SET_NUM ("redrawY", redrawY);
lastWordDrawn = -1;
+ DBG_OBJ_SET_NUM ("lastWordDrawn", lastWordDrawn);
/*
* The initial sizes of lines and words should not be
@@ -250,22 +252,23 @@ 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;
- // random values
- availWidth = 100;
- availAscent = 100;
- availDescent = 0;
+ // -1 means undefined.
+ lineBreakWidth = -1;
+ DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth);
+
+ verticalOffset = 0;
+ DBG_OBJ_SET_NUM ("verticalOffset", verticalOffset);
this->limitTextWidth = limitTextWidth;
@@ -276,6 +279,8 @@ Textblock::Textblock (bool limitTextWidth)
hlEnd[layer].index = 0;
hlEnd[layer].nChar = 0;
}
+
+ initNewLine ();
}
Textblock::~Textblock ()
@@ -285,18 +290,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 +304,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,51 +328,132 @@ Textblock::~Textblock ()
*/
void Textblock::sizeRequestImpl (core::Requisition *requisition)
{
- PRINTF ("[%p] SIZE_REQUEST: ...\n", this);
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl");
+
+ int newLineBreakWidth = getAvailWidth (true);
+ if (newLineBreakWidth != lineBreakWidth) {
+ lineBreakWidth = newLineBreakWidth;
+ wrapRefLines = 0;
+ DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth);
+ DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines);
+ }
rewrap ();
showMissingLines ();
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);
-
- /* Note: the breakSpace of the last line is ignored, so breaks
- at the end of a textblock are not visible. */
- requisition->ascent = lines->getRef(0)->boxAscent;
- requisition->descent = lastLine->top
- + lastLine->boxAscent + lastLine->boxDescent -
- lines->getRef(0)->boxAscent;
+ Line *firstLine = lines->getRef(0), *lastLine = lines->getLastRef ();
+
+ // Note: the breakSpace of the last line is ignored, so breaks
+ // at the end of a textblock are not visible.
+
+ requisition->width = lastLine->maxLineWidth + leftInnerPadding
+ + getStyle()->boxDiffWidth ();
+
+ // Also regard collapsing of this widget top margin and the top
+ // margin of the first line box:
+ requisition->ascent = calcVerticalBorder (getStyle()->padding.top,
+ getStyle()->borderWidth.top,
+ getStyle()->margin.top,
+ firstLine->borderAscent,
+ firstLine->marginAscent);
+
+ // And here, regard collapsing of this widget bottom margin and the
+ // bottom margin of the last line box:
+ requisition->descent =
+ // (BTW, this line:
+ lastLine->top - firstLine->borderAscent + lastLine->borderAscent +
+ // ... is 0 for a block with one line, so special handling
+ // for this case is not necessary.)
+ calcVerticalBorder (getStyle()->padding.bottom,
+ getStyle()->borderWidth.bottom,
+ getStyle()->margin.bottom,
+ lastLine->borderDescent, lastLine->marginDescent);
} else {
- requisition->width = 0; // before: lastLineWidth;
- requisition->ascent = 0;
- requisition->descent = 0;
+ requisition->width = leftInnerPadding + getStyle()->boxDiffWidth ();
+ requisition->ascent = getStyle()->boxOffsetY ();
+ requisition->descent = getStyle()->boxRestHeight ();;
}
- PRINTF ("[%p] SIZE_REQUEST: inner padding = %d, boxDiffWidth = %d\n",
- this, innerPadding, getStyle()->boxDiffWidth ());
+ requisition->ascent += verticalOffset;
+
+ if (mustBeWidenedToAvailWidth ()) {
+ DBG_OBJ_MSGF ("resize", 1,
+ "before considering lineBreakWidth (= %d): %d * (%d + %d)",
+ lineBreakWidth, requisition->width, requisition->ascent,
+ requisition->descent);
+ if (requisition->width < lineBreakWidth)
+ requisition->width = lineBreakWidth;
+ } else
+ DBG_OBJ_MSG ("resize", 1, "lineBreakWidth needs no consideration");
- requisition->width += innerPadding + getStyle()->boxDiffWidth ();
- requisition->ascent += getStyle()->boxOffsetY ();
- requisition->descent += getStyle()->boxRestHeight ();
+ DBG_OBJ_MSGF ("resize", 1, "before correction: %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
- if (requisition->width < availWidth)
- requisition->width = availWidth;
+ correctRequisition (requisition, core::splitHeightPreserveAscent);
- PRINTF ("[%p] SIZE_REQUEST: %d x %d + %d\n", this, requisition->width,
- requisition->ascent, requisition->descent);
+ // 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.
+
+ // Notice that the order is not typical: correctRequisition should
+ // be the last call. However, calling correctRequisition after
+ // outOfFlowMgr->getSize may result again in a size which is too
+ // small for floats, so triggering again (and again) the resize
+ // idle function resulting in CPU hogging. See also
+ // getExtremesImpl.
+ //
+ // Is this really what we want? An alternative could be that
+ // OutOfFlowMgr::getSize honours CSS attributes an corrected sizes.
+
+ 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);
+
+ // Floats must be within the *content* area, not the *margin*
+ // area (which is equivalent to the requisition /
+ // allocation). For this reason, boxRestWidth() and
+ // boxRestHeight() must be considered.
+
+ if (oofWidth + boxRestWidth () > requisition->width)
+ requisition->width = oofWidth + boxRestWidth ();
+ if (oofHeight + boxRestHeight ()
+ > requisition->ascent + requisition->descent)
+ requisition->descent =
+ oofHeight + boxRestHeight () - requisition->ascent;
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "final: %d * (%d + %d)",
+ requisition->width, requisition->ascent, requisition->descent);
+ DBG_OBJ_LEAVE ();
+}
+
+int Textblock::calcVerticalBorder (int widgetPadding, int widgetBorder,
+ int widgetMargin, int lineBorderTotal,
+ int lineMarginTotal)
+{
+ DBG_OBJ_ENTER ("resize", 0, "calcVerticalBorder", "%d, %d, %d, %d, %d",
+ widgetPadding, widgetBorder, widgetMargin, lineBorderTotal,
+ lineMarginTotal);
+
+ int result;
+
+ if (widgetPadding == 0 && widgetBorder == 0) {
+ if (lineMarginTotal - lineBorderTotal >= widgetMargin)
+ result = lineMarginTotal;
+ else
+ result = widgetMargin + lineBorderTotal;
+ } else
+ result = lineMarginTotal + widgetPadding + widgetBorder + widgetMargin;
+
+ DBG_OBJ_MSGF ("resize", 0, "=> %d", result);
+ DBG_OBJ_LEAVE ();
+
+ return result;
}
/**
@@ -375,67 +461,156 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
*/
void Textblock::getWordExtremes (Word *word, core::Extremes *extremes)
{
- if (word->content.type == core::Content::WIDGET) {
- if (word->content.widget->usesHints ())
- word->content.widget->getExtremes (extremes);
- else {
- if (core::style::isPerLength
- (word->content.widget->getStyle()->width)) {
- extremes->minWidth = 0;
- if (word->content.widget->hasContents ())
- extremes->maxWidth = 1000000;
- else
- extremes->maxWidth = 0;
- } else if (core::style::isAbsLength
- (word->content.widget->getStyle()->width)) {
- /* Fixed lengths are only applied to the content, so we have to
- * add padding, border and margin. */
- extremes->minWidth = extremes->maxWidth =
- core::style::absLengthVal (word->content.widget->getStyle()
- ->width)
- + word->style->boxDiffWidth ();
- } else
- word->content.widget->getExtremes (extremes);
- }
- } else {
- extremes->minWidth = word->size.width;
- extremes->maxWidth = word->size.width;
- }
+ if (word->content.type == core::Content::WIDGET_IN_FLOW)
+ word->content.widget->getExtremes (extremes);
+ else
+ extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth =
+ extremes->maxWidthIntrinsic = word->size.width;
}
void Textblock::getExtremesImpl (core::Extremes *extremes)
{
- PRINTF ("[%p] GET_EXTREMES ...\n", this);
+ DBG_OBJ_ENTER0 ("resize", 0, "getExtremesImpl");
+
+ // TODO Can extremes depend on the available width? Should not; if
+ // they do, the following code must be reactivated, but it causes
+ // an endless recursion.
+#if 0
+ int newLineBreakWidth = getAvailWidth (true);
+ if (newLineBreakWidth != lineBreakWidth) {
+ lineBreakWidth = newLineBreakWidth;
+ wrapRefParagraphs = 0;
+ DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth);
+ DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefLines);
+ }
+#endif
fillParagraphs ();
if (paragraphs->size () == 0) {
/* empty page */
extremes->minWidth = 0;
+ extremes->minWidthIntrinsic = 0;
extremes->maxWidth = 0;
+ extremes->maxWidthIntrinsic = 0;
} else {
Paragraph *lastPar = paragraphs->getLastRef ();
extremes->minWidth = lastPar->maxParMin;
+ extremes->minWidthIntrinsic = lastPar->maxParMinIntrinsic;
extremes->maxWidth = lastPar->maxParMax;
+ extremes->maxWidthIntrinsic = lastPar->maxParMaxIntrinsic;
+
+ DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMin = %d (%d)",
+ paragraphs->size () - 1, lastPar->maxParMin,
+ lastPar->maxParMinIntrinsic);
+ DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMax = %d (%d)",
+ paragraphs->size () - 1, lastPar->maxParMax,
+ lastPar->maxParMaxIntrinsic);
}
- int diff = innerPadding + getStyle()->boxDiffWidth ();
+ DBG_OBJ_MSGF ("resize", 0, "after considering paragraphs: %d (%d) / %d (%d)",
+ extremes->minWidth, extremes->minWidthIntrinsic,
+ extremes->maxWidth, extremes->maxWidthIntrinsic);
+
+ int diff = leftInnerPadding + getStyle()->boxDiffWidth ();
extremes->minWidth += diff;
+ extremes->minWidthIntrinsic += diff;
extremes->maxWidth += diff;
+ extremes->maxWidthIntrinsic += diff;
+
+ DBG_OBJ_MSGF ("resize", 0, "after adding diff: %d (%d) / %d (%d)",
+ extremes->minWidth, extremes->minWidthIntrinsic,
+ extremes->maxWidth, extremes->maxWidthIntrinsic);
+
+ // For the order, see similar reasoning in sizeRequestImpl.
+
+ correctExtremes (extremes);
- PRINTF ("[%p] GET_EXTREMES => %d / %d\n",
- this, extremes->minWidth, extremes->maxWidth);
+ DBG_OBJ_MSGF ("resize", 0, "after correction: %d (%d) / %d (%d)",
+ extremes->minWidth, extremes->minWidthIntrinsic,
+ extremes->maxWidth, extremes->maxWidthIntrinsic);
+
+ if (outOfFlowMgr) {
+ int oofMinWidth, oofMaxWidth;
+ outOfFlowMgr->getExtremes (extremes, &oofMinWidth, &oofMaxWidth);
+
+ DBG_OBJ_MSGF ("resize", 1, "OOFM correction: %d / %d",
+ oofMinWidth, oofMaxWidth);
+
+ extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth);
+ extremes->minWidthIntrinsic =
+ misc::max (extremes->minWidthIntrinsic, oofMinWidth);
+ extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth);
+ extremes->maxWidthIntrinsic =
+ misc::max (extremes->maxWidthIntrinsic, oofMinWidth);
+ }
+
+ DBG_OBJ_MSGF ("resize", 0,
+ "finally, after considering OOFM: %d (%d) / %d (%d)",
+ extremes->minWidth, extremes->minWidthIntrinsic,
+ extremes->maxWidth, extremes->maxWidthIntrinsic);
+ DBG_OBJ_LEAVE ();
}
void Textblock::sizeAllocateImpl (core::Allocation *allocation)
{
- PRINTF ("[%p] SIZE_ALLOCATE: %d, %d, %d x %d + %d\n",
- this, allocation->x, allocation->y, allocation->width,
- allocation->ascent, allocation->descent);
+ DBG_OBJ_ENTER ("resize", 0, "sizeAllocateImpl", "%d, %d; %d * (%d + %d)",
+ allocation->x, allocation->y, allocation->width,
+ allocation->ascent, allocation->descent);
showMissingLines ();
+ // In some cases, this allocation results in child allocation which
+ // exceed the top of this allocation, which will then result in an
+ // endless resize idle cascade and CPU hogging (when floats come
+ // into play).
+ //
+ // Example:
+ //
+ // <div id="id1" style="height: 50px">
+ // <div id="id2">...</div>
+ // <div>
+ //
+ // Assume that the inner section, div#id2, has a height of 200px =
+ // 100px (ascent) + 100px (descent). For the outer section,
+ // div#id1, this will be initially calculated for the size, but
+ // then (because of CSS 'height') reduced to 50px = 50px (ascent) +
+ // 0px (descent). Without the following correction, the inner
+ // section (div#id2) would be allocated at 50px top of the
+ // allocation of the outer section: childAllocation->y =
+ // allocation->y - 50.
+ //
+ // For this reason, we calculat "childBaseAllocation", which will
+ // avoid this case; in the example above, the height will be 50px =
+ // 100px (ascent) - 50px (descent: negative).
+
+ childBaseAllocation.x = allocation->x;
+ childBaseAllocation.y = allocation->y;
+ childBaseAllocation.width = allocation->width;
+ childBaseAllocation.ascent =
+ misc::max (allocation->ascent,
+ // Reconstruct the initial size; see
+ // Textblock::sizeRequestImpl.
+ (lines->size () > 0 ?
+ calcVerticalBorder (getStyle()->padding.top,
+ getStyle()->borderWidth.top,
+ getStyle()->margin.top,
+ lines->getRef(0)->borderAscent,
+ lines->getRef(0)->marginAscent) :
+ getStyle()->boxOffsetY ()) + verticalOffset);
+ childBaseAllocation.descent =
+ allocation->ascent + allocation->descent - childBaseAllocation.ascent;
+
+ DBG_OBJ_SET_NUM ("childBaseAllocation.x", childBaseAllocation.x);
+ DBG_OBJ_SET_NUM ("childBaseAllocation.y", childBaseAllocation.y);
+ DBG_OBJ_SET_NUM ("childBaseAllocation.width", childBaseAllocation.width);
+ DBG_OBJ_SET_NUM ("childBaseAllocation.ascent", childBaseAllocation.ascent);
+ DBG_OBJ_SET_NUM ("childBaseAllocation.descent", childBaseAllocation.descent);
+
+ if (containingBlock->outOfFlowMgr)
+ containingBlock->outOfFlowMgr->sizeAllocateStart (this, allocation);
+
int lineIndex, wordIndex;
Line *line;
Word *word;
@@ -445,11 +620,28 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
if (allocation->width != this->allocation.width) {
redrawY = 0;
+ DBG_OBJ_SET_NUM ("redrawY", redrawY);
}
+ DBG_OBJ_MSG_START ();
+
for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) {
+ DBG_OBJ_MSGF ("resize", 1, "line %d", lineIndex);
+ DBG_OBJ_MSG_START ();
+
+ // Especially for floats, allocation->width may be different
+ // from the line break width, so that for centered and right
+ // text, the offsets have to be recalculated again. However, if
+ // the allocation width is greater than the line break width,
+ // due to wide unbreakable lines (large image etc.), use the
+ // original line break width.
+ calcTextOffset (lineIndex,
+ misc::min (childBaseAllocation.width, lineBreakWidth));
+
line = lines->getRef (lineIndex);
- xCursor = lineXOffsetWidget (line);
+ xCursor = line->textOffset;
+
+ DBG_OBJ_MSGF ("resize", 1, "xCursor = %d (initially)", xCursor);
for (wordIndex = line->firstWord; wordIndex <= line->lastWord;
wordIndex++) {
@@ -457,11 +649,21 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
if (wordIndex == lastWordDrawn + 1) {
redrawY = misc::min (redrawY, lineYOffsetWidget (line));
+ DBG_OBJ_SET_NUM ("redrawY", redrawY);
}
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
+ DBG_OBJ_MSGF ("resize", 1,
+ "allocating widget in flow: line %d, word %d",
+ lineIndex, wordIndex);
+
+ childAllocation.x = xCursor + childBaseAllocation.x;
+
+ DBG_OBJ_MSGF ("resize", 1, "childAllocation.x = %d + %d = %d",
+ xCursor, childBaseAllocation.x, childAllocation.x);
+
/** \todo Justification within the line is done here. */
- childAllocation.x = xCursor + allocation->x;
+
/* align=top:
childAllocation.y = line->top + allocation->y;
*/
@@ -469,16 +671,19 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
/* align=bottom (base line) */
/* Commented lines break the n2 and n3 test cases at
* http://www.dillo.org/test/img/ */
- childAllocation.y =
- 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;
- childAllocation.descent = word->size.descent
- + word->content.widget->getStyle()->margin.bottom;
+ childAllocation.y = lineYOffsetCanvas (line)
+ + (line->borderAscent - word->size.ascent);
+ DBG_OBJ_MSGF ("resize", 1,
+ "childAllocation.y = %d + (%d - %d) = %d",
+ lineYOffsetCanvas (line),
+ line->borderAscent, word->size.ascent,
+ childAllocation.y);
+
+ childAllocation.width = word->size.width;
+ childAllocation.ascent = word->size.ascent;
+ childAllocation.descent = word->size.descent;
+
oldChildAllocation = word->content.widget->getAllocation();
if (childAllocation.x != oldChildAllocation->x ||
@@ -488,9 +693,11 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
* so we need to redraw from this line onwards.
*/
redrawY = misc::min (redrawY, lineYOffsetWidget (line));
+ DBG_OBJ_SET_NUM ("redrawY", redrawY);
if (word->content.widget->wasAllocated ()) {
redrawY = misc::min (redrawY,
oldChildAllocation->y - this->allocation.y);
+ DBG_OBJ_SET_NUM ("redrawY", redrawY);
}
} else if (childAllocation.ascent + childAllocation.descent !=
@@ -513,40 +720,134 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
core::Content::BREAK)) {
int childChangedY =
- misc::min(childAllocation.y - allocation->y +
+ misc::min(childAllocation.y - childBaseAllocation.y +
childAllocation.ascent + childAllocation.descent,
oldChildAllocation->y - this->allocation.y +
oldChildAllocation->ascent +
oldChildAllocation->descent);
redrawY = misc::min (redrawY, childChangedY);
+ DBG_OBJ_SET_NUM ("redrawY", redrawY);
} else {
redrawY = misc::min (redrawY, lineYOffsetWidget (line));
+ DBG_OBJ_SET_NUM ("redrawY", redrawY);
}
}
word->content.widget->sizeAllocate (&childAllocation);
}
xCursor += (word->size.width + word->effSpace);
+ DBG_OBJ_MSGF ("resize", 1, "xCursor = %d (after word %d)",
+ xCursor, wordIndex);
+ DBG_MSG_WORD("resize", 1, "<i>that is:</i> ", wordIndex, "");
}
+
+ DBG_OBJ_MSG_END ();
}
+ DBG_OBJ_MSG_END ();
+
+ if (containingBlock->outOfFlowMgr)
+ containingBlock->outOfFlowMgr->sizeAllocateEnd (this);
+
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));
- y = lineYOffsetCanvasAllocation (line, allocation);
+ y = lineYOffsetCanvas (line);
}
changeAnchor (anchor->name, y);
}
+
+ DBG_OBJ_LEAVE ();
+}
+
+int Textblock::getAvailWidthOfChild (Widget *child, bool forceValue)
+{
+ DBG_OBJ_ENTER ("resize", 0, "Textblock/getAvailWidthOfChild", "%p, %s",
+ child, forceValue ? "true" : "false");
+
+ int width;
+
+ if (child->getStyle()->width == core::style::LENGTH_AUTO) {
+ // No width specified: similar to standard implementation (see
+ // there), but "leftInnerPadding" has to be considered, too.
+ DBG_OBJ_MSG ("resize", 1, "no specification");
+ if (forceValue)
+ width = misc::max (getAvailWidth (true) - boxDiffWidth ()
+ - leftInnerPadding,
+ 0);
+ else
+ width = -1;
+ } else
+ width = Widget::getAvailWidthOfChild (child, forceValue);
+
+ if (forceValue && this == child->getContainer () &&
+ !mustBeWidenedToAvailWidth ()) {
+ core::Extremes extremes;
+ getExtremes (&extremes);
+ if (width > extremes.maxWidth - boxDiffWidth () - leftInnerPadding)
+ width = extremes.maxWidth - boxDiffWidth () - leftInnerPadding;
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", width);
+ DBG_OBJ_LEAVE ();
+ return width;
+}
+
+
+
+void Textblock::containerSizeChangedForChildren ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
+
+ for (int i = 0; i < words->size (); i++) {
+ Word *word = words->getRef (i);
+ if (word->content.type == core::Content::WIDGET_IN_FLOW)
+ word->content.widget->containerSizeChanged ();
+ }
+
+ if (outOfFlowMgr)
+ outOfFlowMgr->containerSizeChangedForChildren ();
+
+ DBG_OBJ_LEAVE ();
+}
+
+bool Textblock::affectsSizeChangeContainerChild (Widget *child)
+{
+ DBG_OBJ_ENTER ("resize", 0,
+ "Textblock/affectsSizeChangeContainerChild", "%p", child);
+
+ // See Textblock::getAvailWidthForChild() and Textblock::oofSizeChanged():
+ // Extremes changes affect the size of the child, too:
+ bool ret;
+ if (!mustBeWidenedToAvailWidth () &&
+ (extremesQueued () || extremesChanged ()))
+ ret = true;
+ else
+ ret = Widget::affectsSizeChangeContainerChild (child);
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false");
+ DBG_OBJ_LEAVE ();
+ return ret;
+}
+
+bool Textblock::usesAvailWidth ()
+{
+ return true;
}
void Textblock::resizeDrawImpl ()
{
+ DBG_OBJ_ENTER0 ("draw", 0, "resizeDrawImpl");
+
queueDrawArea (0, redrawY, allocation.width, getHeight () - redrawY);
if (lines->size () > 0) {
Line *lastLine = lines->getRef (lines->size () - 1);
@@ -554,99 +855,141 @@ void Textblock::resizeDrawImpl ()
* draw any new added words (see sizeAllocateImpl()).
*/
lastWordDrawn = lastLine->lastWord;
+ DBG_OBJ_SET_NUM ("lastWordDrawn", lastWordDrawn);
}
redrawY = getHeight ();
+ DBG_OBJ_SET_NUM ("redrawY", redrawY);
+
+ DBG_OBJ_LEAVE ();
}
void Textblock::markSizeChange (int ref)
{
- PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", this, ref, wrapRefLines);
+ DBG_OBJ_ENTER ("resize", 0, "markSizeChange", "%d", ref);
- /* 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 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 (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);
+ }
+
+ DBG_OBJ_LEAVE ();
}
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_ENTER ("resize", 1, "markExtremesChange", "%d", ref);
+
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markExtremesChange (ref);
+ } else {
+ PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n",
+ this, ref, wrapRefParagraphs);
+
+ /* By the way: ref == -1 may have two different causes: (i) flush()
+ calls "queueResize (-1, true)", when no rewrapping is necessary;
+ and (ii) a word may have parentRef == -1 , when it is not yet
+ added to a line. In the latter case, nothing has to be done
+ now, but addLine(...) will do everything necessary. */
+ if (ref != -1) {
+ if (wrapRefParagraphs == -1)
+ wrapRefParagraphs = OutOfFlowMgr::getLineNoFromRef (ref);
+ else
+ wrapRefParagraphs =
+ misc::min (wrapRefParagraphs,
+ OutOfFlowMgr::getLineNoFromRef (ref));
+ }
+
+ DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs);
}
- PRINTF (" ... => %d\n", wrapRefParagraphs);
+ DBG_OBJ_LEAVE ();
}
-void Textblock::setWidth (int width)
+void Textblock::notifySetAsTopLevel()
{
- /* 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());
+ PRINTF ("%p becomes toplevel\n", this);
+ containingBlock = this;
+ PRINTF ("-> %p is its own containing block\n", this);
+}
- availWidth = width;
- queueResize (0, false);
- mustQueueResize = false;
- redrawY = 0;
- }
+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) ||
+ // Inline blocks are containing blocks, too.
+ widget->getStyle()->display == core::style::DISPLAY_INLINE_BLOCK ||
+ // Same for blocks with 'overview' set to another value than
+ // (the default value) 'visible'.
+ widget->getStyle()->overflow != core::style::OVERFLOW_VISIBLE ||
+ // Finally, "out of flow" in a narrower sense: floats and
+ // absolute positions.
+ OutOfFlowMgr::isWidgetOutOfFlow (widget));
}
-void Textblock::setAscent (int ascent)
+void Textblock::notifySetParent ()
{
- if (availAscent != ascent) {
- //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
- // "setAscent: Calling queueResize, "
- // "in page with %d word(s)\n",
- // words->size());
+ PRINTF ("%p becomes a child of %p\n", this, getParent());
- availAscent = ascent;
- queueResize (0, false);
- mustQueueResize = false;
- }
+ // 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::setDescent (int descent)
+bool Textblock::isBlockLevel ()
{
- if (availDescent != descent) {
- //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
- // "setDescent: Calling queueResize, "
- // "in page with %d word(s)\n",
- // words->size());
-
- availDescent = descent;
- queueResize (0, false);
- mustQueueResize = false;
- }
+ return true;
}
bool Textblock::buttonPressImpl (core::EventButton *event)
@@ -743,8 +1086,8 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
} else {
Line *lastLine = lines->getRef (lines->size () - 1);
int yFirst = lineYOffsetCanvasI (0);
- int yLast = lineYOffsetCanvas (lastLine) + lastLine->boxAscent +
- lastLine->boxDescent;
+ int yLast = lineYOffsetCanvas (lastLine) + lastLine->borderAscent +
+ lastLine->borderDescent;
if (event->yCanvas < yFirst) {
// Above the first line: take the first word.
wordIndex = 0;
@@ -753,19 +1096,20 @@ 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 >
- (lineYOffsetWidget (line) + line->boxAscent + line->boxDescent)) {
+ if (event->yWidget > (lineYOffsetWidget (line) +
+ line->borderAscent + line->borderDescent)) {
// Choose this break.
wordIndex = line->lastWord;
charPos = core::SelectionState::END_OF_WORD;
- } else if (event->xWidget < lineXOffsetWidget (line)) {
+ } else if (event->xWidget < line->textOffset) {
// Left of the first word in the line.
wordIndex = line->firstWord;
} else {
- int nextWordStartX = lineXOffsetWidget (line);
+ int nextWordStartX = line->textOffset;
for (wordIndex = line->firstWord;
wordIndex <= line->lastWord;
@@ -778,7 +1122,8 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
if (event->xWidget >= wordStartX &&
event->xWidget < nextWordStartX) {
// We have found the word.
- int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
+ int yWidgetBase =
+ lineYOffsetWidget (line) + line->borderAscent;
if (event->xWidget >= nextWordStartX - word->effSpace) {
charPos = core::SelectionState::END_OF_WORD;
@@ -860,8 +1205,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;
@@ -877,65 +1223,6 @@ core::Iterator *Textblock::iterator (core::Content::Type mask, bool atEnd)
return new TextblockIterator (this, mask, atEnd);
}
-
-/**
- * Calculate the size of a widget within the page.
- * (Subject of change in the near future!)
- */
-void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size)
-{
- core::Requisition requisition;
- int availWidth, availAscent, availDescent;
- core::style::Style *wstyle = widget->getStyle();
-
- /* We ignore line1_offset[_eff]. */
- availWidth = this->availWidth - getStyle()->boxDiffWidth () - innerPadding;
- availAscent = this->availAscent - getStyle()->boxDiffHeight ();
- availDescent = this->availDescent;
-
- if (widget->usesHints ()) {
- widget->setWidth (availWidth);
- widget->setAscent (availAscent);
- widget->setDescent (availDescent);
- widget->sizeRequest (size);
- } else {
- if (wstyle->width == core::style::LENGTH_AUTO ||
- wstyle->height == core::style::LENGTH_AUTO)
- widget->sizeRequest (&requisition);
-
- if (wstyle->width == core::style::LENGTH_AUTO)
- size->width = requisition.width;
- else if (core::style::isAbsLength (wstyle->width))
- /* Fixed lengths are only applied to the content, so we have to
- * add padding, border and margin. */
- size->width = core::style::absLengthVal (wstyle->width)
- + wstyle->boxDiffWidth ();
- else
- size->width =
- core::style::multiplyWithPerLength (availWidth, wstyle->width);
-
- if (wstyle->height == core::style::LENGTH_AUTO) {
- size->ascent = requisition.ascent;
- size->descent = requisition.descent;
- } else if (core::style::isAbsLength (wstyle->height)) {
- /* Fixed lengths are only applied to the content, so we have to
- * add padding, border and margin. */
- size->ascent = core::style::absLengthVal (wstyle->height)
- + wstyle->boxDiffHeight ();
- size->descent = 0;
- } else {
- size->ascent =
- core::style::multiplyWithPerLength (wstyle->height, availAscent);
- size->descent =
- core::style::multiplyWithPerLength (wstyle->height, availDescent);
- }
- }
-
- /* ascent and descent in words do not contain margins. */
- size->ascent -= wstyle->margin.top;
- size->descent -= wstyle->margin.bottom;
-}
-
/*
* Draw the decorations on a word.
*/
@@ -994,13 +1281,13 @@ void Textblock::drawText(core::View *view, core::style::Style *style,
if (isStart) {
/* \bug No way to know about non-ASCII punctuation. */
bool initial_seen = false;
-
+
for (int i = 0; i < start; i++)
if (!ispunct(text[i]))
initial_seen = true;
if (initial_seen)
break;
-
+
int after = 0;
text += start;
while (ispunct(text[after]))
@@ -1009,7 +1296,7 @@ void Textblock::drawText(core::View *view, core::style::Style *style,
after = layout->nextGlyph(text, after);
if (after > len)
after = len;
-
+
char *initial = layout->textToUpper(text, after);
int newlen = strlen(initial) + len-after;
str = (char *)malloc(newlen + 1);
@@ -1020,7 +1307,7 @@ void Textblock::drawText(core::View *view, core::style::Style *style,
}
break;
}
-
+
view->drawText(style->font, style->color, shading, x, y,
str ? str : text + start, str ? strlen(str) : len);
if (str)
@@ -1049,8 +1336,8 @@ void Textblock::drawWord (Line *line, int wordIndex1, int wordIndex2,
for (int i = wordIndex1; i <= wordIndex2; i++)
w += words->getRef(i)->size.width;
w += words->getRef(wordIndex2)->hyphenWidth;
- drawBox (view, style, area, xWidget, yWidgetBase - line->boxAscent,
- w, line->boxAscent + line->boxDescent, false);
+ drawBox (view, style, area, xWidget, yWidgetBase - line->borderAscent,
+ w, line->borderAscent + line->borderDescent, false);
}
if (wordIndex1 == wordIndex2 && !drawHyphen) {
@@ -1080,7 +1367,7 @@ void Textblock::drawWord (Line *line, int wordIndex1, int wordIndex2,
text[p++] = hyphenDrawChar[i];
text[p++] = 0;
}
-
+
drawWord0 (wordIndex1, wordIndex2, text, totalWidth, drawHyphen,
style, view, area, xWidget, yWidgetBase);
}
@@ -1245,8 +1532,17 @@ void Textblock::drawSpace(int wordIndex, core::View *view,
*/
void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
{
- int xWidget = lineXOffsetWidget(line);
- int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
+ DBG_OBJ_ENTER ("draw", 0, "drawLine", "..., %d, %d, %d * %d",
+ area->x, area->y, area->width, area->height);
+
+ int xWidget = line->textOffset;
+ int yWidgetBase = lineYOffsetWidget (line) + line->borderAscent;
+
+ DBG_OBJ_MSGF ("draw", 1, "line from %d to %d (%d words), at (%d, %d)",
+ line->firstWord, line->lastWord, words->size (),
+ xWidget, yWidgetBase);
+ DBG_MSG_WORD ("draw", 0, "<i>line starts with: </i>", line->firstWord, "");
+ DBG_MSG_WORD ("draw", 0, "<i>line ends with: </i>", line->lastWord, "");
for (int wordIndex = line->firstWord;
wordIndex <= line->lastWord && xWidget < area->x + area->width;
@@ -1256,10 +1552,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;
@@ -1290,8 +1586,8 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
if (word->spaceStyle->hasBackground ())
drawBox (view, word->spaceStyle, area,
xWidget + wordSize,
- yWidgetBase - line->boxAscent, word->effSpace,
- line->boxAscent + line->boxDescent, false);
+ yWidgetBase - line->borderAscent, word->effSpace,
+ line->borderAscent + line->borderDescent, false);
drawSpace(wordIndex, view, area, xWidget + wordSize,
yWidgetBase);
}
@@ -1300,13 +1596,46 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
}
xWidget += wordSize + word->effSpace;
}
+
+ DBG_OBJ_LEAVE ();
}
/**
- * 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, calcVerticalBorder (getStyle()->padding.top,
+ getStyle()->borderWidth.top,
+ getStyle()->margin.top,
+ lines->getRef(0)->borderAscent,
+ lines->getRef(0)->marginAscent));
+}
+
+int Textblock::findLineIndexWhenAllocated (int y)
+{
+ assert (wasAllocated ());
+ return findLineIndex (y, childBaseAllocation.ascent);
+}
+
+int Textblock::findLineIndex (int y, int ascent)
+{
+ DBG_OBJ_ENTER ("events", 0, "findLineIndex", "%d, %d", y, ascent);
+
+ core::Allocation alloc;
+ alloc.ascent = ascent; // More is not needed.
+
int maxIndex = lines->size () - 1;
int step, index, low = 0;
@@ -1314,12 +1643,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++;
/*
@@ -1332,6 +1661,10 @@ int Textblock::findLineIndex (int y)
* Dw_page_find_link() --EG
* That function has now been inlined into Dw_page_motion_notify() --JV
*/
+
+ DBG_OBJ_MSGF ("events", 1, "=> %d", low);
+ DBG_OBJ_LEAVE ();
+
return low;
}
@@ -1340,12 +1673,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 +1730,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)
+ yWidgetBase = lineYOffsetWidget (line) + line->borderAscent;
+ if (yWidgetBase + line->borderDescent <= y)
return NULL;
- xCursor = lineXOffsetWidget (line);
+ xCursor = line->textOffset;
for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
word = words->getRef (wordIndex);
lastXCursor = xCursor;
@@ -1432,23 +1766,37 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace)
void Textblock::draw (core::View *view, core::Rectangle *area)
{
- PRINTF ("DRAW: %d, %d, %d x %d\n",
- area->x, area->y, area->width, area->height);
+ DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d",
+ area->x, area->y, area->width, area->height);
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);
if (lineYOffsetWidget (line) >= area->y + area->height)
break;
+ DBG_OBJ_MSGF ("draw", 0, "line %d (of %d)", lineIndex, lines->size ());
drawLine (line, view, area);
}
+
+ if(outOfFlowMgr)
+ outOfFlowMgr->draw(view, area);
+
+ DBG_OBJ_LEAVE ();
}
/**
@@ -1457,11 +1805,22 @@ 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_ENTER ("construct.word", 0, "addWord", "%d * (%d + %d), %d, %p",
+ width, ascent, descent, flags, style);
+
+ if (lineBreakWidth == -1) {
+ lineBreakWidth = getAvailWidth (true);
+ DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth);
+ }
+
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_LEAVE ();
+ return words->getRef (wordNo);
}
/**
@@ -1476,6 +1835,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);
@@ -1581,7 +1955,7 @@ int Textblock::textWidth(const char *text, int start, int len,
if (isStart) {
/* \bug No way to know about non-ASCII punctuation. */
bool initial_seen = false;
-
+
for (int i = 0; i < start; i++)
if (!ispunct(text[i]))
initial_seen = true;
@@ -1589,7 +1963,7 @@ int Textblock::textWidth(const char *text, int start, int len,
ret = layout->textWidth(style->font, text+start, len);
} else {
int after = 0;
-
+
text += start;
while (ispunct(text[after]))
after++;
@@ -1676,7 +2050,8 @@ void Textblock::calcTextSize (const char *text, size_t len,
void Textblock::addText (const char *text, size_t len,
core::style::Style *style)
{
- PRINTF ("[%p] ADD_TEXT (%d characters)\n", this, (int)len);
+ DBG_OBJ_ENTER ("construct.word", 0, "addText", "..., %d, %p",
+ (int)len, style);
// Count dividing characters.
int numParts = 1;
@@ -1736,10 +2111,10 @@ void Textblock::addText (const char *text, size_t len,
foundDiv = j;
}
}
-
+
if (foundDiv != -1) {
int lDiv = strlen (divChars[foundDiv].s);
-
+
if (divChars[foundDiv].charRemoved) {
assert (divChars[foundDiv].penaltyIndexLeft != -1);
assert (divChars[foundDiv].penaltyIndexRight == -1);
@@ -1824,7 +2199,7 @@ void Textblock::addText (const char *text, size_t len,
// Finished!
for (int i = 0; i < numParts; i++) {
short flags = 0;
-
+
// If this parts adjoins at least one division characters,
// for which canBeHyphenated is set to false (this is the
// case for soft hyphens), do not hyphenate.
@@ -1847,14 +2222,14 @@ void Textblock::addText (const char *text, size_t len,
flags |= Word::WORD_START;
if (i == numParts - 1)
flags |= Word::WORD_END;
-
+
addText0 (text + partStart[i], partEnd[i] - partStart[i],
flags, style, &wordSize[i]);
//printf ("[%p] %d: added word part: ", this, words->size() - 1);
//printWordWithFlags (words->getLastRef());
//printf ("\n");
-
+
//PRINTF("H... [%d] '", i);
//for (int j = partStart[i]; j < partEnd[i]; j++)
// PUTCHAR(text[j]);
@@ -1865,6 +2240,7 @@ void Textblock::addText (const char *text, size_t len,
setBreakOption (word, style, penalties[partPenaltyIndex[i]][0],
penalties[partPenaltyIndex[i]][1], false);
+ DBG_SET_WORD (words->size () - 1);
if (charRemoved[i])
// Currently, only unconditional hyphens (UTF-8:
@@ -1881,6 +2257,8 @@ void Textblock::addText (const char *text, size_t len,
}
}
}
+
+ DBG_OBJ_LEAVE ();
}
void Textblock::calcTextSizes (const char *text, size_t textLen,
@@ -1923,6 +2301,19 @@ void Textblock::calcTextSizes (const char *text, size_t textLen,
void Textblock::addText0 (const char *text, size_t len, short flags,
core::style::Style *style, core::Requisition *size)
{
+ DBG_OBJ_ENTER ("construct.word", 0, "addText0",
+ "..., %d, %s:%s:%s:%s:%s:%s:%s, %p, %d * (%d + %d)",
+ (int)len,
+ // Ugly copy&paste from printWordFlags:
+ (flags & Word::CAN_BE_HYPHENATED) ? "h?" : "--",
+ (flags & Word::DIV_CHAR_AT_EOL) ? "de" : "--",
+ (flags & Word::PERM_DIV_CHAR) ? "dp" : "--",
+ (flags & Word::DRAW_AS_ONE_TEXT) ? "t1" : "--",
+ (flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) ? "um" : "--",
+ (flags & Word::WORD_START) ? "st" : "--",
+ (flags & Word::WORD_END) ? "en" : "--",
+ style, size->width, size->ascent, size->descent);
+
//printf("[%p] addText0 ('", this);
//for (size_t i = 0; i < len; i++)
// putchar(text[i]);
@@ -1936,7 +2327,17 @@ 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_SET_WORD (words->size () - 1);
+
+ // 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);
+
+ DBG_OBJ_LEAVE ();
}
/**
@@ -1944,28 +2345,57 @@ 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;
+ DBG_OBJ_ENTER ("construct.word", 0, "addWidget", "%p, %p", widget, style);
/* 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;
+ DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef);
- 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());
+
+ if (containingBlock->outOfFlowMgr == NULL) {
+ containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock);
+ DBG_OBJ_ASSOC (containingBlock, containingBlock->outOfFlowMgr);
+ }
+
+ if (OutOfFlowMgr::isWidgetOutOfFlow (widget)) {
+ PRINTF (" -> out of flow.\n");
+
+ widget->setParent (containingBlock);
+ widget->setGenerator (this);
+ containingBlock->outOfFlowMgr->addWidgetOOF (widget, this,
+ words->size ());
+ Word *word = addWord (0, 0, 0, 0, style);
+ word->content.type = core::Content::WIDGET_OOF_REF;
+ word->content.widget = widget;
- word->content.type = core::Content::WIDGET;
- 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);
+ } else {
+ PRINTF (" -> within flow.\n");
- //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1,
- // word->content.widget);
+ widget->setParent (this);
+
+ // TODO Replace (perhaps) later "textblock" by "OOF aware widget".
+ if (widget->instanceOf (Textblock::CLASS_ID))
+ containingBlock->outOfFlowMgr->addWidgetInFlow ((Textblock*)widget,
+ this, words->size ());
+
+ core::Requisition size;
+ widget->sizeRequest (&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_SET_WORD (words->size () - 1);
processWord (words->size () - 1);
//DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
@@ -1975,6 +2405,8 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
// "Assigning parent_ref = %d to added word %d, "
// "in page with %d word(s)\n",
// lines->size () - 1, words->size() - 1, words->size());
+
+ DBG_OBJ_LEAVE ();
}
/**
@@ -1986,8 +2418,11 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
*/
bool Textblock::addAnchor (const char *name, core::style::Style *style)
{
+ DBG_OBJ_ENTER ("construct.word", 0, "addAnchor", "\"%s\", %p", name, style);
+
char *copy;
int y;
+ bool result;
// Since an anchor does not take any space, it is safe to call
// addAnchor already here.
@@ -2005,7 +2440,7 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style)
* \todo It may be necessary for future uses to save the anchor in
* some way, e.g. when parts of the widget tree change.
*/
- return false;
+ result = false;
else {
Anchor *anchor;
@@ -2013,8 +2448,12 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style)
anchor = anchors->getRef(anchors->size() - 1);
anchor->name = copy;
anchor->wordIndex = words->size();
- return true;
+ result = true;
}
+
+ DBG_OBJ_MSGF ("construct.word", 0, "=> %s", result ? "true" : "false");
+ DBG_OBJ_LEAVE ();
+ return result;
}
@@ -2023,39 +2462,62 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style)
*/
void Textblock::addSpace (core::style::Style *style)
{
+ DBG_OBJ_ENTER ("construct.word", 0, "addSpace", "%p", style);
+
int wordIndex = words->size () - 1;
if (wordIndex >= 0) {
fillSpace (wordIndex, style);
+ DBG_SET_WORD (wordIndex);
accumulateWordData (wordIndex);
correctLastWordExtremes ();
}
+
+ DBG_OBJ_LEAVE ();
}
/**
* 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.
*/
void Textblock::addBreakOption (core::style::Style *style, bool forceBreak)
{
+ DBG_OBJ_ENTER ("construct.word", 0, "addBreakOption", "%p, %s",
+ style, forceBreak ? "true" : "false");
+
int wordIndex = words->size () - 1;
if (wordIndex >= 0) {
setBreakOption (words->getRef(wordIndex), style, 0, 0, forceBreak);
+ DBG_SET_WORD (wordIndex);
// Call of accumulateWordData() is not needed here.
correctLastWordExtremes ();
}
+
+ DBG_OBJ_LEAVE ();
}
void Textblock::fillSpace (int wordNo, core::style::Style *style)
{
+ DBG_OBJ_ENTER ("construct.word", 0, "fillSpace", "%d, ...", wordNo);
+
+ DBG_OBJ_MSGF ("construct.word", 1, "style.white-space = %s",
+ style->whiteSpace == core::style::WHITE_SPACE_NORMAL ? "normal"
+ : style->whiteSpace == core::style::WHITE_SPACE_PRE ? "pre"
+ : style->whiteSpace == core::style::WHITE_SPACE_NOWRAP ?
+ "nowrap"
+ : style->whiteSpace == core::style::WHITE_SPACE_PRE_WRAP ?
+ "pre-wrap"
+ : style->whiteSpace == core::style::WHITE_SPACE_PRE_LINE ?
+ "pre-line" : "???");
+
// Old comment:
- //
+ //
// According to
// http://www.w3.org/TR/CSS2/text.html#white-space-model: "line
// breaking opportunities are determined based on the text
// prior to the white space collapsing steps".
- //
+ //
// So we call addBreakOption () for each Textblock::addSpace ()
// call. This is important e.g. to be able to break between
// foo and bar in: <span style="white-space:nowrap">foo </span>
@@ -2067,30 +2529,26 @@ 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;
- word->effSpace = word->origSpace = style->font->spaceWidth +
- style->wordSpacing;
-
- //DBG_OBJ_ARRSET_NUM (this, "words.%d.origSpace", wordIndex,
- // word->origSpace);
- //DBG_OBJ_ARRSET_NUM (this, "words.%d.effSpace", wordIndex,
- // word->effSpace);
- //DBG_OBJ_ARRSET_NUM (this, "words.%d.content.space", wordIndex,
- // word->content.space);
-
+ word->origSpace = word->effSpace =
+ style->font->spaceWidth + style->wordSpacing;
removeSpaceImgRenderer (wordNo);
word->spaceStyle->unref ();
word->spaceStyle = style;
style->ref ();
-
+
setSpaceImgRenderer (wordNo);
}
+
+ DBG_OBJ_LEAVE ();
}
/**
@@ -2102,19 +2560,24 @@ void Textblock::setBreakOption (Word *word, core::style::Style *style,
int breakPenalty1, int breakPenalty2,
bool forceBreak)
{
+ DBG_OBJ_ENTER ("construct.word", 0, "setBreakOption", "..., %d, %d, %s",
+ breakPenalty1, breakPenalty2, forceBreak ? "true" : "false");
+
// TODO: lineMustBeBroken should be independent of the penalty
// index? Otherwise, examine the last line.
if (!word->badnessAndPenalty.lineMustBeBroken(0)) {
- if (forceBreak || isBreakAllowed (word))
+ if (forceBreak || isBreakAllowed (style))
word->badnessAndPenalty.setPenalties (breakPenalty1, breakPenalty2);
else
word->badnessAndPenalty.setPenalty (PENALTY_PROHIBIT_BREAK);
}
+
+ DBG_OBJ_LEAVE ();
}
-bool Textblock::isBreakAllowed (Word *word)
+bool Textblock::isBreakAllowed (core::style::Style *style)
{
- switch (word->style->whiteSpace) {
+ switch (style->whiteSpace) {
case core::style::WHITE_SPACE_NORMAL:
case core::style::WHITE_SPACE_PRE_LINE:
case core::style::WHITE_SPACE_PRE_WRAP:
@@ -2123,11 +2586,12 @@ bool Textblock::isBreakAllowed (Word *word)
case core::style::WHITE_SPACE_PRE:
case core::style::WHITE_SPACE_NOWRAP:
return false;
+
+ default:
+ // compiler happiness
+ lout::misc::assertNotReached ();
+ return false;
}
-
- // compiler happiness
- lout::misc::assertNotReached ();
- return false;
}
@@ -2136,11 +2600,17 @@ bool Textblock::isBreakAllowed (Word *word)
*/
void Textblock::addParbreak (int space, core::style::Style *style)
{
+ DBG_OBJ_ENTER ("construct.word", 0, "addParbreak", "%d, %p",
+ space, style);
+ DBG_OBJ_MSG ("construct.word", 0,
+ "<i>No nesting! Strack trace may be incomplete.</i>");
+ DBG_OBJ_LEAVE ();
+
Word *word;
/* 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 +2619,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 +2644,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 +2653,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
}
/* Otherwise continue to examine parents. */
}
+
/* Return in any case. */
return;
}
@@ -2195,7 +2667,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
misc::max (word->content.breakSpace, space);
lastLine->breakSpace =
misc::max (word->content.breakSpace,
- lastLine->marginDescent - lastLine->boxDescent,
+ lastLine->marginDescent - lastLine->borderDescent,
lastLine->breakSpace);
return;
}
@@ -2205,6 +2677,10 @@ 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_SET_WORD (words->size () - 1);
+
+ breakAdded ();
processWord (words->size () - 1);
}
@@ -2213,6 +2689,8 @@ void Textblock::addParbreak (int space, core::style::Style *style)
*/
void Textblock::addLinebreak (core::style::Style *style)
{
+ DBG_OBJ_ENTER ("construct.word", 0, "addLinebreak", "%p", style);
+
Word *word;
if (words->size () == 0 ||
@@ -2230,9 +2708,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_SET_WORD (words->size () - 1);
+
+ breakAdded ();
processWord (words->size () - 1);
+
+ DBG_OBJ_LEAVE ();
}
+/**
+ * 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 +2752,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 +2766,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,12 +2785,14 @@ 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);
- if (childAtPoint) {
- return childAtPoint;
+ if (word->content.widget->wasAllocated ()) {
+ childAtPoint = word->content.widget->getWidgetAtPoint (x, y,
+ level + 1);
+ if (childAtPoint) {
+ return childAtPoint;
+ }
}
}
}
@@ -2288,7 +2812,8 @@ void Textblock::handOverBreak (core::style::Style *style)
Line *lastLine = lines->getRef (lines->size () - 1);
if (lastLine->breakSpace != 0 && (parent = getParent()) &&
- parent->instanceOf (Textblock::CLASS_ID)) {
+ parent->instanceOf (Textblock::CLASS_ID) &&
+ parent->getStyle()->display != core::style::DISPLAY_BLOCK) {
Textblock *textblock2 = (Textblock*) parent;
textblock2->addParbreak(lastLine->breakSpace, style);
}
@@ -2303,13 +2828,16 @@ void Textblock::handOverBreak (core::style::Style *style)
*/
void Textblock::flush ()
{
- PRINTF ("[%p] FLUSH => %s (parentRef = %d)\n",
- this, mustQueueResize ? "true" : "false", parentRef);
+ DBG_OBJ_ENTER0 ("resize", 0, "flush");
if (mustQueueResize) {
+ DBG_OBJ_MSG ("resize", 0, "mustQueueResize set");
+
queueResize (-1, true);
mustQueueResize = false;
}
+
+ DBG_OBJ_LEAVE ();
}
@@ -2344,7 +2872,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,
@@ -2362,7 +2890,7 @@ void Textblock::changeLinkColor (int link, int newColor)
}
if (changed)
queueDrawArea (0, lineYOffsetWidget(line), allocation.width,
- line->boxAscent + line->boxDescent);
+ line->borderAscent + line->borderDescent);
}
}
@@ -2387,13 +2915,295 @@ void Textblock::queueDrawRange (int index1, int index2)
if (line1idx >= 0 && line2idx >= 0) {
Line *line1 = lines->getRef (line1idx),
*line2 = lines->getRef (line2idx);
- int y = lineYOffsetWidget (line1) + line1->boxAscent -
+ int y = lineYOffsetWidget (line1) + line1->borderAscent -
line1->contentAscent;
- int h = lineYOffsetWidget (line2) + line2->boxAscent +
+ int h = lineYOffsetWidget (line2) + line2->borderAscent +
line2->contentDescent - y;
queueDrawArea (0, y, allocation.width, h);
}
}
+void Textblock::setVerticalOffset (int verticalOffset)
+{
+ DBG_OBJ_ENTER ("resize", 0, "setVerticalOffset", "%d", verticalOffset);
+
+ if (this->verticalOffset != verticalOffset) {
+ this->verticalOffset = verticalOffset;
+ DBG_OBJ_SET_NUM ("verticalOffset", verticalOffset);
+ mustQueueResize = true;
+ queueDraw (); // Could perhaps be optimized.
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+/**
+ * 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_ENTER ("resize", 0, "borderChanged", "%d, %p", y, vloat);
+
+ 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;
+
+ int realWrapLineIndex = wrapLineIndex;
+ // The following two variables are only used for debugging:
+ int minWrapLineIndex = wrapLineIndex, maxWrapLineIndex = wrapLineIndex;
+
+ if (vloat->getGenerator() == this && lines->size () > 0) {
+ 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 = realWrapLineIndex + i / 2;
+ if (i > 0)
+ exceeds = exceedsEnd = lineIndex2 >= lines->size ();
+ else
+ exceeds = exceedsEnd = false;
+ } else {
+ // odd: -1, -2, ...
+ lineIndex2 = realWrapLineIndex - (i + 1) / 2;
+ exceeds = exceedsBeginning = lineIndex2 < 0;
+ }
+
+ DBG_OBJ_MSGF ("resize", 2,
+ "lineIndex2 = %d (of %d), exceeds = %s, "
+ "exceedsBeginning = %s, exceedsEnd = %s",
+ lineIndex2, lines->size (),
+ exceeds ? "true" : "false",
+ exceedsBeginning ? "true" : "false",
+ exceedsEnd ? "true" : "false");
+
+ 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):
+ realWrapLineIndex =
+ misc::min (realWrapLineIndex, lineIndex2);
+ }
+ }
+
+ minWrapLineIndex = misc::min (minWrapLineIndex, lineIndex2);
+ maxWrapLineIndex = misc::max (maxWrapLineIndex, lineIndex2);
+ }
+ }
+
+ assert (found);
+ }
+
+ DBG_OBJ_MSGF ("resize", 1,
+ "wrapLineIndex: corrected from %d to %d (%d lines total); "
+ "searched between %d and %d; this is the GB: %s",
+ wrapLineIndex, realWrapLineIndex, lines->size (),
+ minWrapLineIndex, maxWrapLineIndex,
+ vloat->getGenerator() == this ? "yes" : "no");
+
+ queueResize (OutOfFlowMgr::createRefNormalFlow (realWrapLineIndex), true);
+
+ // Notice that the line no. realWrapLineIndex may not exist yet.
+ if (realWrapLineIndex == 0)
+ lastWordDrawn = misc::min (lastWordDrawn, -1);
+ else
+ lastWordDrawn =
+ misc::min (lastWordDrawn,
+ lines->getRef(realWrapLineIndex - 1)->lastWord);
+ DBG_OBJ_SET_NUM ("lastWordDrawn", lastWordDrawn);
+
+ // TODO Is the following necessary? Or even useless?
+ //redrawY =
+ // misc::min (redrawY,
+ // lineYOffsetWidget (lines->getRef (realWrapLineIndex)));
+ //DBG_OBJ_SET_NUM ("redrawY", redrawY);
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+void Textblock::clearPositionChanged ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "clearPositionChanged");
+ // Not very efficient (actually, a rewrapping could be easily
+ // avoided), but this case should not occur very often.
+ queueResize (0, false);
+ DBG_OBJ_LEAVE ();
+}
+
+void Textblock::oofSizeChanged (bool extremesChanged)
+{
+ DBG_OBJ_ENTER ("resize", 0, "oofSizeChanged", "%s",
+ extremesChanged ? "true" : "false");
+ queueResize (-1, extremesChanged);
+
+ // See Textblock::getAvailWidthForChild(): Extremes changes may become also
+ // relevant for the children, under certain conditions:
+ if (extremesChanged && !mustBeWidenedToAvailWidth ())
+ containerSizeChanged ();
+
+ DBG_OBJ_LEAVE ();
+}
+
+Textblock *Textblock::getTextblockForLine (Line *line)
+{
+ return getTextblockForLine (line->firstWord, line->lastWord);
+}
+
+Textblock *Textblock::getTextblockForLine (int lineNo)
+{
+ // Can also be used for a line not yet existing.
+ 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)
+{
+ DBG_OBJ_ENTER ("resize", 0, "getTextblockForLine", "%d, %d",
+ firstWord, lastWord);
+ DBG_OBJ_MSGF ("resize", 1, "words.size = %d", words->size ());
+
+ Textblock *textblock = NULL;
+
+ if (firstWord < words->size ()) {
+ // A textblock is always between two line breaks, and so the
+ // first word of the line.
+ Word *word = words->getRef (firstWord);
+
+ DBG_MSG_WORD ("resize", 1, "<i>first word:</i> ", firstWord, "");
+
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
+ Widget *widget = word->content.widget;
+ if (widget->instanceOf (Textblock::CLASS_ID) &&
+ // Exclude some cases where a textblock constitutes a new
+ // container (see definition of float container in
+ // Textblock::isContainingBlock).
+ widget->getStyle()->display != core::style::DISPLAY_INLINE_BLOCK &&
+ widget->getStyle()->overflow == core::style::OVERFLOW_VISIBLE)
+ textblock = (Textblock*)widget;
+
+ // (TODO: It would look nicer if there is one common place
+ // for such definitions. Will be fixed in "dillo_grows", not
+ // here.)
+ }
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %p", textblock);
+ DBG_OBJ_LEAVE ();
+ return textblock;
+}
+
+/**
+ * Includes margin, border, and padding.
+ */
+int Textblock::yOffsetOfLineToBeCreated ()
+{
+ // This method does not return an exact result: the position of the
+ // new line, which does not yet exist, cannot be calculated, since
+ // the top margin of the new line (which collapses either with the
+ // top margin of the textblock widget, or the bottom margin of the
+ // last line) must be taken into account. However, this method is
+ // only called for positioning floats; here, a slight incorrectness
+ // does not cause real harm.
+
+ // (Similar applies to the line *height*, which calculated in an
+ // iterative way; see wrapWordInFlow. Using the same approach for
+ // the *position* is possible, but not worth the increased
+ // complexity.)
+
+ DBG_OBJ_ENTER0 ("line.yoffset", 0, "yOffsetOfLineToBeCreated");
+
+ int result;
+
+ if (lines->size () == 0) {
+ result = verticalOffset + calcVerticalBorder (getStyle()->padding.top,
+ getStyle()->borderWidth.top,
+ getStyle()->margin.top,
+ 0, 0);
+ DBG_OBJ_MSGF ("line.yoffset", 1, "first line: ... = %d", result);
+ } else {
+ Line *firstLine = lines->getRef (0), *lastLine = lines->getLastRef ();
+ result = verticalOffset + calcVerticalBorder (getStyle()->padding.top,
+ getStyle()->borderWidth.top,
+ getStyle()->margin.top,
+ firstLine->borderAscent,
+ firstLine->marginAscent)
+ - firstLine->borderAscent + lastLine->top + lastLine->totalHeight (0);
+ DBG_OBJ_MSGF ("line.yoffset", 1, "other line: ... = %d", result);
+ }
+
+ DBG_OBJ_LEAVE ();
+
+ return result;
+}
+
} // namespace dw
diff --git a/dw/textblock.hh b/dw/textblock.hh
index b85937ba..4bc74669 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>
*
@@ -32,6 +38,11 @@ namespace dw {
*
* <h3>Collapsing Spaces</h3>
*
+ * <div style="border: 2px solid #ffff00; margin-top: 0.5em;
+ * margin-bottom: 0.5em; padding: 0.5em 1em; background-color:
+ * #ffffe0"><b>Info:</b> Collapsing spaces are deprecated, in favor of
+ * collapsing margins (see below).</div>
+ *
* The idea behind this is that every paragraph has a specific vertical
* space around and that they are combined to one space, according to
* rules stated below. A paragraph consists either of the lines between
@@ -47,9 +58,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,19 +84,20 @@ 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>
*
*
* <h3>Collapsing Margins</h3>
*
* Collapsing margins, as defined in the CSS2 specification, are,
- * supported in addition to collapsing spaces. Also, spaces and margins
- * collapse themselves. I.e., the space between two paragraphs is the
- * maximum of the space calculated as described in "Collapsing Spaces"
- * and the space calculated according to the rules for collapsing margins.
+ * supported in addition to collapsing spaces. Also, spaces and
+ * margins collapse themselves. I.&nbsp;e., the space between two
+ * paragraphs is the maximum of the space calculated as described in
+ * "Collapsing Spaces" and the space calculated according to the rules
+ * for collapsing margins.
*
* (This is an intermediate hybrid state, collapsing spaces are used in
* the current version of dillo, while I implemented collapsing margins
@@ -93,6 +105,44 @@ namespace dw {
* a pure CSS-based dillo, collapsing spaces will not be needed anymore, and
* may be removed for simplicity.)
*
+ * Currently implemented cases:
+ *
+ * - The top margin of of the textblock widget and the top margin of
+ * the first line box (based on widgets in the first line) collapse.
+ *
+ * - The bottom margin of of the textblock widget and the bottom
+ * margin of the last line box collapse.
+ *
+ * - The bottom margin of a line box and the top margin of the
+ * following line collapse. Here, the break space is regarded, too.
+ *
+ * Open issues:
+ *
+ * - Only the value of Style::margin is regarded, not the result of
+ * the collapsing itself. For the widgets A, B (child of A), and C
+ * (child of B), the effective margin of A is the maximum of the
+ * *style* margins of A and B, while the effective margin of B (the
+ * collapsed margin of B and C) is ignored here. This could be
+ * solved by introducing an additional "effective" ("calculated",
+ * "collapsed") margin as an attribute of Widget.
+ *
+ * - For similar reasons, backgrounds to not work exactly. Usage of
+ * Widget::extraSpace should fix this, but it is only fully working
+ * in the GROWS branch (<http://flpsed.org/hgweb/dillo_grows>).
+ *
+ * - Do margins of inline blocks and tables collapse? Check CSS
+ * spec. (They do currently; if not, ignoring them is simple.)
+ *
+ * - Lines which only contain a BREAK should be skipped for collapsing
+ * margins, or at least all three should collapse: the previous
+ * margin, the break, and the following margin. (Compare this with
+ * the CSS spec.)
+ *
+ * - Related to this: adding breaks should be revised.
+ * Textblock::addLinebreak and Textblock::addParbreak work quite
+ * differently, and Textblock::addParbreak seems much to complex for
+ * our needs, even when spaces of lines are kept.
+ *
*
* <h3>Some Internals</h3>
*
@@ -124,7 +174,7 @@ namespace dw {
* widget:
*
* <ul>
- * <li> The available size of the widget has changed, e.g., because the
+ * <li> The line break size of the widget has changed, e.g., because the
* user has changed the size of the browser window. In this case,
* it is necessary to rewrap all the lines.
*
@@ -157,7 +207,7 @@ private:
* badness is not well defined, so fiddling with the penalties is a
* bit difficult.
*/
-
+
enum {
PENALTY_FORCE_BREAK = INT_MIN,
PENALTY_PROHIBIT_BREAK = INT_MAX
@@ -170,7 +220,7 @@ private:
badnessState;
int ratio; // ratio is only defined when badness is defined
int badness, penalty[2];
-
+
// For debugging: define DEBUG for more informations in print().
#ifdef DEBUG
int totalWidth, idealWidth, totalStretchability, totalShrinkability;
@@ -204,13 +254,16 @@ private:
void setSinglePenalty (int index, int penalty);
int badnessValue (int infLevel);
int penaltyValue (int index, int infLevel);
-
+
public:
void calcBadness (int totalWidth, int idealWidth,
int totalStretchability, int totalShrinkability);
inline void setPenalty (int penalty) { setPenalties (penalty, penalty); }
void setPenalties (int penalty1, int penalty2);
+ // Rather for debugging:
+ inline int getPenalty (int i) { return penalty[i]; }
+
bool lineLoose ();
bool lineTight ();
bool lineTooTight ();
@@ -218,6 +271,7 @@ private:
bool lineCanBeBroken (int penaltyIndex);
int compareTo (int penaltyIndex, BadnessAndPenalty *other);
+ void intoStringBuffer(lout::misc::StringBuffer *sb);
void print ();
};
@@ -236,6 +290,9 @@ private:
static const char *hyphenDrawChar;
+ Textblock *containingBlock;
+ OutOfFlowMgr *outOfFlowMgr;
+
protected:
/**
* \brief Implementation used for words.
@@ -251,7 +308,7 @@ protected:
public:
WordImgRenderer (Textblock *textblock, int wordNo);
~WordImgRenderer ();
-
+
void setData (int xWordWidget, int lineNo);
bool readyToDraw ();
@@ -293,14 +350,18 @@ protected:
int parMin; /* The sum of all word minima (plus spaces,
hyphen width etc.) since the last possible
break within this paragraph. */
+ int parMinIntrinsic;
int parMax; /* The sum of all word maxima in this
- * paragraph (plus spaces, hyphen width
- * etc.). */
+ paragraph (plus spaces, hyphen width
+ etc.). */
+ int parMaxIntrinsic;
int maxParMin; /* Maximum of all paragraph minima (value of
- * "parMin), including this paragraph. */
+ "parMin"), including this paragraph. */
+ int maxParMinIntrinsic;
int maxParMax; /* Maximum of all paragraph maxima (value of
- * "parMax"), including this paragraph. */
+ "parMax""), including this paragraph. */
+ int maxParMaxIntrinsic;
};
struct Line
@@ -308,14 +369,40 @@ 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;
- /* This is similar to descent, but includes the bottom margins of the
- * widgets within this line. */
- int marginDescent;
+ int top; /* "top" is always relative to the top
+ of the first line, i.e.
+ page->lines[0].top is always 0. */
+ int marginAscent; /* Maximum of all total ascents
+ (including margin: hence the name)
+ of the words in this line. */
+ int marginDescent; /* Maximum of all total decents
+ (including margin: hence the name)
+ of the words in this line. */
+ int borderAscent; /* Maximum of all ascents minus margin
+ (but including padding and border:
+ hence the name) of the words in
+ this line. */
+ int borderDescent; /* Maximum of all descents minus margin
+ (but including padding and border:
+ hence the name) of the words in
+ this line. */
+ int contentAscent; /* ??? (depricated?) */
+ int contentDescent; /* ??? (depricated?) */
+ int breakSpace; /* Space between this line and the next one. */
+ int textOffset; /* ??? (to be documented) */
+
+ /**
+ * \brief Returns the difference between two vertical lines
+ * positions: height of this line plus space below this
+ * line. The margin of the next line (marginAscent -
+ * borderAscent) must be passed seperately.
+ */
+ inline int totalHeight (int marginNextLine)
+ { return borderAscent + borderDescent
+ // Collapsing of the margins of adjacent lines is done here:
+ + lout::misc::max (marginDescent - borderDescent, marginNextLine,
+ breakSpace); }
/* Maximum of all line widths, including this line. Does not
* include the last space, but the last hyphen width. Please
@@ -323,6 +410,18 @@ protected:
* changed line breaking), the values were accumulated up to the
* last line, not this line.*/
int maxLineWidth;
+
+ /* 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;
+
+ int leftOffset, rightOffset;
+ enum { LEFT, RIGHT, CENTER } alignment;
};
struct Word
@@ -409,13 +508,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,14 +525,18 @@ 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;
+ // See sizeAllocateImpl for details. It is also used elsewhere.
+ core::Allocation childBaseAllocation;
+
/* These fields provide some ad-hoc-functionality, used by sub-classes. */
bool hasListitemValue; /* If true, the first word of the page is treated
specially (search in source). */
- int innerPadding; /* This is an additional padding on the left side
+ int leftInnerPadding; /* This is an additional padding on the left side
(used by ListItem). */
int line1Offset; /* This is an additional offset of the first line.
May be negative (shift to left) or positive
@@ -449,7 +553,7 @@ protected:
* (which is used by DwTable!), and
* (ii) line1_offset is ignored (line1_offset_eff is set to 0),
* when line1_offset plus the width of the first word is
- * greater than the the available witdh.
+ * greater than the the line break witdh.
*
* \todo Eliminate all these ad-hoc features by a new, simpler and
* more elegant design. ;-)
@@ -476,10 +580,35 @@ protected:
int redrawY;
int lastWordDrawn;
- /* These values are set by set_... */
- int availWidth, availAscent, availDescent;
+ /* This value is (currently) set by setAscent(). */
+ int lineBreakWidth;
+
+ // 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 */
+ int newLineLeftFloatHeight, newLineRightFloatHeight;
- int wrapRefLines, wrapRefParagraphs; /* [0 based] */
+ // 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;
@@ -487,21 +616,26 @@ protected:
lout::misc::NotSoSimpleVector <Word> *words;
lout::misc::SimpleVector <Anchor> *anchors;
- struct {int index, nChar;}
+ struct { int index, nChar; }
hlStart[core::HIGHLIGHT_NUM_LAYERS], hlEnd[core::HIGHLIGHT_NUM_LAYERS];
int hoverLink; /* The link under the mouse pointer */
-
void queueDrawRange (int index1, int index2);
+ int calcVerticalBorder (int widgetPadding, int widgetBorder,
+ int widgetMargin, int lineBorderTotal,
+ int lineMarginTotal);
void getWordExtremes (Word *word, core::Extremes *extremes);
void justifyLine (Line *line, int diff);
- Line *addLine (int firstWord, int lastWord, bool temporary);
- void calcWidgetSize (core::Widget *widget, core::Requisition *size);
+ Line *addLine (int firstWord, int lastWord, int newLastOofPos,
+ bool temporary, int minHeight);
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 +654,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);
@@ -536,52 +675,47 @@ protected:
void fillSpace (int wordNo, core::style::Style *style);
void setBreakOption (Word *word, core::style::Style *style,
int breakPenalty1, int breakPenalty2, bool forceBreak);
- bool isBreakAllowed (Word *word);
+ bool isBreakAllowedInWord (Word *word)
+ { return isBreakAllowed (word->style); }
+ bool isBreakAllowed (core::style::Style *style);
int textWidth (const char *text, int start, int len,
core::style::Style *style, bool isStart, bool isEnd);
void calcTextSize (const char *text, size_t len, core::style::Style *style,
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,
- core::Allocation *allocation)
+ inline int _lineYOffsetWidgetAllocation (Line *line,
+ core::Allocation *allocation)
{
- return line->top + (allocation->ascent - lines->getRef(0)->boxAscent);
+ return line->top + (allocation->ascent - lines->getRef(0)->borderAscent);
}
inline int lineYOffsetWidget (Line *line)
{
- return lineYOffsetWidgetAllocation (line, &allocation);
+ return _lineYOffsetWidgetAllocation (line, &childBaseAllocation);
}
/**
- * Like lineYOffsetCanvas, but with the allocation as parameter.
+ * Like lineYOffsetCanvas, but with the allocation as parameter. Rarely used
+ * outside of lineYOffsetCanvas.
*/
- inline int lineYOffsetCanvasAllocation (Line *line,
- core::Allocation *allocation)
+ inline int _lineYOffsetCanvasAllocation (Line *line,
+ core::Allocation *allocation)
{
- return allocation->y + lineYOffsetWidgetAllocation(line, allocation);
+ return allocation->y + _lineYOffsetWidgetAllocation (line, allocation);
}
/**
@@ -589,7 +723,7 @@ protected:
*/
inline int lineYOffsetCanvas (Line *line)
{
- return lineYOffsetCanvasAllocation(line, &allocation);
+ return _lineYOffsetCanvasAllocation (line, &childBaseAllocation);
}
inline int lineYOffsetWidgetI (int lineIndex)
@@ -597,57 +731,98 @@ 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));
}
-
+
inline int calcPenaltyIndexForNewLine ()
{
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 yOffsetOfLineToBeCreated ();
+
bool sendSelectionEvent (core::SelectionState::EventType eventType,
core::MousePositionEvent *event);
- void accumulateWordExtremes (int firstWord, int lastWord,
- int *maxOfMinWidth, int *sumOfMaxWidth);
void processWord (int wordIndex);
- virtual bool wordWrap (int wordIndex, bool wrapAll);
+ virtual int wordWrap (int wordIndex, bool wrapAll);
+ int wrapWordInFlow (int wordIndex, bool wrapAll);
+ void balanceBreakPosAndHeight (int wordIndex, int firstIndex,
+ int *searchUntil, bool tempNewLine,
+ int penaltyIndex, bool borderIsCalculated,
+ bool *thereWillBeMoreSpace, bool wrapAll,
+ int *diffWords, 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,
+ int *diffWords, int *wordIndexEnd,
+ int *addIndex1 = NULL);
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 ();
static int getSpaceShrinkability(struct Word *word);
static int getSpaceStretchability(struct Word *word);
- static int getLineShrinkability(Word *lastWord);
- static int getLineStretchability(Word *lastWord);
- int hyphenateWord (int wordIndex);
+ int getLineShrinkability(int lastWordIndex);
+ int getLineStretchability(int lastWordIndex);
+ int hyphenateWord (int wordIndex, int *addIndex1 = NULL);
+ void moveWordIndices (int wordIndex, int num, int *addIndex1 = NULL);
void accumulateWordForLine (int lineIndex, int wordIndex);
void accumulateWordData (int wordIndex);
- int calcAvailWidth (int lineIndex);
+ int calcLineBreakWidth (int lineIndex);
void initLine1Offset (int wordIndex);
void alignLine (int lineIndex);
+ void calcTextOffset (int lineIndex, int totalWidth);
void sizeRequestImpl (core::Requisition *requisition);
void getExtremesImpl (core::Extremes *extremes);
void sizeAllocateImpl (core::Allocation *allocation);
+ int getAvailWidthOfChild (Widget *child, bool forceValue);
+ void containerSizeChangedForChildren ();
+ bool affectsSizeChangeContainerChild (Widget *child);
+ bool usesAvailWidth ();
void resizeDrawImpl ();
void markSizeChange (int ref);
void markExtremesChange (int ref);
- void setWidth (int width);
- void setAscent (int ascent);
- void setDescent (int descent);
+
+ void notifySetAsTopLevel();
+ void notifySetParent();
+
+ bool isBlockLevel ();
+
void draw (core::View *view, core::Rectangle *area);
bool buttonPressImpl (core::EventButton *event);
@@ -664,6 +839,20 @@ protected:
core::style::Style *style,
int numBreaks, int *breakPos,
core::Requisition *wordSize);
+ static bool isContainingBlock (Widget *widget);
+
+ inline bool mustBeWidenedToAvailWidth () {
+ DBG_OBJ_ENTER0 ("resize", 0, "mustBeWidenedToAvailWidth");
+ bool toplevel = getParent () == NULL,
+ block = getStyle()->display == core::style::DISPLAY_BLOCK,
+ vloat = getStyle()->vloat != core::style::FLOAT_NONE,
+ result = toplevel || (block && !vloat);
+ DBG_OBJ_MSGF ("resize", 0, "=> %s (toplevel: %s, block: %s, float: %s)",
+ result ? "true" : "false", toplevel ? "true" : "false",
+ block ? "true" : "false", vloat ? "true" : "false");
+ DBG_OBJ_LEAVE ();
+ return result;
+ }
public:
static int CLASS_ID;
@@ -684,9 +873,9 @@ public:
void addText (const char *text, size_t len, core::style::Style *style);
inline void addText (const char *text, core::style::Style *style)
- {
- addText (text, strlen(text), style);
- }
+ { addText (text, strlen(text), style); }
+ static bool isStyleOutOfFlow (core::style::Style *style)
+ { return OutOfFlowMgr::isStyleOutOfFlow (style); }
void addWidget (core::Widget *widget, core::style::Style *style);
bool addAnchor (const char *name, core::style::Style *style);
void addSpace (core::style::Style *style);
@@ -699,8 +888,84 @@ 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);
+ void clearPositionChanged ();
+ void oofSizeChanged (bool extremesChanged);
+ inline int getLineBreakWidth () { return lineBreakWidth; }
};
+#define DBG_SET_WORD_PENALTY(n, i, is) \
+ D_STMT_START { \
+ if (words->getRef(n)->badnessAndPenalty.getPenalty (i) == INT_MIN) \
+ DBG_OBJ_ARRATTRSET_SYM ("words", n, "penalty." is, "-inf"); \
+ else if (words->getRef(n)->badnessAndPenalty.getPenalty (i) == INT_MAX) \
+ DBG_OBJ_ARRATTRSET_SYM ("words", n, "penalty." is, "inf"); \
+ else \
+ DBG_OBJ_ARRATTRSET_NUM ("words", n, "penalty." is, \
+ words->getRef(n)->badnessAndPenalty \
+ .getPenalty (i)); \
+ } D_STMT_END
+
+#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", "BREAK"); \
+ 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", "???"); \
+ } \
+ DBG_SET_WORD_PENALTY (n, 0, "0"); \
+ DBG_SET_WORD_PENALTY (n, 1, "1"); \
+ } D_STMT_END
+
+#define DBG_MSG_WORD(aspect, prio, prefix, n, suffix) \
+ D_STMT_START { \
+ if ((n) < 0 || (n) >= words->size ()) \
+ DBG_OBJ_MSG (aspect, prio, prefix "undefined (wrong index)" suffix); \
+ else { \
+ 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 "??? / ???" suffix); \
+ } \
+ } \
+ } D_STMT_END
+
} // namespace dw
#endif // __DW_TEXTBLOCK_HH__
diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc
index 7531ecd5..b6423a29 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,225 @@ 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 (!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 > 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->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 (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);
+ if (oofm) {
+ // TODO Consider start and end?
+ *allocation =
+ *(textblock->outOfFlowMgr->getWidget(index)->getAllocation());
+ } else {
+ Word *word = textblock->words->getRef (index);
+ int firstWordOfLine, textOffset, lineYOffsetCanvas, lineBorderAscent;
+
+ int lineIndex = textblock->findLineOfWord (index);
+
+ // It may be that the line does not exist yet.
+ if (lineIndex != -1) {
+ // Line exists: simple.
+ Line *line = textblock->lines->getRef (lineIndex);
+ firstWordOfLine = line->firstWord;
+ textOffset = line->textOffset;
+ lineYOffsetCanvas = textblock->lineYOffsetCanvas (line);
+ lineBorderAscent = line->borderAscent;
+ } else {
+ // Line does not exist. Calculate the values in a similar way as in
+ // Textblock::addLine().
+ Line *prevLine = textblock->lines->size () > 0 ?
+ textblock->lines->getLastRef () : NULL;
+ firstWordOfLine = prevLine ? prevLine->lastWord + 1 : 0;
+
+ // The variable textOffset, defined below, is what Line::leftOffset
+ // will be for the next line; Line::textOffset itself cannot be
+ // calculated before the line is complete.
+ bool regardBorder =
+ textblock->mustBorderBeRegarded (textblock->lines->size ());
+ textOffset =
+ misc::max (regardBorder ? textblock->newLineLeftBorder : 0,
+ textblock->boxOffsetX () + textblock->leftInnerPadding
+ + (textblock->lines->size () == 0 ?
+ textblock->line1OffsetEff : 0));
+
+ lineYOffsetCanvas = textblock->yOffsetOfLineToBeCreated ();
+
+ lineBorderAscent = 0;
+ for (int i = firstWordOfLine; i < textblock->words->size (); i++) {
+ Word *w = textblock->words->getRef (i);
+ int borderAscent =
+ w->content.type == core::Content::WIDGET_IN_FLOW ?
+ w->size.ascent - w->content.widget->getStyle()->margin.top :
+ w->size.ascent;
+ lineBorderAscent = misc::max (lineBorderAscent, borderAscent);
+ }
+ }
- 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->x = textblock->allocation.x + textOffset;
+ for (int i = firstWordOfLine; 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 = lineYOffsetCanvas + lineBorderAscent - 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..38d325bc 100644
--- a/dw/textblock_linebreaking.cc
+++ b/dw/textblock_linebreaking.cc
@@ -1,7 +1,7 @@
/*
* Dillo Widget
*
- * Copyright 2005-2007, 2012-2013 Sebastian Geerken <sgeerken@dillo.org>
+ * Copyright 2005-2007, 2012-2014 Sebastian Geerken <sgeerken@dillo.org>
*
* (Parts of this file were originally part of textblock.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>
@@ -91,7 +92,7 @@ void Textblock::BadnessAndPenalty::calcBadness (int totalWidth, int idealWidth,
badness = ratio * ratio * ratio;
}
}
- } else { // if (totalWidth > availWidth)
+ } else { // if (totalWidth > idealWidth)
if (totalShrinkability == 0)
badnessState = TOO_TIGHT;
else {
@@ -186,67 +187,69 @@ int Textblock::BadnessAndPenalty::compareTo (int penaltyIndex,
if (thisValue != otherValue)
return thisValue - otherValue;
}
-
+
return 0;
}
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,10 @@ 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_ENTER ("construct.line", 0, "justifyLine", "..., %d", diff);
+
+ // To avoid rounding errors, the calculation is based on accumulated
+ // values. See doc/rounding-errors.doc.
if (diff > 0) {
int spaceStretchabilitySum = 0;
@@ -306,8 +311,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,89 +333,156 @@ 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_LEAVE ();
}
Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
- bool temporary)
+ int newLastOofPos, bool temporary,
+ int minHeight)
{
- PRINTF ("[%p] ADD_LINE (%d, %d) => %d\n",
- this, firstWord, lastWord, lines->size ());
+ DBG_OBJ_ENTER ("construct.line", 0, "addLine", "%d, %d, %d, %s, %d",
+ firstWord, lastWord, newLastOofPos,
+ temporary ? "true" : "false", minHeight);
+ DBG_OBJ_MSGF ("construct.line", 0, "=> %d", lines->size ());
+
+ 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;
-
- 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);
+ DBG_OBJ_MSGF ("construct.line", 1, "lineWidth (line1OffsetEff): %d",
+ lineWidth);
+ }
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.
- if (lines->size () == 1 || nonTemporaryLines == lines->size () -1)
+ if (lines->size () == 1 || nonTemporaryLines == lines->size () - 1)
nonTemporaryLines = lines->size ();
}
- PRINTF ("nonTemporaryLines = %d\n", nonTemporaryLines);
+ DBG_OBJ_MSGF ("construct.line", 1, "nonTemporaryLines = %d",
+ nonTemporaryLines);
int lineIndex = lines->size () - 1;
Line *line = lines->getRef (lineIndex);
line->firstWord = firstWord;
line->lastWord = lastWord;
- line->boxAscent = line->contentAscent = 0;
- line->boxDescent = line->contentDescent = 0;
+
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "firstWord", line->firstWord);
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "lastWord", line->lastWord);
+
+ line->borderAscent = line->contentAscent = 0;
+ line->borderDescent = line->contentDescent = 0;
+ line->marginAscent = 0;
line->marginDescent = 0;
line->breakSpace = 0;
- line->leftOffset = 0;
+
+ bool regardBorder = mustBorderBeRegarded (line);
+ line->leftOffset = misc::max (regardBorder ? newLineLeftBorder : 0,
+ boxOffsetX () + leftInnerPadding
+ + (lineIndex == 0 ? line1OffsetEff : 0));
+ line->rightOffset = misc::max (regardBorder ? newLineRightBorder : 0,
+ boxRestWidth ());
+
+ DBG_OBJ_MSGF ("construct.line", 1,
+ "regardBorder = %s, newLineLeftBorder = %d, "
+ "newLineRightBorder = %d",
+ regardBorder ? "true" : "false", newLineLeftBorder,
+ newLineRightBorder);
+
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "leftOffset", line->leftOffset);
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "rightOffset",
+ line->rightOffset);
alignLine (lineIndex);
+ calcTextOffset (lineIndex, lineBreakWidth);
+
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;
}
-
+
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "maxLineWidth",
+ line->maxLineWidth);
+
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);
+ if (lines->size () == 1)
+ line->top = 0;
+ else {
+ // See comment in Line::totalHeight for collapsing of the
+ // margins of adjacent lines.
+ Line *prevLine = lines->getRef (lines->size () - 2);
+ line->top = prevLine->top
+ + prevLine->totalHeight (line->marginAscent - line->borderAscent);
+ }
- PRINTF (" line[%d].maxLineWidth = %d\n",
- lines->size () - 1, line->maxLineWidth);
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "top", line->top);
+
+ // Especially empty lines (possible when there are floats) have
+ // zero height, which may cause endless loops. For this reasons,
+ // the height should be positive (assuming the caller passed
+ // minHeight > 0).
+ line->borderAscent = misc::max (line->borderAscent, minHeight);
+ line->marginAscent = misc::max (line->marginAscent, minHeight);
+
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "borderAscent",
+ line->borderAscent);
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "borderDescent",
+ line->borderDescent);
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "marginAscent",
+ line->marginAscent);
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "marginDescent",
+ line->marginDescent);
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "contentAscent",
+ line->contentAscent);
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "contentDescent",
+ line->contentDescent);
mustQueueResize = true;
@@ -421,7 +493,7 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
//words->getRef(line->lastWord)->badnessAndPenalty.print ();
//printf ("\n");
- int xWidget = lineXOffsetWidget(line);
+ int xWidget = line->textOffset;
for (int i = firstWord; i <= lastWord; i++) {
Word *word = words->getRef (i);
if (word->wordImgRenderer)
@@ -430,71 +502,67 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
word->spaceImgRenderer->setData (xWidget, lines->size () - 1);
xWidget += word->size.width + word->effSpace;
}
-
- return line;
-}
-
-void Textblock::accumulateWordExtremes (int firstWord, int lastWord,
- int *maxOfMinWidth, int *sumOfMaxWidth)
-{
- int parMin = 0;
- *maxOfMinWidth = *sumOfMaxWidth = 0;
-
- for (int i = firstWord; i <= lastWord; i++) {
- Word *word = words->getRef (i);
- bool atLastWord = i == lastWord;
-
- core::Extremes extremes;
- getWordExtremes (word, &extremes);
-
- // Minimum: between two *possible* breaks (or at the end).
- // TODO This is redundant to getExtremesImpl().
- // TODO: Again, index 1 is used for lineCanBeBroken(). See getExtremes().
- if (word->badnessAndPenalty.lineCanBeBroken (1) || atLastWord) {
- parMin += extremes.minWidth + word->hyphenWidth;
- *maxOfMinWidth = misc::max (*maxOfMinWidth, parMin);
- parMin = 0;
- } else
- // Shrinkability could be considered, but really does not play a
- // role.
- parMin += extremes.minWidth + word->origSpace;
- //printf ("[%p] after word: ", this);
- //printWord (word);
- //printf ("\n");
+ line->lastOofRefPositionedBeforeThisLine =
+ misc::max (line->lastOofRefPositionedBeforeThisLine, newLastOofPos);
+ DBG_OBJ_SET_NUM ("lastLine.lastOofRefPositionedBeforeThisLine",
+ line->lastOofRefPositionedBeforeThisLine);
- //printf ("[%p] (%d / %d) => parMin = %d, maxOfMinWidth = %d\n",
- // this, extremes.minWidth, extremes.maxWidth, parMin,
- // *maxOfMinWidth);
+ initNewLine ();
- *sumOfMaxWidth += (extremes.maxWidth + word->origSpace);
- // Notice that the last space is added. See also: Line::parMax.
- }
+ DBG_OBJ_LEAVE ();
+ return line;
}
void Textblock::processWord (int wordIndex)
{
- bool wordListChanged = wordWrap (wordIndex, false);
+ DBG_OBJ_ENTER ("construct.all", 0, "processWord", "%d", wordIndex);
+ DBG_MSG_WORD ("construct.all", 1, "<i>processed word:</i>", wordIndex, "");
- if (wordListChanged) {
+ int diffWords = wordWrap (wordIndex, false);
+
+ if (diffWords == 0)
+ handleWordExtremes (wordIndex);
+ else {
// If wordWrap has called hyphenateWord here, this has an effect
// on the call of handleWordExtremes. To avoid adding values
// more than one time (original un-hyphenated word, plus all
// parts of the hyphenated word, except the first one), the
// whole paragraph is recalculated again.
+ //
+ // (Note: the hyphenated word is often *before* wordIndex, and
+ // it may be even more than one word, which makes it nearly
+ // impossible to reconstruct what has happend. Therefore, there
+ // is no simpler approach to handle this.)
+
+ DBG_OBJ_MSGF ("construct.paragraph", 1,
+ "word list has become longer by %d", diffWords);
+ DBG_MSG_WORD ("construct.all", 1, "<i>processed word now:</i>",
+ wordIndex, "");
int firstWord;
if (paragraphs->size() > 0) {
firstWord = paragraphs->getLastRef()->firstWord;
paragraphs->setSize (paragraphs->size() - 1);
+ DBG_OBJ_MSG ("construct.paragraph", 1, "removing last paragraph");
} else
firstWord = 0;
- for (int i = firstWord; i <= wordIndex - 1; i++)
+ int lastIndex = wordIndex + diffWords;
+ DBG_OBJ_MSGF ("construct.paragraph", 1,
+ "processing words again from %d to %d",
+ firstWord, lastIndex);
+
+ // Furthermore, some more words have to be processed, so we
+ // iterate until wordIndex + diffWords, not only
+ // wordIndex.
+ DBG_OBJ_MSG_START ();
+ for (int i = firstWord; i <= lastIndex; i++)
handleWordExtremes (i);
+ DBG_OBJ_MSG_END ();
}
- handleWordExtremes (wordIndex);
+ DBG_OBJ_LEAVE ();
}
/*
@@ -505,65 +573,136 @@ void Textblock::processWord (int wordIndex)
* Returns whether the words list has changed at, or before, the word
* index.
*/
-bool Textblock::wordWrap (int wordIndex, bool wrapAll)
+int Textblock::wordWrap (int wordIndex, bool wrapAll)
{
- PRINTF ("[%p] WORD_WRAP (%d, %s)\n",
- this, wordIndex, wrapAll ? "true" : "false");
-
- Word *word;
- bool wordListChanged = false;
+ DBG_OBJ_ENTER ("construct.word", 0, "wordWrap", "%d, %s",
+ wordIndex, wrapAll ? "true" : "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);
- //printf (" ");
- //printWord (word);
- //printf ("\n");
+ int n;
+ if (word->content.type == core::Content::WIDGET_OOF_REF)
+ n = 0;
+ else
+ n = wrapWordInFlow (wordIndex, wrapAll);
+
+ DBG_OBJ_MSGF ("construct.word", 1, "=> %d", n);
+ DBG_OBJ_LEAVE ();
+
+ return n;
+}
+
+int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll)
+{
+ DBG_OBJ_ENTER ("construct.word", 0, "wrapWordInFlow", "%d, %s",
+ wordIndex, wrapAll ? "true" : "false");
+
+ Word *word = words->getRef (wordIndex);
+ int diffWords = 0;
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 line break width. 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;
+ }
+ }
- if (possibleLineBreak && word->badnessAndPenalty.lineTooTight ()) {
+ DBG_OBJ_MSG_END ();
+ DBG_OBJ_MSGF ("construct.word", 1, "=> %s",
+ possibleLineBreak ? "true" : "false");
+
+ DBG_OBJ_MSGF ("construct.word", 1, "word->... too tight: %s",
+ word->badnessAndPenalty.lineTooTight () ?
+ "true" : "false");
+
+ if ((thereWillBeMoreSpace || possibleLineBreak)
+ && word->badnessAndPenalty.lineTooTight ()) {
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 +714,117 @@ 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;
+ int lastFloatPos = lines->size() > 0 ?
+ lines->getLastRef()->lastOofRefPositionedBeforeThisLine : -1;
+ DBG_OBJ_MSGF ("construct.word", 2, "lastFloatPos = %d", lastFloatPos);
+
+ balanceBreakPosAndHeight (wordIndex, firstIndex, &searchUntil,
+ tempNewLine, penaltyIndex, true,
+ &thereWillBeMoreSpace, wrapAll,
+ &diffWords, &wordIndexEnd,
+ &lastFloatPos, regardBorder, &height,
+ &breakPos);
+
+ bool floatHandled;
+ int yNewLine = yOffsetOfLineToBeCreated ();
- 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);
+
+ 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;
}
-
- PRINTF ("[%p] accumulating again from %d to %d\n",
- this, breakPos + 1, wordIndexEnd);
- for(int i = breakPos + 1; i <= wordIndexEnd; i++)
- accumulateWordData (i);
- } while(!lineAdded);
+ DBG_OBJ_MSGF ("construct.word", 2, "newFloatPos = %d", newFloatPos);
+
+ if (newFloatPos == -1)
+ floatHandled = false;
+ else {
+ floatHandled = true;
+
+ // 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,
+ &diffWords, &wordIndexEnd,
+ &lastFloatPos, regardBorder, &height,
+ &breakPos);
+ }
+
+ DBG_OBJ_MSG_END ();
+ } while (floatHandled);
+
+ int minHeight;
+ if (firstIndex <= breakPos)
+ // Not an empty line: calculate line height from contents.
+ minHeight = 1;
+ else {
+ // Empty line. Too avoid too many lines one pixel high, we
+ // use the float heights.
+ if (newLineHasFloatLeft && newLineHasFloatRight)
+ minHeight = misc::max (misc::min (newLineLeftFloatHeight,
+ newLineRightFloatHeight),
+ 1);
+ else if (newLineHasFloatLeft && !newLineHasFloatRight)
+ minHeight = misc::max (newLineLeftFloatHeight, 1);
+ else if (!newLineHasFloatLeft && newLineHasFloatRight)
+ minHeight = misc::max (newLineRightFloatHeight, 1);
+ else
+ // May this happen?
+ minHeight = 1;
+ }
+
+ addLine (firstIndex, breakPos, lastFloatPos, tempNewLine, minHeight);
+
+ 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
@@ -648,43 +839,259 @@ bool Textblock::wordWrap (int wordIndex, bool wrapAll)
firstWordWithoutLine = 0;
else
firstWordWithoutLine = lines->getLastRef()->lastWord + 1;
-
+
if (wordIndex >= firstWordWithoutLine) {
- word->content.widget->parentRef = lines->size ();
- PRINTF ("The %s %p is assigned parentRef = %d.\n",
- word->content.widget->getClassName(), word->content.widget,
- word->content.widget->parentRef);
+ word->content.widget->parentRef =
+ OutOfFlowMgr::createRefNormalFlow (lines->size ());
+ DBG_OBJ_SET_NUM_O (word->content.widget, "parentRef",
+ word->content.widget->parentRef);
+ }
+ }
+
+ DBG_OBJ_LEAVE ();
+
+ return diffWords;
+}
+
+// *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, int *diffWords,
+ int *wordIndexEnd, int *lastFloatPos,
+ bool regardBorder, int *height,
+ int *breakPos)
+{
+ DBG_OBJ_ENTER ("construct.word", 0, "balanceBreakPosAndHeight",
+ "%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);
+
+ // 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, diffWords, wordIndexEnd, lastFloatPos);
+ 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++;
}
- return wordListChanged;
+ DBG_OBJ_LEAVE ();
+}
+
+// *wordIndexEnd must be initialized (initially to wordIndex)
+int Textblock::searchBreakPos (int wordIndex, int firstIndex, int *searchUntil,
+ bool tempNewLine, int penaltyIndex,
+ bool thereWillBeMoreSpace, bool wrapAll,
+ int *diffWords, int *wordIndexEnd,
+ int *addIndex1)
+{
+ DBG_OBJ_ENTER ("construct.word", 0, "searchBreakPos",
+ "%d, %d. %d, %s, %d, %s, %s, ...",
+ wordIndex, firstIndex, *searchUntil,
+ tempNewLine ? "true" : "false", penaltyIndex,
+ thereWillBeMoreSpace ? "true" : "false",
+ wrapAll ? "true" : "false");
+ DBG_MSG_WORD ("construct.word", 0, "<i>first word:</i> ", firstIndex, "");
+
+ 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 ()) {
+ int hyphenatedWord = considerHyphenation (firstIndex, firstIndex);
+ DBG_OBJ_MSGF ("construct.word", 1, "too tight ... hyphenatedWord = %d",
+ hyphenatedWord);
+
+ if (hyphenatedWord == -1) {
+ DBG_OBJ_MSG ("construct.word", 1, "... => empty line");
+ result = firstIndex - 1;
+ lineAdded = true;
+ } else {
+ DBG_OBJ_MSG ("construct.word", 1,
+ "... => hyphenate word and try again");
+ int n = hyphenateWord (hyphenatedWord, addIndex1);
+ *searchUntil += n;
+ if (hyphenatedWord <= wordIndex)
+ *wordIndexEnd += n;
+ DBG_OBJ_MSGF ("construct.word", 1, "new searchUntil = %d",
+ *searchUntil);
+
+ lineAdded = false;
+ }
+ } 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, addIndex1);
+ *searchUntil += n;
+ if (hyphenatedWord <= wordIndex)
+ *wordIndexEnd += n;
+ DBG_OBJ_MSGF ("construct.word", 1, "new searchUntil = %d",
+ *searchUntil);
+ lineAdded = false;
+
+ if (hyphenatedWord <= wordIndex)
+ *diffWords += n;
+
+ 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_LEAVE ();
+
+ 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_ENTER ("construct.word", 0, "searchMinBap", "%d, %d, %d, %s, %s",
+ firstWord, lastWord, penaltyIndex,
+ thereWillBeMoreSpace ? "true" : "false",
+ correctAtEnd ? "true" : "false");
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
@@ -692,28 +1099,31 @@ int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex,
// the last word. However, since more words may follow, the
// penalty is not changed, but here, the search is corrected
// (maybe only temporary).
-
+
// (Notice that it was once (temporally) set to -inf, not 0, but
// this will make e.g. test/table-1.html not work.)
Word *w = words->getRef (lastWord);
BadnessAndPenalty correctedBap = w->badnessAndPenalty;
correctedBap.setPenalty (0);
- //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_LEAVE ();
return pos;
}
-
/**
* Suggest a word to hyphenate, when breaking at breakPos is
* planned. Return a word index or -1, when hyphenation makes no
@@ -733,7 +1143,7 @@ int Textblock::considerHyphenation (int firstIndex, int breakPos)
if (wordBreak->badnessAndPenalty.lineTight ()) {
// Sometimes, it is not the last word, which must be hyphenated,
// but some word before. Here, we search for the first word
- // which can be hyphenated, *and* makes the line too tight.
+ // which can be hyphenated, *and* makes the line too tight.
for (int i = breakPos; i >= firstIndex; i--) {
Word *word1 = words->getRef (i);
if (word1->badnessAndPenalty.lineTight () &&
@@ -758,12 +1168,25 @@ bool Textblock::isHyphenationCandidate (Word *word)
{
return (word->flags & Word::CAN_BE_HYPHENATED) &&
word->style->x_lang[0] &&
- isBreakAllowed(word) &&
+ isBreakAllowedInWord (word) &&
word->content.type == core::Content::TEXT &&
Hyphenator::isHyphenationCandidate (word->content.text);
}
+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,17 +1194,26 @@ void Textblock::handleWordExtremes (int wordIndex)
{
// TODO Overall, clarify penalty index.
+ DBG_OBJ_ENTER ("construct.paragraph", 0, "handleWordExtremes", "%d",
+ wordIndex);
+
+ initLine1Offset (wordIndex);
+
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) / %d (%d)",
+ wordExtremes.minWidth, wordExtremes.minWidthIntrinsic,
+ wordExtremes.maxWidth, wordExtremes.maxWidthIntrinsic);
if (wordIndex == 0) {
- wordExtremes.minWidth += line1Offset;
- wordExtremes.maxWidth += line1Offset;
+ wordExtremes.minWidth += line1OffsetEff;
+ wordExtremes.minWidthIntrinsic += line1OffsetEff;
+ wordExtremes.maxWidth += line1OffsetEff;
+ wordExtremes.maxWidthIntrinsic += line1OffsetEff;
}
if (paragraphs->size() == 0 ||
@@ -794,18 +1226,24 @@ void Textblock::handleWordExtremes (int wordIndex)
Paragraph *par = paragraphs->getLastRef();
par->firstWord = par->lastWord = wordIndex;
- par->parMin = par->parMax = 0;
+ par->parMin = par->parMinIntrinsic = par->parMax = par->parMaxIntrinsic
+ = 0;
if (prevPar) {
par->maxParMin = prevPar->maxParMin;
+ par->maxParMinIntrinsic = prevPar->maxParMinIntrinsic;
par->maxParMax = prevPar->maxParMax;
+ par->maxParMaxIntrinsic = prevPar->maxParMaxIntrinsic;
} else
- par->maxParMin = par->maxParMax = 0;
+ par->maxParMin = par->maxParMinIntrinsic = par->maxParMax =
+ par->maxParMaxIntrinsic = 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;
@@ -816,31 +1254,54 @@ void Textblock::handleWordExtremes (int wordIndex)
corrDiffMin = 0;
else
corrDiffMin = lastWord->origSpace - lastWord->hyphenWidth;
-
+
corrDiffMax = lastWord->origSpace - lastWord->hyphenWidth;
} 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);
+
+ DBG_OBJ_MSGF ("construct.paragraph", 1,
+ "before: parMin = %d (%d) (max = %d (%d)), "
+ "parMax = %d (%d) (max = %d (%d))",
+ lastPar->parMin, lastPar->parMinIntrinsic,
+ lastPar->maxParMin, lastPar->maxParMinIntrinsic,
+ lastPar->parMax, lastPar->parMaxIntrinsic,
+ lastPar->maxParMax, lastPar->maxParMaxIntrinsic);
// Minimum: between two *possible* breaks.
// Shrinkability could be considered, but really does not play a role.
lastPar->parMin += wordExtremes.minWidth + word->hyphenWidth + corrDiffMin;
+ lastPar->parMinIntrinsic +=
+ wordExtremes.minWidthIntrinsic + word->hyphenWidth + corrDiffMin;
lastPar->maxParMin = misc::max (lastPar->maxParMin, lastPar->parMin);
+ lastPar->maxParMinIntrinsic =
+ misc::max (lastPar->maxParMinIntrinsic, lastPar->parMinIntrinsic);
if (word->badnessAndPenalty.lineCanBeBroken (1) &&
(word->flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) == 0)
- lastPar->parMin = 0;
+ lastPar->parMin = lastPar->parMinIntrinsic = 0;
// Maximum: between two *necessary* breaks.
lastPar->parMax += wordExtremes.maxWidth + word->hyphenWidth + corrDiffMax;
+ lastPar->parMaxIntrinsic +=
+ wordExtremes.maxWidthIntrinsic + word->hyphenWidth + corrDiffMax;
lastPar->maxParMax = misc::max (lastPar->maxParMax, lastPar->parMax);
+ lastPar->maxParMaxIntrinsic =
+ misc::max (lastPar->maxParMaxIntrinsic, lastPar->parMaxIntrinsic);
- PRINTF (" => parMin = %d (max = %d), parMax = %d (max = %d)\n",
- lastPar->parMin, lastPar->maxParMin, lastPar->parMax,
- lastPar->maxParMax);
+ DBG_OBJ_MSGF ("construct.paragraph", 1,
+ "after: parMin = %d (%d) (max = %d (%d)), "
+ "parMax = %d (%d) (max = %d (%d))",
+ lastPar->parMin, lastPar->parMinIntrinsic,
+ lastPar->maxParMin, lastPar->maxParMinIntrinsic,
+ lastPar->parMax, lastPar->parMaxIntrinsic,
+ lastPar->maxParMax, lastPar->maxParMaxIntrinsic);
lastPar->lastWord = wordIndex;
+ DBG_OBJ_LEAVE ();
}
/**
@@ -852,7 +1313,8 @@ void Textblock::correctLastWordExtremes ()
Word *word = words->getLastRef ();
if (word->badnessAndPenalty.lineCanBeBroken (1) &&
(word->flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) == 0) {
- paragraphs->getLastRef()->parMin = 0;
+ paragraphs->getLastRef()->parMin =
+ paragraphs->getLastRef()->parMinIntrinsic = 0;
PRINTF (" => corrected; parMin = %d\n",
paragraphs->getLastRef()->parMin);
}
@@ -860,10 +1322,10 @@ void Textblock::correctLastWordExtremes ()
}
-int Textblock::hyphenateWord (int wordIndex)
+int Textblock::hyphenateWord (int wordIndex, int *addIndex1)
{
Word *hyphenatedWord = words->getRef(wordIndex);
- char lang[3] = { hyphenatedWord->style->x_lang[0],
+ char lang[3] = { hyphenatedWord->style->x_lang[0],
hyphenatedWord->style->x_lang[1], 0 };
Hyphenator *hyphenator = Hyphenator::getHyphenator (lang);
PRINTF ("[%p] considering to hyphenate word %d, '%s', in language '%s'\n",
@@ -879,20 +1341,28 @@ int Textblock::hyphenateWord (int wordIndex)
core::Requisition wordSize[numBreaks + 1];
calcTextSizes (origWord.content.text, strlen (origWord.content.text),
origWord.style, numBreaks, breakPos, wordSize);
-
+
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, addIndex1);
+
// Adjust anchor indexes.
for (int i = 0; i < anchors->size (); i++) {
Anchor *anchor = anchors->getRef (i);
if (anchor->wordIndex > wordIndex)
anchor->wordIndex += numBreaks;
}
-
+
for (int i = 0; i < numBreaks + 1; i++) {
Word *w = words->getRef (wordIndex + i);
fillWord (wordIndex + i, wordSize[i].width, wordSize[i].ascent,
@@ -907,17 +1377,16 @@ int Textblock::hyphenateWord (int wordIndex)
w->content.text =
layout->textZone->strndup (origWord.content.text + start,
end - start);
- PRINTF (" [%d] -> '%s'\n", wordIndex + i, w->content.text);
// Note: there are numBreaks + 1 word parts.
if (i == 0)
w->flags |= Word::WORD_START;
- else
+ else
w->flags &= ~Word::WORD_START;
if (i == numBreaks)
w->flags |= Word::WORD_END;
- else
+ else
w->flags &= ~Word::WORD_END;
if (i < numBreaks) {
@@ -930,26 +1399,25 @@ int Textblock::hyphenateWord (int wordIndex)
strlen (hyphenDrawChar));
w->flags |= (Word::DRAW_AS_ONE_TEXT | Word::DIV_CHAR_AT_EOL |
Word::UNBREAKABLE_FOR_MIN_WIDTH);
-
- PRINTF (" [%d] + hyphen\n", wordIndex + i);
} else {
- if (origWord.content.space) {
+ if (origWord.content.space)
fillSpace (wordIndex + i, origWord.spaceStyle);
- PRINTF (" [%d] + space\n", wordIndex + i);
- } else {
- PRINTF (" [%d] + nothing\n", wordIndex + i);
- }
}
- accumulateWordData (wordIndex + i);
-
- //printf ("[%p] %d: hyphenated word part: ", this, wordIndex + i);
- //printWordWithFlags (w);
- //printf ("\n");
+ DBG_SET_WORD (wordIndex + i);
}
+ // AccumulateWordData() will calculate the width, which depends
+ // on the borders (possibly limited by floats), which depends on
+ // the widgeds so far. For this reason, it is important to first
+ // make all words consistent before calling
+ // accumulateWordData(); therefore the second loop.
+
+ for (int i = 0; i < numBreaks + 1; i++)
+ accumulateWordData (wordIndex + i);
+
PRINTF (" finished\n");
-
+
//delete origword->content.text; TODO: Via textZone?
origWord.style->unref ();
origWord.spaceStyle->unref ();
@@ -961,66 +1429,116 @@ int Textblock::hyphenateWord (int wordIndex)
return numBreaks;
}
+void Textblock::moveWordIndices (int wordIndex, int num, int *addIndex1)
+{
+ DBG_OBJ_ENTER ("construct.word", 0, "moveWordIndices", "%d, %d",
+ wordIndex, 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.
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "lines[%d]->lastOofRef = %d < %d => stop",
+ i, line->lastOofRefPositionedBeforeThisLine, wordIndex);
+ break;
+ } else {
+ DBG_OBJ_MSGF ("construct.word", 1,
+ "adding %d to lines[%d]->lastOofRef...: %d -> %d",
+ num, i, line->lastOofRefPositionedBeforeThisLine,
+ line->lastOofRefPositionedBeforeThisLine + num);
+ line->lastOofRefPositionedBeforeThisLine += num;
+ }
+ }
+
+ // Unlike the last line, the last paragraph is already constructed. (To
+ // make sure we cover all cases, we iterate over the last paragraphs.)
+ Paragraph *par;
+ for (int parNo = paragraphs->size () - 1;
+ parNo >= 0 &&
+ (par = paragraphs->getRef(parNo)) && par->lastWord > wordIndex;
+ parNo--) {
+ par->lastWord += num;
+ if (par->firstWord > wordIndex)
+ par->firstWord += num;
+ }
+
+ // Addiditional indices. When needed, the number can be extended.
+ if (addIndex1 && *addIndex1 >= wordIndex)
+ *addIndex1 += num;
+
+ DBG_OBJ_LEAVE ();
+}
+
void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
{
+ DBG_OBJ_ENTER ("construct.line", 1, "accumulateWordForLine", "%d, %d",
+ lineIndex, wordIndex);
+ DBG_MSG_WORD ("construct.line", 2, "<i>word:</i> ", wordIndex, "");
+
Line *line = lines->getRef (lineIndex);
Word *word = words->getRef (wordIndex);
- PRINTF (" %d + %d / %d + %d\n", line->boxAscent, line->boxDescent,
- word->size.ascent, word->size.descent);
-
- line->boxAscent = misc::max (line->boxAscent, word->size.ascent);
- line->boxDescent = misc::max (line->boxDescent, word->size.descent);
-
int len = word->style->font->ascent;
if (word->style->valign == core::style::VALIGN_SUPER)
len += len / 2;
line->contentAscent = misc::max (line->contentAscent, len);
-
+
len = word->style->font->descent;
if (word->style->valign == core::style::VALIGN_SUB)
len += word->style->font->ascent / 3;
line->contentDescent = misc::max (line->contentDescent, len);
- if (word->content.type == core::Content::WIDGET) {
- int collapseMarginTop = 0;
-
- line->marginDescent =
- misc::max (line->marginDescent,
- word->size.descent +
- word->content.widget->getStyle()->margin.bottom);
-
- if (lines->size () == 1 &&
- word->content.widget->blockLevel () &&
- getStyle ()->borderWidth.top == 0 &&
- getStyle ()->padding.top == 0) {
- // collapse top margins of parent element and its first child
- // see: http://www.w3.org/TR/CSS21/box.html#collapsing-margins
- collapseMarginTop = getStyle ()->margin.top;
- }
+ int borderAscent, borderDescent, marginAscent, marginDescent;
+
+ DBG_OBJ_MSGF ("construct.line", 2, "size.ascent = %d, size.descent = %d",
+ word->size.ascent, word->size.descent);
- line->boxAscent =
- misc::max (line->boxAscent,
- word->size.ascent,
- word->size.ascent
- + word->content.widget->getStyle()->margin.top
- - collapseMarginTop);
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
+ // TODO Consider extraSpace?
+ marginAscent = word->size.ascent;
+ marginDescent = word->size.descent;
+ borderAscent =
+ marginAscent - word->content.widget->getStyle()->margin.top;
+ borderDescent =
+ marginDescent - word->content.widget->getStyle()->margin.bottom;
- word->content.widget->parentRef = lineIndex;
+ word->content.widget->parentRef =
+ OutOfFlowMgr::createRefNormalFlow (lineIndex);
+ DBG_OBJ_SET_NUM_O (word->content.widget, "parentRef",
+ word->content.widget->parentRef);
} else {
- line->marginDescent =
- misc::max (line->marginDescent, line->boxDescent);
+ borderAscent = marginAscent = word->size.ascent;
+ borderDescent = marginDescent = word->size.descent;
if (word->content.type == core::Content::BREAK)
line->breakSpace =
- misc::max (word->content.breakSpace,
- line->marginDescent - line->boxDescent,
- line->breakSpace);
+ misc::max (word->content.breakSpace, line->breakSpace);
}
+
+ DBG_OBJ_MSGF ("construct.line", 2,
+ "borderAscent = %d, borderDescent = %d, marginAscent = %d, "
+ "marginDescent = %d",
+ borderAscent, borderDescent, marginAscent, marginDescent);
+
+ line->borderAscent = misc::max (line->borderAscent, borderAscent);
+ line->borderDescent = misc::max (line->borderDescent, borderDescent);
+ line->marginAscent = misc::max (line->marginAscent, marginAscent);
+ line->marginDescent = misc::max (line->marginDescent, marginDescent);
+
+ DBG_OBJ_LEAVE ();
}
void Textblock::accumulateWordData (int wordIndex)
{
+ DBG_OBJ_ENTER ("construct.word.accum", 1, "accumulateWordData", "%d",
+ wordIndex);
+ DBG_MSG_WORD ("construct.word.accum", 1, "<i>word:</i> ", wordIndex, "");
+
// 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 +1553,15 @@ 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.accum", 2, "lineIndex = %d", lineIndex);
- int availWidth = calcAvailWidth (lineIndex);
+ int lineBreakWidth = calcLineBreakWidth (lineIndex);
- PRINTF (" (%s existing line %d starts with word %d)\n",
- lineIndex < lines->size () ? "already" : "not yet",
- lineIndex, firstWordOfLine);
+ DBG_OBJ_MSGF ("construct.word.accum", 2,
+ "(%s existing line %d starts with word %d; "
+ "lineBreakWidth = %d)",
+ lineIndex < lines->size () ? "already" : "not yet",
+ lineIndex, firstWordOfLine, lineBreakWidth);
if (wordIndex == firstWordOfLine) {
// first word of the (not neccessarily yet existing) line
@@ -1051,49 +1570,94 @@ void Textblock::accumulateWordData (int wordIndex)
word->maxDescent = word->size.descent;
word->totalSpaceStretchability = 0;
word->totalSpaceShrinkability = 0;
+
+ DBG_OBJ_MSGF ("construct.word.accum", 1,
+ "first word of line: words[%d].totalWidth = %d + %d = %d; "
+ "maxAscent = %d, maxDescent = %d",
+ wordIndex, word->size.width, word->hyphenWidth,
+ word->totalWidth, word->maxAscent, word->maxDescent);
} else {
Word *prevWord = words->getRef (wordIndex - 1);
word->totalWidth = prevWord->totalWidth
+ prevWord->origSpace - prevWord->hyphenWidth
+ word->size.width + word->hyphenWidth;
- word->maxAscent = misc::max (prevWord->size.ascent, word->size.ascent);
- word->maxDescent = misc::max (prevWord->size.descent, word->size.descent);
+ word->maxAscent = misc::max (prevWord->maxAscent, word->size.ascent);
+ word->maxDescent = misc::max (prevWord->maxDescent, word->size.descent);
word->totalSpaceStretchability =
prevWord->totalSpaceStretchability + getSpaceStretchability(prevWord);
word->totalSpaceShrinkability =
prevWord->totalSpaceShrinkability + getSpaceShrinkability(prevWord);
+
+ DBG_OBJ_MSGF ("construct.word.accum", 1,
+ "not first word of line: words[%d].totalWidth = %d + %d - "
+ "%d + %d + %d = %d; maxAscent = max (%d, %d) = %d, "
+ "maxDescent = max (%d, %d) = %d",
+ wordIndex, prevWord->totalWidth, prevWord->origSpace,
+ prevWord->hyphenWidth, word->size.width,
+ word->hyphenWidth, word->totalWidth,
+ prevWord->maxAscent, word->size.ascent, word->maxAscent,
+ prevWord->maxDescent, word->size.descent, word->maxDescent);
}
int totalStretchability =
- word->totalSpaceStretchability + getLineStretchability (word);
+ word->totalSpaceStretchability + getLineStretchability (wordIndex);
int totalShrinkability =
- word->totalSpaceShrinkability + getLineShrinkability (word);
- word->badnessAndPenalty.calcBadness (word->totalWidth, availWidth,
+ word->totalSpaceShrinkability + getLineShrinkability (wordIndex);
+
+ DBG_OBJ_MSGF ("construct.word.accum", 1,
+ "totalStretchability = %d + ... = %d",
+ word->totalSpaceStretchability, totalStretchability);
+ DBG_OBJ_MSGF ("construct.word.accum", 1,
+ "totalShrinkability = %d + ... = %d",
+ word->totalSpaceShrinkability, totalShrinkability);
+
+ word->badnessAndPenalty.calcBadness (word->totalWidth, lineBreakWidth,
totalStretchability,
totalShrinkability);
- //printf (" => ");
- //printWord (word);
- //printf ("\n");
+ DBG_IF_RTFL {
+ misc::StringBuffer sb;
+ word->badnessAndPenalty.intoStringBuffer (&sb);
+ DBG_OBJ_MSGF ("construct.word.accum", 1, "b+p: %s", sb.getChars ());
+ }
+
+ DBG_OBJ_LEAVE ();
}
-int Textblock::calcAvailWidth (int lineIndex)
+int Textblock::calcLineBreakWidth (int lineIndex)
{
- int availWidth =
- this->availWidth - getStyle()->boxDiffWidth() - innerPadding;
+ DBG_OBJ_ENTER ("construct.word.width", 1, "calcLineBreakWidth",
+ "%d <i>of %d</i>", lineIndex, lines->size());
+
+ int lineBreakWidth = this->lineBreakWidth - leftInnerPadding;
if (limitTextWidth &&
layout->getUsesViewport () &&
- availWidth > layout->getWidthViewport () - 10)
- availWidth = layout->getWidthViewport () - 10;
+ // margin/border/padding will be subtracted later, via OOFM.
+ lineBreakWidth - getStyle()->boxDiffWidth()
+ > layout->getWidthViewport () - 10)
+ lineBreakWidth = layout->getWidthViewport () - 10;
if (lineIndex == 0)
- availWidth -= line1OffsetEff;
+ lineBreakWidth -= line1OffsetEff;
+
+ 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());
+
+ lineBreakWidth -= (leftBorder + rightBorder);
- //PRINTF("[%p] CALC_AVAIL_WIDTH => %d - %d - %d = %d\n",
- // this, this->availWidth, getStyle()->boxDiffWidth(), innerPadding,
- // availWidth);
+ DBG_OBJ_MSGF ("construct.word.width", 2, "=> %d - %d - (%d + %d) = %d\n",
+ this->lineBreakWidth, leftInnerPadding, leftBorder,
+ rightBorder, lineBreakWidth);
- return availWidth;
+ DBG_OBJ_LEAVE ();
+ return lineBreakWidth;
}
void Textblock::initLine1Offset (int wordIndex)
@@ -1103,18 +1667,18 @@ void Textblock::initLine1Offset (int wordIndex)
/* Test whether line1Offset can be used. */
if (wordIndex == 0) {
if (ignoreLine1OffsetSometimes &&
- line1Offset + word->size.width > availWidth) {
+ line1Offset + word->size.width > lineBreakWidth) {
line1OffsetEff = 0;
} else {
int indent = 0;
- if (word->content.type == core::Content::WIDGET &&
- word->content.widget->blockLevel() == true) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW &&
+ word->content.widget->isBlockLevel()) {
/* don't use text-indent when nesting blocks */
} else {
if (core::style::isPerLength(getStyle()->textIndent)) {
indent = core::style::multiplyWithPerLengthRounded
- (this->availWidth, getStyle()->textIndent);
+ (lineBreakWidth, getStyle()->textIndent);
} else {
indent = core::style::absLengthVal (getStyle()->textIndent);
}
@@ -1131,49 +1695,109 @@ void Textblock::initLine1Offset (int wordIndex)
*/
void Textblock::alignLine (int lineIndex)
{
+ DBG_OBJ_ENTER ("construct.line", 0, "alignLine", "%d", lineIndex);
+
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;
- }
- /* For large lines (images etc), which do not fit into the viewport: */
- if (line->leftOffset < 0)
- line->leftOffset = 0;
+ if (line->firstWord <= line->lastWord) {
+ Word *firstWord = words->getRef (line->firstWord);
+ Word *lastWord = words->getRef (line->lastWord);
+ int lineBreakWidth =
+ this->lineBreakWidth - (line->leftOffset + line->rightOffset);
+
+ for (int i = line->firstWord; i < line->lastWord; i++)
+ words->getRef(i)->effSpace = words->getRef(i)->origSpace;
+
+ 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->alignment = Line::LEFT;
+ 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->alignment = Line::LEFT;
+ break;
+ case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */
+ DBG_OBJ_MSG ("construct.line", 1,
+ "first word has 'text-align: justify'");
+ line->alignment = Line::LEFT;
+ // 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.)
+ lineBreakWidth < lastWord->totalWidth)
+ justifyLine (line, lineBreakWidth - lastWord->totalWidth);
+ break;
+ case core::style::TEXT_ALIGN_RIGHT:
+ DBG_OBJ_MSG ("construct.line", 1,
+ "first word has 'text-align: right'");
+ line->alignment = Line::RIGHT;
+ break;
+ case core::style::TEXT_ALIGN_CENTER:
+ DBG_OBJ_MSG ("construct.line", 1,
+ "first word has 'text-align: center'");
+ line->alignment = Line::CENTER;
+ break;
+ default:
+ // compiler happiness
+ line->alignment = Line::LEFT;
+ }
+
+ } else
+ // empty line (only line break);
+ line->alignment = Line::LEFT;
+ } else
+ // empty line
+ line->alignment = Line::LEFT;
+
+ DBG_OBJ_LEAVE ();
+}
+
+void Textblock::calcTextOffset (int lineIndex, int totalWidth)
+{
+ DBG_OBJ_ENTER ("construct.line", 0, "calcTextOffset", "%d, %d",
+ lineIndex, totalWidth);
+
+ Line *line = lines->getRef (lineIndex);
+ int lineWidth = line->firstWord <= line->lastWord ?
+ words->getRef(line->lastWord)->totalWidth : 0;
+
+ DBG_OBJ_MSGF ("construct.line", 1, "leftOffset = %d, lineWidth = %d",
+ line->leftOffset, lineWidth);
+
+ switch (line->alignment) {
+ case Line::LEFT:
+ line->textOffset = line->leftOffset;
+ break;
+
+ case Line::RIGHT:
+ line->textOffset = totalWidth - line->rightOffset - lineWidth;
+ break;
+
+ case Line::CENTER:
+ line->textOffset =
+ (line->leftOffset + totalWidth - line->rightOffset - lineWidth) / 2;
+ break;
+
+ default:
+ misc::assertNotReached ();
+ break;
}
+
+ // For large lines (images etc), which do not fit into the viewport:
+ if (line->textOffset < line->leftOffset)
+ line->textOffset = line->leftOffset;
+
+ DBG_OBJ_ARRATTRSET_NUM ("lines", lineIndex, "textOffset", line->textOffset);
+ DBG_OBJ_LEAVE ();
}
/**
@@ -1184,43 +1808,55 @@ void Textblock::alignLine (int lineIndex)
*/
void Textblock::rewrap ()
{
- PRINTF ("[%p] REWRAP: wrapRef = %d\n", this, wrapRef);
+ DBG_OBJ_ENTER0 ("construct.line", 0, "rewrap");
if (wrapRefLines == -1)
- /* page does not have to be rewrapped */
- return;
+ 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);
- /* 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);
+ initNewLine ();
- int firstWord;
- if (lines->size () > 0)
- firstWord = lines->getLastRef()->lastWord + 1;
- else
- firstWord = 0;
+ int firstWord;
+ if (lines->size () > 0) {
+ Line *lastLine = lines->getLastRef();
+ firstWord = lastLine->lastWord + 1;
+ } else
+ firstWord = 0;
- 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);
+ DBG_OBJ_MSGF ("construct.line", 0, "starting with word %d", firstWord);
- // 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);
+ lastWordDrawn = misc::min (lastWordDrawn, firstWord - 1);
+ DBG_OBJ_SET_NUM ("lastWordDrawn", lastWordDrawn);
+
+ for (int i = firstWord; i < words->size (); i++) {
+ Word *word = words->getRef (i);
+
+ if (word->content.type == core::Content::WIDGET_IN_FLOW)
+ word->content.widget->sizeRequest (&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);
+ }
+
+ // 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_LEAVE ();
}
/**
@@ -1228,62 +1864,255 @@ void Textblock::rewrap ()
*/
void Textblock::fillParagraphs ()
{
- if (wrapRefParagraphs == -1)
- return;
+ DBG_OBJ_ENTER0 ("resize", 0, "fillParagraphs");
+
+ DBG_OBJ_MSGF ("resize", 1, "wrapRefParagraphs = %d", wrapRefParagraphs);
+
+ if (wrapRefParagraphs != -1) {
+ // Notice that wrapRefParagraphs refers to the lines, not to the
+ // paragraphs.
+ int firstWordOfLine;
+ 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;
+ if (paragraphs->size() > 0 &&
+ firstWordOfLine > paragraphs->getLastRef()->firstWord)
+ // A special case: the paragraphs list has been partly built, but
+ // not yet the paragraph containing the word in question. In
+ // this case, only the rest of the paragraphs list must be
+ // constructed. (Without this check, findParagraphOfWord would
+ // return -1 in this case, so that all paragraphs would be
+ // rebuilt.)
+ parNo = paragraphs->size ();
+ else
+ // If there are no paragraphs yet, findParagraphOfWord will return
+ // -1: use 0 then instead.
+ parNo = misc::max (0, findParagraphOfWord (firstWordOfLine));
- // 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
- firstWordOfLine = 0;
+ paragraphs->setSize (parNo);
- int parNo;
- if (paragraphs->size() > 0 &&
- firstWordOfLine > paragraphs->getLastRef()->firstWord)
- // A special case: the paragraphs list has been partly built, but
- // not yet the paragraph containing the word in question. In
- // this case, only the rest of the paragraphs list must be
- // constructed. (Without this check, findParagraphOfWord would
- // return -1 in this case, so that all paragraphs would be
- // rebuilt.)
- parNo = paragraphs->size ();
- else
- // If there are no paragraphs yet, findParagraphOfWord will return
- // -1: use 0 then instead.
- parNo = misc::max (0, findParagraphOfWord (firstWordOfLine));
+ int firstWord;
+ if (paragraphs->size () > 0)
+ firstWord = paragraphs->getLastRef()->lastWord + 1;
+ else
+ firstWord = 0;
- paragraphs->setSize (parNo);
+ DBG_OBJ_MSGF ("resize", 1, "firstWord = %d, words->size() = %d [before]",
+ firstWord, words->size ());
- int firstWord;
- if (paragraphs->size () > 0)
- firstWord = paragraphs->getLastRef()->lastWord + 1;
- else
- firstWord = 0;
+ for (int i = firstWord; i < words->size (); i++)
+ handleWordExtremes (i);
- PRINTF ("[%p] FILL_PARAGRAPHS: now %d paragraphs; starting from word %d\n",
- this, parNo, firstWord);
+ DBG_OBJ_MSGF ("resize", 1, "words->size() = %d [after]", words->size ());
- for (int i = firstWord; i < words->size (); i++)
- handleWordExtremes (i);
+ wrapRefParagraphs = -1;
+ DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs);
+ }
- wrapRefParagraphs = -1;
+ DBG_OBJ_LEAVE ();
+}
+
+void Textblock::initNewLine ()
+{
+ DBG_OBJ_ENTER0 ("construct.line", 0, "initNewLine");
+
+ // At the very beginning, in Textblock::Textblock, where this
+ // method is called, containingBlock is not yet defined.
+
+ if (containingBlock && containingBlock->outOfFlowMgr) {
+ if (lines->size () == 0) {
+ int clearPosition =
+ containingBlock->outOfFlowMgr->getClearPosition (this);
+ setVerticalOffset (misc::max (clearPosition, 0));
+ }
+ }
+
+ 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_LEAVE ();
+}
+
+void Textblock::calcBorders (int lastOofRef, int height)
+{
+ DBG_OBJ_ENTER ("construct.line", 0, "calcBorders", "%d, %d",
+ lastOofRef, height);
+
+ 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 - 1);
+
+ int y = yOffsetOfLineToBeCreated ();
+
+ 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);
+ newLineLeftFloatHeight = newLineHasFloatLeft ?
+ containingBlock->outOfFlowMgr->getLeftFloatHeight (this, y, height,
+ this, effOofRef) :
+ 0;
+ newLineRightFloatHeight = newLineHasFloatRight ?
+ containingBlock->outOfFlowMgr->getRightFloatHeight (this, y, height,
+ this, effOofRef) :
+ 0;
+
+ DBG_OBJ_MSGF ("construct.line", 1,
+ "%d * %d (%s) / %d * %d (%s), at %d (%d), until %d = "
+ "max (%d, %d - 1)",
+ newLineLeftBorder, newLineLeftFloatHeight,
+ newLineHasFloatLeft ? "true" : "false",
+ newLineRightBorder, newLineRightFloatHeight,
+ newLineHasFloatRight ? "true" : "false",
+ y, height, effOofRef, lastOofRef, firstWordOfLine);
+ } else {
+ newLineHasFloatLeft = newLineHasFloatRight = false;
+ newLineLeftBorder = newLineRightBorder = 0;
+ newLineLeftFloatHeight = newLineRightFloatHeight = 0;
+
+ DBG_OBJ_MSG ("construct.line", 0, "<i>no CB of OOFM</i>");
+ }
+
+ DBG_OBJ_SET_BOOL ("newLineHasFloatLeft", newLineHasFloatLeft);
+ DBG_OBJ_SET_BOOL ("newLineHasFloatRight", newLineHasFloatRight);
+ DBG_OBJ_SET_NUM ("newLineLeftBorder", newLineLeftBorder);
+ DBG_OBJ_SET_NUM ("newLineRightBorder", newLineRightBorder);
+ DBG_OBJ_SET_NUM ("newLineLeftFloatHeight", newLineLeftFloatHeight);
+ DBG_OBJ_SET_NUM ("newLineRightFloatHeight", newLineRightFloatHeight);
+
+ DBG_OBJ_LEAVE ();
}
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_ENTER0 ("construct.line", 0, "showMissingLines");
+
+ // "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_LEAVE ();
}
void Textblock::removeTemporaryLines ()
{
- lines->setSize (nonTemporaryLines);
+ DBG_OBJ_ENTER0 ("construct.line", 0, "removeTemporaryLines");
+
+ if (nonTemporaryLines < lines->size ()) {
+ lines->setSize (nonTemporaryLines);
+ DBG_OBJ_SET_NUM ("lines.size", lines->size ());
+
+ // For words which will be added, the values calculated before in
+ // accumulateWordData() are wrong, so it is called again. (Actually, the
+ // words from the first temporary line are correct, but for simplicity,
+ // we re-calculate all.)
+ int firstWord =
+ lines->size () > 0 ? lines->getLastRef()->lastWord + 1 : 0;
+ for (int i = firstWord; i < words->size (); i++)
+ accumulateWordData (i);
+ }
+
+ DBG_OBJ_LEAVE ();
}
int Textblock::getSpaceShrinkability(struct Word *word)
@@ -1304,18 +2133,35 @@ int Textblock::getSpaceStretchability(struct Word *word)
// Alternative: return word->origSpace / 2;
}
-int Textblock::getLineShrinkability(Word *lastWord)
+int Textblock::getLineShrinkability(int lastWordIndex)
{
return 0;
}
-int Textblock::getLineStretchability(Word *lastWord)
+int Textblock::getLineStretchability(int lastWordIndex)
{
- if (lastWord->spaceStyle->textAlign == core::style::TEXT_ALIGN_JUSTIFY)
- return 0;
- else
- return stretchabilityFactor * (lastWord->maxAscent
- + lastWord->maxDescent) / 100;
+ DBG_OBJ_ENTER ("construct.word.accum", 0, "getLineStretchability", "%d",
+ lastWordIndex);
+ DBG_MSG_WORD ("construct.word.accum", 1, "<i>last word:</i> ",
+ lastWordIndex, "");
+
+ Word *lastWord = words->getRef (lastWordIndex);
+ int str;
+
+ if (lastWord->spaceStyle->textAlign == core::style::TEXT_ALIGN_JUSTIFY) {
+ str = 0;
+ DBG_OBJ_MSG ("construct.word.accum", 1, "justified => 0");
+ } else {
+ str = stretchabilityFactor * (lastWord->maxAscent
+ + lastWord->maxDescent) / 100;
+ DBG_OBJ_MSGF ("construct.word.accum", 1,
+ "not justified => %d * (%d + %d) / 100 = %d",
+ stretchabilityFactor, lastWord->maxAscent,
+ lastWord->maxDescent, str);
+ }
+
+ DBG_OBJ_LEAVE ();
+ return str;
// Alternative: return 0;
}
diff --git a/dw/types.cc b/dw/types.cc
index 86836bc1..56af66d1 100644
--- a/dw/types.cc
+++ b/dw/types.cc
@@ -268,5 +268,90 @@ void Region::addRectangle (Rectangle *rPointer)
rectangleList->append (r);
}
+Content::Type Content::maskForSelection (bool followReferences)
+{
+ Content::Type widgetMask = (Content::Type)
+ (Content::WIDGET_IN_FLOW |
+ (followReferences ? Content::WIDGET_OOF_REF : Content::WIDGET_OOF_CONT));
+ return (Content::Type)(Content::SELECTION_CONTENT | widgetMask);
+}
+
+void Content::intoStringBuffer(Content *content, misc::StringBuffer *sb)
+{
+ switch(content->type) {
+ case START:
+ sb->append ("<start>");
+ break;
+ case END:
+ sb->append ("<end>");
+ break;
+ case TEXT:
+ sb->append ("\"");
+ sb->append (content->text);
+ sb->append ("\"");
+ break;
+ case WIDGET_IN_FLOW:
+ sb->append ("<widget in flow: ");
+ sb->appendPointer (content->widget);
+ sb->append (" (");
+ sb->append (content->widget->getClassName());
+ sb->append (")>");
+ break;
+ case WIDGET_OOF_REF:
+ sb->append ("<widget oof ref: ");
+ sb->appendPointer (content->widget);
+ sb->append (" (");
+ sb->append (content->widget->getClassName());
+ sb->append (")>");
+ break;
+ case WIDGET_OOF_CONT:
+ sb->append ("<widget oof cont: ");
+ sb->appendPointer (content->widget);
+ sb->append (" (");
+ sb->append (content->widget->getClassName());
+ sb->append (")>");
+ break;
+ case BREAK:
+ sb->append ("<break>");
+ break;
+ default:
+ sb->append ("<");
+ sb->appendInt (content->type);
+ sb->append ("?>");
+ break;
+ }
+}
+
+void Content::maskIntoStringBuffer(Type mask, misc::StringBuffer *sb)
+{
+ sb->append ((mask & START) ? "st" : "--");
+ sb->append (":");
+ sb->append ((mask & END) ? "en" : "--");
+ sb->append (":");
+ sb->append ((mask & TEXT) ? "tx" : "--");
+ sb->append (":");
+ sb->append ((mask & WIDGET_IN_FLOW) ? "wf" : "--");
+ sb->append (":");
+ sb->append ((mask & WIDGET_OOF_REF) ? "Wr" : "--");
+ sb->append (":");
+ sb->append ((mask & WIDGET_OOF_CONT) ? "Wc" : "--");
+ sb->append (":");
+ sb->append ((mask & BREAK) ? "br" : "--");
+}
+
+void Content::print (Content *content)
+{
+ misc::StringBuffer sb;
+ intoStringBuffer (content, &sb);
+ printf ("%s", sb.getChars ());
+}
+
+void Content::printMask (Type mask)
+{
+ misc::StringBuffer sb;
+ maskIntoStringBuffer (mask, &sb);
+ printf ("%s", sb.getChars ());
+}
+
} // namespace core
} // namespace dw
diff --git a/dw/types.hh b/dw/types.hh
index f04fc138..36d6caa1 100644
--- a/dw/types.hh
+++ b/dw/types.hh
@@ -180,6 +180,8 @@ struct Extremes
{
int minWidth;
int maxWidth;
+ int minWidthIntrinsic;
+ int maxWidthIntrinsic;
};
struct Content
@@ -188,11 +190,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 +223,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..67e3fabc 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()
@@ -55,6 +56,7 @@ void Embed::sizeRequestImpl (Requisition *requisition)
void Embed::getExtremesImpl (Extremes *extremes)
{
resource->getExtremes (extremes);
+ correctExtremes (extremes);
}
void Embed::sizeAllocateImpl (Allocation *allocation)
@@ -62,6 +64,35 @@ void Embed::sizeAllocateImpl (Allocation *allocation)
resource->sizeAllocate (allocation);
}
+int Embed::getAvailWidthOfChild (Widget *child, bool forceValue)
+{
+ return resource->getAvailWidthOfChild (child, forceValue);
+}
+
+int Embed::getAvailHeightOfChild (Widget *child, bool forceValue)
+{
+ return resource->getAvailHeightOfChild (child, forceValue);
+}
+
+void Embed::correctRequisitionOfChild (Widget *child,
+ Requisition *requisition,
+ void (*splitHeightFun) (int, int*, int*))
+{
+ resource->correctRequisitionOfChild (child, requisition, splitHeightFun);
+}
+
+void Embed::correctExtremesOfChild (Widget *child, Extremes *extremes)
+{
+ resource->correctExtremesOfChild (child, extremes);
+}
+
+void Embed::containerSizeChangedForChildren ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
+ resource->containerSizeChangedForChildren ();
+ DBG_OBJ_LEAVE ();
+}
+
void Embed::enterNotifyImpl (core::EventCrossing *event)
{
resource->emitEnter();
@@ -87,21 +118,6 @@ bool Embed::buttonPressImpl (core::EventButton *event)
return handled;
}
-void Embed::setWidth (int width)
-{
- resource->setWidth (width);
-}
-
-void Embed::setAscent (int ascent)
-{
- resource->setAscent (ascent);
-}
-
-void Embed::setDescent (int descent)
-{
- resource->setDescent (descent);
-}
-
void Embed::setDisplayed (bool displayed)
{
resource->setDisplayed (displayed);
@@ -180,6 +196,7 @@ void Resource::ActivateEmitter::emitLeave (Resource *resource)
Resource::~Resource ()
{
+ DBG_OBJ_DELETE ();
}
void Resource::setEmbed (Embed *embed)
@@ -189,26 +206,56 @@ void Resource::setEmbed (Embed *embed)
void Resource::getExtremes (Extremes *extremes)
{
+ DBG_OBJ_ENTER0 ("resize", 0, "getExtremes");
+
/* Simply return the requisition width */
Requisition requisition;
sizeRequest (&requisition);
extremes->minWidth = extremes->maxWidth = requisition.width;
+ extremes->minWidthIntrinsic = extremes->minWidth;
+ extremes->maxWidthIntrinsic = extremes->maxWidth;
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d / %d",
+ extremes->minWidth, extremes->maxWidth);
+ DBG_OBJ_LEAVE ();
}
void Resource::sizeAllocate (Allocation *allocation)
{
}
-void Resource::setWidth (int width)
+int Resource::getAvailWidthOfChild (Widget *child, bool forceValue)
+{
+ // Only used when the resource contains other dillo widgets.
+ misc::assertNotReached ();
+ return 0;
+}
+
+int Resource::getAvailHeightOfChild (Widget *child, bool forceValue)
+{
+ // Only used when the resource contains other dillo widgets.
+ misc::assertNotReached ();
+ return 0;
+}
+
+void Resource::correctRequisitionOfChild (Widget *child,
+ Requisition *requisition,
+ void (*splitHeightFun) (int, int*,
+ int*))
{
+ // Only used when the resource contains other dillo widgets.
+ misc::assertNotReached ();
}
-void Resource::setAscent (int ascent)
+void Resource::correctExtremesOfChild (Widget *child, Extremes *extremes)
{
+ // Only used when the resource contains other dillo widgets.
+ misc::assertNotReached ();
}
-void Resource::setDescent (int descent)
+void Resource::containerSizeChangedForChildren ()
{
+ // No children by default.
}
void Resource::setDisplayed (bool displayed)
@@ -264,18 +311,17 @@ Iterator *LabelButtonResource::iterator (Content::Type mask, bool atEnd)
// ----------------------------------------------------------------------
-void ComplexButtonResource::LayoutReceiver::canvasSizeChanged (int width,
- int ascent,
- int descent)
+void ComplexButtonResource::LayoutReceiver::resizeQueued (bool extremesChanged)
{
- /**
- * \todo Verify that this is correct.
- */
- resource->queueResize (resource->childWidget->extremesChanged ());
+ DBG_OBJ_ENTER ("resize", 0, "LayoutReceiver/resizeQueued", "%s",
+ extremesChanged ? "true" : "false");
+ resource->queueResize (extremesChanged);
+ DBG_OBJ_LEAVE ();
}
ComplexButtonResource::ComplexButtonResource ()
{
+ DBG_OBJ_CREATE ("dw::core::ui::ComplexButtonResource");
layout = NULL;
layoutReceiver.resource = this;
click_x = click_y = -1;
@@ -283,61 +329,136 @@ ComplexButtonResource::ComplexButtonResource ()
void ComplexButtonResource::init (Widget *widget)
{
- this->childWidget = widget;
+ childWidget = widget;
layout = new Layout (createPlatform ());
setLayout (layout);
+ DBG_OBJ_ASSOC_CHILD (layout);
layout->setWidget (widget);
layout->connect (&layoutReceiver);
+
+ if (getEmbed ())
+ childWidget->setQuasiParent (getEmbed ());
}
void ComplexButtonResource::setEmbed (Embed *embed)
{
ButtonResource::setEmbed (embed);
- if (childWidget->usesHints ())
- embed->setUsesHints ();
+ if (childWidget)
+ childWidget->setQuasiParent (getEmbed ());
}
ComplexButtonResource::~ComplexButtonResource ()
{
delete layout;
+ DBG_OBJ_DELETE ();
}
void ComplexButtonResource::sizeRequest (Requisition *requisition)
{
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest");
+
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_LEAVE ();
}
void ComplexButtonResource::getExtremes (Extremes *extremes)
{
+ DBG_OBJ_ENTER0 ("resize", 0, "getExtremes");
+
Extremes widgetExtremes;
childWidget->getExtremes (&widgetExtremes);
extremes->minWidth = widgetExtremes.minWidth + 2 * reliefXThickness ();
extremes->maxWidth = widgetExtremes.maxWidth + 2 * reliefXThickness ();
+ extremes->minWidthIntrinsic = extremes->minWidth;
+ extremes->maxWidthIntrinsic = extremes->maxWidth;
+
+ DBG_OBJ_MSGF ("resize", 1, "result: %d / %d",
+ extremes->minWidth, extremes->maxWidth);
+ DBG_OBJ_LEAVE ();
}
void ComplexButtonResource::sizeAllocate (Allocation *allocation)
{
}
-void ComplexButtonResource::setWidth (int width)
+int ComplexButtonResource::getAvailWidthOfChild (Widget *child, bool forceValue)
+{
+ int embedWidth = getEmbed()->getAvailWidth (forceValue);
+ if (embedWidth == -1)
+ return -1;
+ else
+ return misc::max (embedWidth - 2 * reliefXThickness (), 0);
+}
+
+int ComplexButtonResource::getAvailHeightOfChild (Widget *child,
+ bool forceValue)
+{
+ int embedHeight = getEmbed()->getAvailHeight (forceValue);
+ if (embedHeight == -1)
+ return -1;
+ else
+ return misc::max (embedHeight - 2 * reliefYThickness (), 0);
+}
+
+void ComplexButtonResource::correctRequisitionOfChild (Widget *child,
+ Requisition *requisition,
+ void (*splitHeightFun)
+ (int, int*, int*))
{
- childWidget->setWidth (width - 2 * reliefXThickness ());
+ // Similar to Widget::correctRequisitionOfChild, but for percentage
+ // the relief has to be considered.
+
+ if (style::isPerLength (child->getStyle()->width)) {
+ int availWidth = getEmbed()->getAvailHeight (false);
+ if (availWidth != -1) {
+ int baseWidth = misc::max (availWidth
+ - getEmbed()->boxDiffWidth ()
+ - 2 * reliefXThickness (),
+ 0);
+ requisition->width =
+ child->applyPerWidth (baseWidth, child->getStyle()->width);
+ }
+ } else
+ getEmbed()->correctReqWidthOfChildNoRec (child, requisition);
+
+ // TODO Percentage heights are ignored again.
+ getEmbed()->correctReqHeightOfChildNoRec (child, requisition,
+ splitHeightFun);
+
}
-void ComplexButtonResource::setAscent (int ascent)
+void ComplexButtonResource::correctExtremesOfChild (Widget *child,
+ Extremes *extremes)
{
- childWidget->setAscent (ascent - reliefYThickness ());
+ // Similar to Widget::correctExtremesOfChild, but for percentage
+ // the relief has to be considered.
+
+ if (style::isPerLength (child->getStyle()->width)) {
+ int availWidth = getEmbed()->getAvailHeight (false);
+ if (availWidth != -1) {
+ int baseWidth = misc::max (availWidth
+ - getEmbed()->boxDiffWidth ()
+ - 2 * reliefXThickness (),
+ 0);
+ extremes->minWidth = extremes->maxWidth =
+ child->applyPerWidth (baseWidth, child->getStyle()->width);
+ }
+ } else
+ getEmbed()->correctExtremesOfChildNoRec (child, extremes);
}
-void ComplexButtonResource::setDescent (int descent)
+void ComplexButtonResource::containerSizeChangedForChildren ()
{
- childWidget->setDescent (descent - reliefYThickness ());
+ layout->containerSizeChanged ();
}
Iterator *ComplexButtonResource::iterator (Content::Type mask, bool atEnd)
diff --git a/dw/ui.hh b/dw/ui.hh
index d46705fe..6703ccca 100644
--- a/dw/ui.hh
+++ b/dw/ui.hh
@@ -231,6 +231,16 @@ protected:
void sizeRequestImpl (Requisition *requisition);
void getExtremesImpl (Extremes *extremes);
void sizeAllocateImpl (Allocation *allocation);
+
+ int getAvailWidthOfChild (Widget *child, bool forceValue);
+ int getAvailHeightOfChild (Widget *child, bool forceValue);
+ void correctRequisitionOfChild (Widget *child,
+ Requisition *requisition,
+ void (*splitHeightFun) (int, int*, int*));
+ void correctExtremesOfChild (Widget *child, Extremes *extremes);
+
+ void containerSizeChangedForChildren ();
+
void enterNotifyImpl (core::EventCrossing *event);
void leaveNotifyImpl (core::EventCrossing *event);
bool buttonPressImpl (core::EventButton *event);
@@ -241,18 +251,26 @@ public:
Embed(Resource *resource);
~Embed();
- void setWidth (int width);
- void setAscent (int ascent);
- void setDescent (int descent);
void setDisplayed (bool displayed);
void setEnabled (bool enabled);
void draw (View *view, Rectangle *area);
Iterator *iterator (Content::Type mask, bool atEnd);
void setStyle (style::Style *style);
- inline void setUsesHints () { setFlags (USES_HINTS); }
-
inline Resource *getResource () { return resource; }
+
+ inline void correctReqWidthOfChildNoRec (Widget *child,
+ Requisition *requisition)
+ { Widget::correctReqWidthOfChild (child, requisition); }
+
+ inline void correctReqHeightOfChildNoRec (Widget *child,
+ Requisition *requisition,
+ void (*splitHeightFun) (int, int*,
+ int*))
+ { Widget::correctReqHeightOfChild (child, requisition, splitHeightFun); }
+
+ virtual void correctExtremesOfChildNoRec (Widget *child, Extremes *extremes)
+ { Widget::correctExtremesOfChild (child, extremes); }
};
/**
@@ -329,16 +347,24 @@ protected:
clickedEmitter.emitClicked (this, event); }
public:
- inline Resource () { embed = NULL; }
+ inline Resource ()
+ { embed = NULL; DBG_OBJ_CREATE ("dw::core::ui::Resource"); }
virtual ~Resource ();
virtual void sizeRequest (Requisition *requisition) = 0;
virtual void getExtremes (Extremes *extremes);
virtual void sizeAllocate (Allocation *allocation);
- virtual void setWidth (int width);
- virtual void setAscent (int ascent);
- virtual void setDescent (int descent);
+
+ virtual int getAvailWidthOfChild (Widget *child, bool forceValue);
+ virtual int getAvailHeightOfChild (Widget *child, bool forceValue);
+ virtual void correctRequisitionOfChild (Widget *child,
+ Requisition *requisition,
+ void (*splitHeightFun) (int, int*,
+ int*));
+ virtual void correctExtremesOfChild (Widget *child, Extremes *extremes);
+ virtual void containerSizeChangedForChildren ();
+
virtual void setDisplayed (bool displayed);
virtual void draw (View *view, Rectangle *area);
virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0;
@@ -377,7 +403,7 @@ private:
public:
ComplexButtonResource *resource;
- void canvasSizeChanged (int width, int ascent, int descent);
+ void resizeQueued (bool extremesChanged);
};
friend class LayoutReceiver;
@@ -406,9 +432,15 @@ public:
void sizeRequest (Requisition *requisition);
void getExtremes (Extremes *extremes);
void sizeAllocate (Allocation *allocation);
- void setWidth (int width);
- void setAscent (int ascent);
- void setDescent (int descent);
+
+ int getAvailWidthOfChild (Widget *child, bool forceValue);
+ int getAvailHeightOfChild (Widget *child, bool forceValue);
+ void correctRequisitionOfChild (Widget *child,
+ Requisition *requisition,
+ void (*splitHeightFun) (int, int*, int*));
+ void correctExtremesOfChild (Widget *child, Extremes *extremes);
+ void containerSizeChangedForChildren ();
+
Iterator *iterator (Content::Type mask, bool atEnd);
int getClickX () {return click_x;};
int getClickY () {return click_y;};
diff --git a/dw/widget.cc b/dw/widget.cc
index 385bdb97..ed2c2b8e 100644
--- a/dw/widget.cc
+++ b/dw/widget.cc
@@ -17,8 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-
#include "core.hh"
#include "../lout/msg.h"
@@ -62,6 +60,7 @@ void Widget::WidgetImgRenderer::draw (int x, int y, int width, int height)
// ----------------------------------------------------------------------
+bool Widget::adjustMinWidth = false;
int Widget::CLASS_ID = -1;
Widget::Widget ()
@@ -69,8 +68,10 @@ Widget::Widget ()
DBG_OBJ_CREATE ("dw::core::Widget");
registerName ("dw::core::Widget", &CLASS_ID);
- flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS);
- parent = NULL;
+ flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED);
+ parent = quasiParent = generator = container = NULL;
+ DBG_OBJ_SET_PTR ("container", container);
+
layout = NULL;
allocation.x = -1;
@@ -79,6 +80,8 @@ Widget::Widget ()
allocation.ascent = 1;
allocation.descent = 0;
+ extraSpace.top = extraSpace.right = extraSpace.bottom = extraSpace.left = 0;
+
style = NULL;
bgColor = NULL;
buttonSensitive = true;
@@ -106,7 +109,7 @@ Widget::~Widget ()
if (parent)
parent->removeChild (this);
- else
+ else if (layout)
layout->removeWidget ();
DBG_OBJ_DELETE ();
@@ -150,59 +153,320 @@ 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);
+
+ // Determine the container. Currently rather simple; will become
+ // more complicated when absolute and fixed positions are
+ // supported.
+ container = NULL;
+ for (Widget *widget = getParent (); widget != NULL && container == NULL;
+ widget = widget->getParent())
+ if (widget->isPossibleContainer ())
+ container = widget;
+ // If there is no possible container widget, there is
+ // (surprisingly!) also no container (i. e. the viewport is
+ // used). Does not occur in dillo, where the toplevel widget is a
+ // Textblock.
+ DBG_OBJ_SET_PTR ("container", container);
+
+ notifySetParent();
+}
+
+void Widget::setQuasiParent (Widget *quasiParent)
+{
+ this->quasiParent = quasiParent;
+
+ // More to do? Compare with setParent().
+
+ DBG_OBJ_SET_PTR ("quasiParent", quasiParent);
}
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);
- _MSG("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height);
+
+ DBG_OBJ_ENTER ("draw", 0, "queueDrawArea", "%d, %d, %d, %d",
+ x, y, width, height);
+
+ _MSG("Widget::queueDrawArea alloc(%d %d %d %d) wid(%d %d %d %d)\n",
+ allocation.x, allocation.y,
+ allocation.width, allocation.ascent + allocation.descent,
+ x, y, width, height);
+ if (layout)
+ layout->queueDraw (x + allocation.x, y + allocation.y, width, height);
+
+ DBG_OBJ_LEAVE ();
}
/**
* \brief This method should be called, when a widget changes its size.
+ *
+ * A "fast" queueResize will ignore the anchestors, and furthermore
+ * not trigger the idle function. Used only within
+ * viewportSizeChanged, and not available outside Layout and Widget.
*/
-void Widget::queueResize (int ref, bool extremesChanged)
+void Widget::queueResize (int ref, bool extremesChanged, bool fast)
{
+ DBG_OBJ_ENTER ("resize", 0, "queueResize", "%d, %s, %s",
+ ref, extremesChanged ? "true" : "false",
+ fast ? "true" : "false");
+
+ // 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->pushUnder (new Layout::QueueResizeItem
+ (this, ref, extremesChanged,
+ fast));
+ } else {
+ actualQueueResize (ref, extremesChanged, fast);
+
+ DBG_IF_RTFL {
+ if (layout == NULL)
+ DBG_OBJ_MSG ("resize", 1, "layout is not set");
+ else if (layout->queueQueueResizeList->size () == 0)
+ DBG_OBJ_MSG ("resize", 1, "queue item list is empty");
+ }
+
+ while (layout != NULL && layout->queueQueueResizeList->size () > 0) {
+ DBG_IF_RTFL {
+ DBG_OBJ_MSGF ("resize", 1, "queue item list has %d elements:",
+ layout->queueQueueResizeList->size ());
+#if 0
+ // TODO This worked when queueQueueResizeList was a Vector; now,
+ // iterators should be used.
+ DBG_OBJ_MSG_START ();
+ for (int i = 0; i < layout->queueQueueResizeList->size (); i++) {
+ DBG_OBJ_MSGF
+ ("resize", 1,
+ "#%d: widget = %p, ref = %d, extremesChanged = %s, "
+ "fast = %s",
+ i, layout->queueQueueResizeList->get(i)->widget,
+ layout->queueQueueResizeList->get(i)->ref,
+ layout->queueQueueResizeList->get(i)->extremesChanged ?
+ "true" : "false",
+ layout->queueQueueResizeList->get(i)->fast ?
+ "true" : "false");
+ }
+ DBG_OBJ_MSG_END ();
+ DBG_OBJ_MSG ("resize", 1, "taking #0 out of list");
+#endif
+ }
+
+ Layout::QueueResizeItem *item =
+ layout->queueQueueResizeList->getTop ();
+ item->widget->actualQueueResize (item->ref, item->extremesChanged,
+ item->fast);
+ layout->queueQueueResizeList->pop ();
+ }
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+void Widget::actualQueueResize (int ref, bool extremesChanged, bool fast)
+{
+ assert (!queueResizeEntered ());
+
+ DBG_OBJ_ENTER ("resize", 0, "actualQueueResize", "%d, %s, %s",
+ ref, extremesChanged ? "true" : "false",
+ fast ? "true" : "false");
+
+ enterQueueResize ();
+
Widget *widget2, *child;
- //printf("The %stop-level %s %p with parentRef = %d has changed its size.\n",
- // parent ? "non-" : "", getClassName(), this, parentRef);
+ Flags resizeFlag, extremesFlag;
+
+ if (layout) {
+ // If RESIZE_QUEUED is set, this widget is already in the list.
+ if (!resizeQueued ())
+ layout->queueResizeList->put (this);
- setFlags (NEEDS_RESIZE);
- setFlags (NEEDS_ALLOCATE);
+ 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 "
- // "%stop-level %s %p with parentRef = %d\n",
- // widget2->parent ? "non-" : "", widget2->getClassName(), widget2,
- // widget2->parentRef);
-
- if (extremesChanged) {
- widget2->setFlags (EXTREMES_CHANGED);
- widget2->markExtremesChange (child->parentRef);
+ if (fast) {
+ if (parent) {
+ // In this case, queueResize is called from top (may be a
+ // random entry point) to bottom, so markSizeChange and
+ // markExtremesChange have to be called explicitly for the
+ // parent. The tests (needsResize etc.) are uses to check
+ // whether queueResize has been called for the parent, or
+ // whether this widget is the enty point.
+ if (parent->needsResize () || parent->resizeQueued ())
+ parent->markSizeChange (parentRef);
+ if (parent->extremesChanged () || parent->extremesQueued ())
+ parent->markExtremesChange (parentRef);
}
+ } else {
+ for (widget2 = parent, child = this; widget2;
+ child = widget2, widget2 = widget2->parent) {
+ if (layout && !widget2->resizeQueued ())
+ layout->queueResizeList->put (widget2);
+
+ DBG_OBJ_MSGF ("resize", 2, "setting %s and ALLOCATE_QUEUED for %p",
+ resizeFlag == RESIZE_QUEUED ?
+ "RESIZE_QUEUED" : "NEEDS_RESIZE",
+ widget2);
+
+ widget2->setFlags (resizeFlag);
+ widget2->markSizeChange (child->parentRef);
+ widget2->setFlags (ALLOCATE_QUEUED);
+
+ if (extremesChanged) {
+ widget2->setFlags (extremesFlag);
+ widget2->markExtremesChange (child->parentRef);
+ }
+ }
+
+ if (layout)
+ layout->queueResize (extremesChanged);
}
- if (layout)
- layout->queueResize ();
+ leaveQueueResize ();
+
+ DBG_OBJ_LEAVE ();
+}
+
+void Widget::containerSizeChanged ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChanged");
+
+ // If there is a container widget (not the viewport), which has not
+ // changed its size (which can be determined by the respective
+ // flags: this method is called recursively), this widget will
+ // neither change its size. Also, the recursive iteration can be
+ // stopped, since the children of this widget will
+ if (container == NULL ||
+ container->needsResize () || container->resizeQueued () ||
+ container->extremesChanged () || container->extremesQueued ()) {
+ // Viewport (container == NULL) or container widget has changed
+ // its size.
+ if (affectedByContainerSizeChange ())
+ queueResizeFast (0, true);
+
+ // Even if *this* widget is not affected, children may be, so
+ // iterate over children.
+ containerSizeChangedForChildren ();
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+bool Widget::affectedByContainerSizeChange ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "affectedByContainerSizeChange");
+
+ bool ret;
+
+ // This standard implementation is suitable for all widgets which
+ // call correctRequisition() and correctExtremes(), even in the way
+ // how Textblock and Image do (see comments there). Has to be kept
+ // in sync.
+
+ if (container == NULL) {
+ if (style::isAbsLength (getStyle()->width) &&
+ style::isAbsLength (getStyle()->height))
+ // Both absolute, i. e. fixed: no dependency.
+ ret = false;
+ else if (style::isPerLength (getStyle()->width) ||
+ style::isPerLength (getStyle()->height)) {
+ // Any percentage: certainly dependenant.
+ ret = true;
+ } else
+ // One or both is "auto": depends ...
+ ret =
+ (getStyle()->width == style::LENGTH_AUTO ?
+ usesAvailWidth () : false) ||
+ (getStyle()->height == style::LENGTH_AUTO ?
+ usesAvailHeight () : false);
+ } else
+ ret = container->affectsSizeChangeContainerChild (this);
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false");
+ DBG_OBJ_LEAVE ();
+ return ret;
+}
+
+bool Widget::affectsSizeChangeContainerChild (Widget *child)
+{
+ DBG_OBJ_ENTER ("resize", 0, "affectsSizeChangeContainerChild", "%p", child);
+
+ bool ret;
+
+ // From the point of view of the container. This standard
+ // implementation should be suitable for most (if not all)
+ // containers.
+
+ if (style::isAbsLength (child->getStyle()->width) &&
+ style::isAbsLength (child->getStyle()->height))
+ // Both absolute, i. e. fixed: no dependency.
+ ret = false;
+ else if (style::isPerLength (child->getStyle()->width) ||
+ style::isPerLength (child->getStyle()->height)) {
+ // Any percentage: certainly dependenant.
+ ret = true;
+ } else
+ // One or both is "auto": depends ...
+ ret =
+ (child->getStyle()->width == style::LENGTH_AUTO ?
+ child->usesAvailWidth () : false) ||
+ (child->getStyle()->height == style::LENGTH_AUTO ?
+ child->usesAvailHeight () : false);
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false");
+ DBG_OBJ_LEAVE ();
+ return ret;
+}
+
+void Widget::containerSizeChangedForChildren ()
+{
+ DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
+
+ // Working, but inefficient standard implementation.
+ Iterator *it = iterator ((Content::Type)(Content::WIDGET_IN_FLOW |
+ Content::WIDGET_OOF_CONT),
+ false);
+ while (it->next ())
+ it->getContent()->widget->containerSizeChanged ();
+ it->unref ();
+
+ DBG_OBJ_LEAVE ();
+}
+
+/**
+ * \brief Must be implemengted by a method returning true, when
+ * getAvailWidth() is called.
+ */
+bool Widget::usesAvailWidth ()
+{
+ return false;
}
+/**
+ * \brief Must be implemengted by a method returning true, when
+ * getAvailHeight() is called.
+ */
+bool Widget::usesAvailHeight ()
+{
+ return false;
+}
/**
* \brief This method is a wrapper for Widget::sizeRequestImpl(); it calls
@@ -210,6 +474,21 @@ void Widget::queueResize (int ref, bool extremesChanged)
*/
void Widget::sizeRequest (Requisition *requisition)
{
+ assert (!queueResizeEntered ());
+
+ DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest");
+
+ enterSizeRequest ();
+
+ 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 +500,377 @@ void Widget::sizeRequest (Requisition *requisition)
DBG_OBJ_SET_NUM ("requisition.descent", requisition->descent);
} else
*requisition = this->requisition;
+
+ leaveSizeRequest ();
+
+ DBG_OBJ_LEAVE ();
+}
+
+/**
+ * \brief Used to evaluate Widget::adjustMinWidth.
+ *
+ * If extremes == NULL, getExtremes is called. ForceValue is the same
+ * value passed to getAvailWidth etc.; if false, getExtremes is not
+ * called.
+ */
+int Widget::getMinWidth (Extremes *extremes, bool forceValue)
+{
+ DBG_OBJ_ENTER ("resize", 0, "getMinWidth", "..., %s",
+ forceValue ? "true" : "false");
+ int minWidth;
+
+ if (getAdjustMinWidth ()) {
+ Extremes extremes2;
+ if (extremes == NULL) {
+ if (forceValue) {
+ getExtremes (&extremes2);
+ extremes = &extremes2;
+ }
+ }
+
+ // TODO Not completely clear whether this is feasable: Within
+ // the context of getAvailWidth(false) etc., getExtremes may not
+ // be called. We ignore the minimal width then.
+ minWidth = extremes ? extremes->minWidthIntrinsic : 0;
+ } else
+ minWidth = 0;
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", minWidth);
+ DBG_OBJ_LEAVE ();
+
+ return minWidth;
+}
+
+/**
+ * Return available width including margin/border/padding
+ * (extraSpace?), not only the content width.
+ */
+int Widget::getAvailWidth (bool forceValue)
+{
+ DBG_OBJ_ENTER ("resize", 0, "getAvailWidth", "%s",
+ forceValue ? "true" : "false");
+
+ int width;
+
+ if (parent == NULL && quasiParent == NULL) {
+ DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport");
+ DBG_OBJ_MSG_START ();
+
+ // TODO Consider nested layouts (e. g. <button>).
+
+ int viewportWidth =
+ layout->viewportWidth - (layout->canvasHeightGreater ?
+ layout->vScrollbarThickness : 0);
+ width = -1;
+ calcFinalWidth (getStyle (), viewportWidth, NULL, 0, forceValue, &width);
+ if (width == -1)
+ width = viewportWidth;
+
+ DBG_OBJ_MSG_END ();
+ } else if (parent) {
+ DBG_OBJ_MSG ("resize", 1, "delegated to parent");
+ DBG_OBJ_MSG_START ();
+ width = parent->getAvailWidthOfChild (this, forceValue);
+ DBG_OBJ_MSG_END ();
+ } else /* if (quasiParent) */ {
+ DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent");
+ DBG_OBJ_MSG_START ();
+ width = quasiParent->getAvailWidthOfChild (this, forceValue);
+ DBG_OBJ_MSG_END ();
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", width);
+ DBG_OBJ_LEAVE ();
+
+ return width;
+}
+
+/**
+ * Return available height including margin/border/padding
+ * (extraSpace?), not only the content height.
+ */
+int Widget::getAvailHeight (bool forceValue)
+{
+ // TODO Correct by ... not extremes, but ...? (Height extremes?)
+
+ // TODO Consider 'min-height' and 'max-height'. (Minor priority, as long as
+ // "getAvailHeight (true)" is not used.
+
+ DBG_OBJ_ENTER ("resize", 0, "getAvailHeight", "%s",
+ forceValue ? "true" : "false");
+
+ int height;
+
+ if (parent == NULL && quasiParent == NULL) {
+ DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport");
+ DBG_OBJ_MSG_START ();
+
+ // TODO Consider nested layouts (e. g. <button>).
+ if (style::isAbsLength (getStyle()->height)) {
+ DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx",
+ style::absLengthVal (getStyle()->height));
+ height = style::absLengthVal (getStyle()->height) + boxDiffHeight ();
+ } else if (style::isPerLength (getStyle()->height)) {
+ DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%",
+ 100 * style::perLengthVal_useThisOnlyForDebugging
+ (getStyle()->height));
+ // Notice that here -- unlike getAvailWidth() --
+ // layout->hScrollbarThickness is not considered here;
+ // something like canvasWidthGreater (analogue to
+ // canvasHeightGreater) would be complicated and lead to
+ // possibly contradictory self-references.
+ height = applyPerHeight (layout->viewportHeight, getStyle()->height);
+ } else {
+ DBG_OBJ_MSG ("resize", 1, "no specification");
+ height = layout->viewportHeight;
+ }
+
+ DBG_OBJ_MSG_END ();
+ } else if (parent) {
+ DBG_OBJ_MSG ("resize", 1, "delegated to parent");
+ DBG_OBJ_MSG_START ();
+ height = parent->getAvailHeightOfChild (this, forceValue);
+ DBG_OBJ_MSG_END ();
+ } else /* if (quasiParent) */ {
+ DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent");
+ DBG_OBJ_MSG_START ();
+ height = quasiParent->getAvailHeightOfChild (this, forceValue);
+ DBG_OBJ_MSG_END ();
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", height);
+ DBG_OBJ_LEAVE ();
+
+ return height;
+}
+
+void Widget::correctRequisition (Requisition *requisition,
+ void (*splitHeightFun) (int, int *, int *))
+{
+ // TODO Correct height by ... not extremes, but ...? (Height extremes?)
+
+ DBG_OBJ_ENTER ("resize", 0, "correctRequisition", "%d * (%d + %d), ...",
+ requisition->width, requisition->ascent,
+ requisition->descent);
+
+ if (parent == NULL && quasiParent == NULL) {
+ DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport");
+ DBG_OBJ_MSG_START ();
+
+ int limitMinWidth = getMinWidth (NULL, true);
+ int viewportWidth =
+ layout->viewportWidth - (layout->canvasHeightGreater ?
+ layout->vScrollbarThickness : 0);
+ calcFinalWidth (getStyle (), viewportWidth, NULL, limitMinWidth, false,
+ &requisition->width);
+
+ // For layout->viewportHeight, see comment in getAvailHeight().
+ int height = calcHeight (getStyle()->height, false,
+ layout->viewportHeight, NULL, false);
+ int minHeight = calcHeight (getStyle()->minHeight, false,
+ layout->viewportHeight, NULL, false);
+ int maxHeight = calcHeight (getStyle()->maxHeight, false,
+ layout->viewportHeight, NULL, false);
+
+ // TODO Perhaps split first, then add box ascent and descent.
+ if (height != -1)
+ splitHeightFun (height, &requisition->ascent, &requisition->descent);
+ if (minHeight != -1 &&
+ requisition->ascent + requisition->descent < minHeight)
+ splitHeightFun (minHeight, &requisition->ascent,
+ &requisition->descent);
+ if (maxHeight != -1 &&
+ requisition->ascent + requisition->descent > maxHeight)
+ splitHeightFun (maxHeight, &requisition->ascent,
+ &requisition->descent);
+
+ DBG_OBJ_MSG_END ();
+ } else if (parent) {
+ DBG_OBJ_MSG ("resize", 1, "delegated to parent");
+ DBG_OBJ_MSG_START ();
+ parent->correctRequisitionOfChild (this, requisition, splitHeightFun);
+ DBG_OBJ_MSG_END ();
+ } else /* if (quasiParent) */ {
+ DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent");
+ DBG_OBJ_MSG_START ();
+ quasiParent->correctRequisitionOfChild (this, requisition,
+ splitHeightFun);
+ DBG_OBJ_MSG_END ();
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
+ requisition->width, requisition->ascent,
+ requisition->descent);
+ DBG_OBJ_LEAVE ();
+}
+
+void Widget::correctExtremes (Extremes *extremes)
+{
+ DBG_OBJ_ENTER ("resize", 0, "correctExtremes", "%d (%d) / %d (%d)",
+ extremes->minWidth, extremes->minWidthIntrinsic,
+ extremes->maxWidth, extremes->maxWidthIntrinsic);
+
+ if (container == NULL && quasiParent == NULL) {
+ DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport");
+ DBG_OBJ_MSG_START ();
+
+ int limitMinWidth = getMinWidth (extremes, false);
+ int viewportWidth =
+ layout->viewportWidth - (layout->canvasHeightGreater ?
+ layout->vScrollbarThickness : 0);
+
+ int width = calcWidth (getStyle()->width, viewportWidth, NULL,
+ limitMinWidth, false);
+ int minWidth = calcWidth (getStyle()->minWidth, viewportWidth, NULL,
+ limitMinWidth, false);
+ int maxWidth = calcWidth (getStyle()->maxWidth, viewportWidth, NULL,
+ limitMinWidth, false);
+
+ DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d",
+ width, minWidth, maxWidth);
+
+ if (width != -1)
+ extremes->minWidth = extremes->maxWidth = width;
+ if (minWidth != -1)
+ extremes->minWidth = minWidth;
+ if (maxWidth != -1)
+ extremes->maxWidth = maxWidth;
+
+ DBG_OBJ_MSG_END ();
+ } else if (parent) {
+ DBG_OBJ_MSG ("resize", 1, "delegated to parent");
+ DBG_OBJ_MSG_START ();
+ parent->correctExtremesOfChild (this, extremes);
+ DBG_OBJ_MSG_END ();
+ } else /* if (quasiParent) */ {
+ DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent");
+ DBG_OBJ_MSG_START ();
+ quasiParent->correctExtremesOfChild (this, extremes);
+ DBG_OBJ_MSG_END ();
+ }
+
+ if (extremes->maxWidth < extremes->minWidth)
+ extremes->maxWidth = extremes->minWidth;
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d / %d",
+ extremes->minWidth, extremes->maxWidth);
+ DBG_OBJ_LEAVE ();
+}
+
+int Widget::calcWidth (style::Length cssValue, int refWidth, Widget *refWidget,
+ int limitMinWidth, bool forceValue)
+{
+ DBG_OBJ_ENTER ("resize", 0, "calcWidth", "0x%x, %d, %p, %d",
+ cssValue, refWidth, refWidget, limitMinWidth);
+
+ assert (refWidth != -1 || refWidget != NULL);
+
+ int width;
+
+ if (style::isAbsLength (cssValue)) {
+ DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx",
+ style::absLengthVal (cssValue));
+ width = misc::max (style::absLengthVal (cssValue) + boxDiffWidth (),
+ limitMinWidth);
+ } else if (style::isPerLength (cssValue)) {
+ DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%",
+ 100 * style::perLengthVal_useThisOnlyForDebugging
+ (cssValue));
+ if (refWidth != -1)
+ width = misc::max (applyPerWidth (refWidth, cssValue), limitMinWidth);
+ else {
+ int availWidth = refWidget->getAvailWidth (forceValue);
+ if (availWidth != -1) {
+ int containerWidth = availWidth - refWidget->boxDiffWidth ();
+ width = misc::max (applyPerWidth (containerWidth, cssValue),
+ limitMinWidth);
+ } else
+ width = -1;
+ }
+ } else {
+ DBG_OBJ_MSG ("resize", 1, "not specified");
+ width = -1;
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", width);
+ DBG_OBJ_LEAVE ();
+ return width;
+}
+
+// *finalWidth may be -1.
+void Widget::calcFinalWidth (style::Style *style, int refWidth,
+ Widget *refWidget, int limitMinWidth,
+ bool forceValue, int *finalWidth)
+{
+ DBG_OBJ_ENTER ("resize", 0, "calcFinalWidth", "..., %d, %p, %d, [%d]",
+ refWidth, refWidget, limitMinWidth, *finalWidth);
+
+ int width = calcWidth (style->width, refWidth, refWidget, limitMinWidth,
+ forceValue);
+ int minWidth = calcWidth (style->minWidth, refWidth, refWidget,
+ limitMinWidth, forceValue);
+ int maxWidth = calcWidth (style->maxWidth, refWidth, refWidget,
+ limitMinWidth, forceValue);
+
+ DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d",
+ width, minWidth, maxWidth);
+
+ if (width != -1)
+ *finalWidth = width;
+ if (minWidth != -1 && *finalWidth != -1 && *finalWidth < minWidth)
+ *finalWidth = minWidth;
+ if (maxWidth != -1 && *finalWidth == -1 && *finalWidth > maxWidth)
+ *finalWidth = maxWidth;
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", *finalWidth);
+ DBG_OBJ_LEAVE ();
+}
+
+int Widget::calcHeight (style::Length cssValue, bool usePercentage,
+ int refHeight, Widget *refWidget, bool forceValue)
+{
+ // TODO Search for usage of this method and check the value of
+ // "usePercentage"; this has to be clarified.
+
+ DBG_OBJ_ENTER ("resize", 0, "calcHeight", "0x%x, %s, %d, %p",
+ cssValue, usePercentage ? "true" : "false", refHeight,
+ refWidget);
+
+ assert (refHeight != -1 || refWidget != NULL);
+
+ int height;
+
+ if (style::isAbsLength (cssValue)) {
+ DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx",
+ style::absLengthVal (cssValue));
+ height =
+ misc::max (style::absLengthVal (cssValue) + boxDiffHeight (), 0);
+ } else if (style::isPerLength (cssValue)) {
+ DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%",
+ 100 *
+ style::perLengthVal_useThisOnlyForDebugging (cssValue));
+ if (usePercentage) {
+ if (refHeight != -1)
+ height = misc::max (applyPerHeight (refHeight, cssValue), 0);
+ else {
+ int availHeight = refWidget->getAvailHeight (forceValue);
+ if (availHeight != -1) {
+ int containerHeight = availHeight - refWidget->boxDiffHeight ();
+ height =
+ misc::max (applyPerHeight (containerHeight, cssValue), 0);
+ } else
+ height = -1;
+ }
+ } else
+ height = -1;
+ } else {
+ DBG_OBJ_MSG ("resize", 1, "not specified");
+ height = -1;
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", height);
+ DBG_OBJ_LEAVE ();
+ return height;
}
/**
@@ -228,15 +878,48 @@ void Widget::sizeRequest (Requisition *requisition)
*/
void Widget::getExtremes (Extremes *extremes)
{
+ assert (!queueResizeEntered ());
+
+ DBG_OBJ_ENTER0 ("resize", 0, "getExtremes");
+
+ 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 ()) {
+ // For backward compatibility (part 1/2):
+ extremes->minWidthIntrinsic = extremes->maxWidthIntrinsic = -1;
+
getExtremesImpl (extremes);
+
+ // For backward compatibility (part 2/2):
+ if (extremes->minWidthIntrinsic == -1)
+ extremes->minWidthIntrinsic = extremes->minWidth;
+ if (extremes->maxWidthIntrinsic == -1)
+ extremes->maxWidthIntrinsic = extremes->maxWidth;
+
this->extremes = *extremes;
unsetFlags (EXTREMES_CHANGED);
DBG_OBJ_SET_NUM ("extremes.minWidth", extremes->minWidth);
+ DBG_OBJ_SET_NUM ("extremes.minWidthIntrinsic",
+ extremes->minWidthIntrinsic);
DBG_OBJ_SET_NUM ("extremes.maxWidth", extremes->maxWidth);
+ DBG_OBJ_SET_NUM ("extremes.maxWidthIntrinsic",
+ extremes->maxWidthIntrinsic);
} else
*extremes = this->extremes;
+
+ leaveGetExtremes ();
+
+ DBG_OBJ_LEAVE ();
}
/**
@@ -245,6 +928,33 @@ void Widget::getExtremes (Extremes *extremes)
*/
void Widget::sizeAllocate (Allocation *allocation)
{
+ assert (!queueResizeEntered ());
+ assert (!sizeRequestEntered ());
+ assert (!getExtremesEntered ());
+ assert (resizeIdleEntered ());
+
+ DBG_OBJ_ENTER ("resize", 0, "sizeAllocate", "%d, %d; %d * (%d + %d)",
+ allocation->x, allocation->y, allocation->width,
+ allocation->ascent, allocation->descent);
+
+ DBG_OBJ_MSGF ("resize", 1,
+ "old allocation (%d, %d; %d * (%d + %d)); needsAllocate: %s",
+ this->allocation.x, this->allocation.y, this->allocation.width,
+ this->allocation.ascent, this->allocation.descent,
+ needsAllocate () ? "true" : "false");
+
+ 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 +995,10 @@ void Widget::sizeAllocate (Allocation *allocation)
}
/*unsetFlags (NEEDS_RESIZE);*/
+
+ leaveSizeAllocate ();
+
+ DBG_OBJ_LEAVE ();
}
bool Widget::buttonPress (EventButton *event)
@@ -356,6 +1070,59 @@ void Widget::setStyle (style::Style *style)
queueResize (0, true);
else
queueDraw ();
+
+ // These should better be attributed to the style itself, and a
+ // script processing RTFL messages could transfer it to something
+ // equivalent:
+
+ DBG_OBJ_SET_NUM ("style.margin.top", style->margin.top);
+ DBG_OBJ_SET_NUM ("style.margin.bottom", style->margin.bottom);
+ DBG_OBJ_SET_NUM ("style.margin.left", style->margin.left);
+ DBG_OBJ_SET_NUM ("style.margin.right", style->margin.right);
+
+ DBG_OBJ_SET_NUM ("style.border-width.top", style->borderWidth.top);
+ DBG_OBJ_SET_NUM ("style.border-width.bottom", style->borderWidth.bottom);
+ DBG_OBJ_SET_NUM ("style.border-width.left", style->borderWidth.left);
+ DBG_OBJ_SET_NUM ("style.border-width.right", style->borderWidth.right);
+
+ DBG_OBJ_SET_NUM ("style.padding.top", style->padding.top);
+ DBG_OBJ_SET_NUM ("style.padding.bottom", style->padding.bottom);
+ DBG_OBJ_SET_NUM ("style.padding.left", style->padding.left);
+ DBG_OBJ_SET_NUM ("style.padding.right", style->padding.right);
+
+ DBG_OBJ_SET_NUM ("style.border-spacing (h)", style->hBorderSpacing);
+ DBG_OBJ_SET_NUM ("style.border-spacing (v)", style->vBorderSpacing);
+
+ DBG_OBJ_SET_SYM ("style.display",
+ style->display == style::DISPLAY_BLOCK ? "block" :
+ style->display == style::DISPLAY_INLINE ? "inline" :
+ style->display == style::DISPLAY_INLINE_BLOCK ?
+ "inline-block" :
+ style->display == style::DISPLAY_LIST_ITEM ? "list-item" :
+ style->display == style::DISPLAY_NONE ? "none" :
+ style->display == style::DISPLAY_TABLE ? "table" :
+ style->display == style::DISPLAY_TABLE_ROW_GROUP ?
+ "table-row-group" :
+ style->display == style::DISPLAY_TABLE_HEADER_GROUP ?
+ "table-header-group" :
+ style->display == style::DISPLAY_TABLE_FOOTER_GROUP ?
+ "table-footer-group" :
+ style->display == style::DISPLAY_TABLE_ROW ? "table-row" :
+ style->display == style::DISPLAY_TABLE_CELL ? "table-cell" :
+ "???");
+
+ DBG_OBJ_SET_NUM ("style.width (raw)", style->width);
+ DBG_OBJ_SET_NUM ("style.min-width (raw)", style->minWidth);
+ DBG_OBJ_SET_NUM ("style.max-width (raw)", style->maxWidth);
+ DBG_OBJ_SET_NUM ("style.height (raw)", style->height);
+ DBG_OBJ_SET_NUM ("style.min-height (raw)", style->minHeight);
+ DBG_OBJ_SET_NUM ("style.max-height (raw)", style->maxHeight);
+
+ if (style->backgroundColor)
+ DBG_OBJ_SET_COL ("style.background-color",
+ style->backgroundColor->getColor ());
+ else
+ DBG_OBJ_SET_SYM ("style.background-color", "transparent");
}
/**
@@ -411,6 +1178,10 @@ void Widget::drawBox (View *view, style::Style *style, Rectangle *area,
// does not define what here is called "reference area". To make it look
// smoothly, the widget padding box is used.
+ // TODO Handle inverse drawing the same way as in drawWidgetBox?
+ // Maybe this method (drawBox) is anyway obsolete when extraSpace
+ // is fully supported (as in the "dillo_grows" repository).
+
int xPad, yPad, widthPad, heightPad;
getPaddingArea (&xPad, &yPad, &widthPad, &heightPad);
style::drawBackground
@@ -421,7 +1192,8 @@ void Widget::drawBox (View *view, style::Style *style, Rectangle *area,
- style->margin.right - style->borderWidth.right,
height - style->margin.top - style->borderWidth.top
- style->margin.bottom - style->borderWidth.bottom,
- xPad, yPad, widthPad, heightPad, style, inverse, false);
+ xPad, yPad, widthPad, heightPad, style, style->backgroundColor,
+ inverse, false);
}
/**
@@ -443,10 +1215,26 @@ void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse)
int xPad, yPad, widthPad, heightPad;
getPaddingArea (&xPad, &yPad, &widthPad, &heightPad);
+
+ style::Color *bgColor;
+ if (inverse && style->backgroundColor == NULL) {
+ // See style::drawBackground: for inverse drawing, we need a
+ // defined background color. Search through ancestors.
+ Widget *w = this;
+ while (w != NULL && w->style->backgroundColor == NULL)
+ w = w->parent;
+
+ if (w != NULL && w->style->backgroundColor != NULL)
+ bgColor = w->style->backgroundColor;
+ else
+ bgColor = layout->getBgColor ();
+ } else
+ bgColor = style->backgroundColor;
+
style::drawBackground (view, layout, &canvasArea,
xPad, yPad, widthPad, heightPad,
xPad, yPad, widthPad, heightPad,
- style, inverse, parent == NULL);
+ style, bgColor, inverse, parent == NULL);
}
/*
@@ -507,6 +1295,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,11 +1373,15 @@ 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);
-
- while (childAtPoint == NULL && it->next ())
- childAtPoint = it->getContent()->widget->getWidgetAtPoint (x, y,
- level + 1);
+ it = iterator ((Content::Type)
+ (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT),
+ false);
+
+ while (childAtPoint == NULL && it->next ()) {
+ Widget *child = it->getContent()->widget;
+ if (child->wasAllocated ())
+ childAtPoint = child->getWidgetAtPoint (x, y, level + 1);
+ }
it->unref ();
@@ -603,18 +1414,10 @@ void Widget::getPaddingArea (int *xPad, int *yPad, int *widthPad,
*yPad = allocation.y + style->margin.top + style->borderWidth.top;
*widthPad = allocation.width - style->margin.left - style->borderWidth.left
- style->margin.right - style->borderWidth.right;
- *heightPad = getHeight () - style->margin.top - style->borderWidth.top
+ *heightPad = getHeight () - style->margin.top - style->borderWidth.top
- style->margin.bottom - style->borderWidth.bottom;
}
-void Widget::getExtremesImpl (Extremes *extremes)
-{
- /* Simply return the requisition width */
- Requisition requisition;
- sizeRequest (&requisition);
- extremes->minWidth = extremes->maxWidth = requisition.width;
-}
-
void Widget::sizeAllocateImpl (Allocation *allocation)
{
}
@@ -627,16 +1430,277 @@ void Widget::markExtremesChange (int ref)
{
}
-void Widget::setWidth (int width)
+int Widget::applyPerWidth (int containerWidth, style::Length perWidth)
+{
+ return style::multiplyWithPerLength (containerWidth, perWidth)
+ + boxDiffWidth ();
+}
+
+int Widget::applyPerHeight (int containerHeight, style::Length perHeight)
+{
+ return style::multiplyWithPerLength (containerHeight, perHeight)
+ + boxDiffHeight ();
+}
+
+int Widget::getAvailWidthOfChild (Widget *child, bool forceValue)
+{
+ // This is a halfway suitable implementation for all
+ // containers. For simplification, this will be used during the
+ // development; then, a differentiation could be possible.
+
+ DBG_OBJ_ENTER ("resize", 0, "getAvailWidthOfChild", "%p, %s",
+ child, forceValue ? "true" : "false");
+
+ int width;
+
+ if (child->getStyle()->width == style::LENGTH_AUTO) {
+ DBG_OBJ_MSG ("resize", 1, "no specification");
+ if (forceValue)
+ width = misc::max (getAvailWidth (true) - boxDiffWidth (), 0);
+ else
+ width = -1;
+ } else {
+ // In most cases, the toplevel widget should be a container, so
+ // the container is non-NULL when the parent is non-NULL. Just
+ // in case, regard also parent. And quasiParent.
+ Widget *effContainer = child->quasiParent ? child->quasiParent :
+ (child->container ? child->container : child->parent);
+
+ if (effContainer == this) {
+ width = -1;
+ child->calcFinalWidth (child->getStyle(), -1, this, 0, forceValue,
+ &width);
+ } else {
+ DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container");
+ DBG_OBJ_MSG_START ();
+ width = effContainer->getAvailWidthOfChild (child, forceValue);
+ DBG_OBJ_MSG_END ();
+ }
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", width);
+ DBG_OBJ_LEAVE ();
+
+ return width;
+}
+
+int Widget::getAvailHeightOfChild (Widget *child, bool forceValue)
+{
+ // Again, a suitable implementation for all widgets (perhaps).
+
+ // TODO Consider 'min-height' and 'max-height'. (Minor priority, as long as
+ // "getAvailHeight (true)" is not used.
+
+ DBG_OBJ_ENTER ("resize", 0, "getAvailHeightOfChild", "%p, %s",
+ child, forceValue ? "true" : "false");
+
+ int height;
+
+ if (child->getStyle()->height == style::LENGTH_AUTO) {
+ DBG_OBJ_MSG ("resize", 1, "no specification");
+ if (forceValue)
+ height = misc::max (getAvailHeight (true) - boxDiffHeight (), 0);
+ else
+ height = -1;
+ } else {
+ // See comment in Widget::getAvailWidthOfChild.
+ Widget *effContainer = child->quasiParent ? child->quasiParent :
+ (child->container ? child->container : child->parent);
+
+ if (effContainer == this) {
+ if (style::isAbsLength (child->getStyle()->height)) {
+ DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx",
+ style::absLengthVal (child->getStyle()->height));
+ height = misc::max (style::absLengthVal (child->getStyle()->height)
+ + child->boxDiffHeight (), 0);
+ } else {
+ assert (style::isPerLength (child->getStyle()->height));
+ DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%",
+ 100 * style::perLengthVal_useThisOnlyForDebugging
+ (child->getStyle()->height));
+
+ int availHeight = getAvailHeight (forceValue);
+ if (availHeight == -1)
+ height = -1;
+ else
+ height =
+ misc::max (child->applyPerHeight (availHeight -
+ boxDiffHeight (),
+ child->getStyle()->height),
+ 0);
+ }
+ } else {
+ DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container");
+ DBG_OBJ_MSG_START ();
+ height = effContainer->getAvailHeightOfChild (child, forceValue);
+ DBG_OBJ_MSG_END ();
+ }
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d", height);
+ DBG_OBJ_LEAVE ();
+
+ return height;
+}
+
+void Widget::correctRequisitionOfChild (Widget *child, Requisition *requisition,
+ void (*splitHeightFun) (int, int*,
+ int*))
+{
+ // Again, a suitable implementation for all widgets (perhaps).
+
+ DBG_OBJ_ENTER ("resize", 0, "correctRequisitionOfChild",
+ "%p, %d * (%d + %d), ...", child, requisition->width,
+ requisition->ascent, requisition->descent);
+
+ // See comment in Widget::getAvailWidthOfChild.
+ Widget *effContainer = child->quasiParent ? child->quasiParent :
+ (child->container ? child->container : child->parent);
+
+ if (effContainer == this) {
+ correctReqWidthOfChild (child, requisition);
+ correctReqHeightOfChild (child, requisition, splitHeightFun);
+ } else {
+ DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container");
+ DBG_OBJ_MSG_START ();
+ effContainer->correctRequisitionOfChild (child, requisition,
+ splitHeightFun);
+ DBG_OBJ_MSG_END ();
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
+ requisition->width, requisition->ascent,
+ requisition->descent);
+ DBG_OBJ_LEAVE ();
+}
+
+void Widget::correctReqWidthOfChild (Widget *child, Requisition *requisition)
{
+ DBG_OBJ_ENTER ("resize", 0, "correctReqWidthOfChild", "%p, %d * (%d + %d)",
+ child, requisition->width, requisition->ascent,
+ requisition->descent);
+
+ assert (this == child->quasiParent || this == child->container);
+
+ int limitMinWidth = child->getMinWidth (NULL, true);
+ child->calcFinalWidth (child->getStyle(), -1, this, limitMinWidth, false,
+ &requisition->width);
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
+ requisition->width, requisition->ascent,
+ requisition->descent);
+ DBG_OBJ_LEAVE ();
}
-void Widget::setAscent (int ascent)
+void Widget::correctReqHeightOfChild (Widget *child, Requisition *requisition,
+ void (*splitHeightFun) (int, int*, int*))
{
+ // TODO Correct height by extremes? (Height extemes?)
+
+ assert (this == child->quasiParent || this == child->container);
+
+ DBG_OBJ_ENTER ("resize", 0, "correctReqHeightOfChild",
+ "%p, %d * (%d + %d), ...", child, requisition->width,
+ requisition->ascent, requisition->descent);
+
+ int height = child->calcHeight (child->getStyle()->height, false, -1, this,
+ false);
+ int minHeight = child->calcHeight (child->getStyle()->minHeight, false, -1,
+ this, false);
+ int maxHeight = child->calcHeight (child->getStyle()->maxHeight, false, -1,
+ this, false);
+
+ // TODO Perhaps split first, then add box ascent and descent.
+ if (height != -1)
+ splitHeightFun (height, &requisition->ascent, &requisition->descent);
+ if (minHeight != -1 &&
+ requisition->ascent + requisition->descent < minHeight)
+ splitHeightFun (minHeight, &requisition->ascent,
+ &requisition->descent);
+ if (maxHeight != -1 &&
+ requisition->ascent + requisition->descent > maxHeight)
+ splitHeightFun (maxHeight, &requisition->ascent,
+ &requisition->descent);
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
+ requisition->width, requisition->ascent,
+ requisition->descent);
+ DBG_OBJ_LEAVE ();
}
-void Widget::setDescent (int descent)
+void Widget::correctExtremesOfChild (Widget *child, Extremes *extremes)
{
+ // See comment in correctRequisitionOfChild.
+
+ DBG_OBJ_ENTER ("resize", 0, "correctExtremesOfChild",
+ "%p, %d (%d) / %d (%d)",
+ child, extremes->minWidth, extremes->minWidthIntrinsic,
+ extremes->maxWidth, extremes->maxWidthIntrinsic);
+
+ // See comment in Widget::getAvailWidthOfChild.
+ Widget *effContainer = child->quasiParent ? child->quasiParent :
+ (child->container ? child->container : child->parent);
+
+ if (effContainer == this) {
+ int limitMinWidth = child->getMinWidth (extremes, false);
+ int width = child->calcWidth (child->getStyle()->width, -1, this,
+ limitMinWidth, false);
+ int minWidth = child->calcWidth (child->getStyle()->minWidth, -1, this,
+ limitMinWidth, false);
+ int maxWidth = child->calcWidth (child->getStyle()->maxWidth, -1, this,
+ limitMinWidth, false);
+
+ DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d",
+ width, minWidth, maxWidth);
+
+ if (width != -1)
+ extremes->minWidth = extremes->maxWidth = width;
+ if (minWidth != -1)
+ extremes->minWidth = minWidth;
+ if (maxWidth != -1)
+ extremes->maxWidth = maxWidth;
+ } else {
+ DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container");
+ DBG_OBJ_MSG_START ();
+ effContainer->correctExtremesOfChild (child, extremes);
+ DBG_OBJ_MSG_END ();
+ }
+
+
+ DBG_OBJ_MSGF ("resize", 1, "=> %d / %d",
+ extremes->minWidth, extremes->maxWidth);
+ DBG_OBJ_LEAVE ();
+}
+
+/**
+ * \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()
+{
+}
+
+bool Widget::isBlockLevel ()
+{
+ // Most widgets are not block-level.
+ return false;
+}
+
+bool Widget::isPossibleContainer ()
+{
+ // In most (all?) cases identical to:
+ return isBlockLevel ();
}
bool Widget::buttonPressImpl (EventButton *event)
@@ -656,7 +1720,7 @@ bool Widget::motionNotifyImpl (EventMotion *event)
void Widget::enterNotifyImpl (EventCrossing *)
{
- core::style::Tooltip *tooltip = getStyle()->x_tooltip;
+ style::Tooltip *tooltip = getStyle()->x_tooltip;
if (tooltip)
tooltip->onEnter();
@@ -664,7 +1728,7 @@ void Widget::enterNotifyImpl (EventCrossing *)
void Widget::leaveNotifyImpl (EventCrossing *)
{
- core::style::Tooltip *tooltip = getStyle()->x_tooltip;
+ style::Tooltip *tooltip = getStyle()->x_tooltip;
if (tooltip)
tooltip->onLeave();
@@ -676,5 +1740,25 @@ void Widget::removeChild (Widget *child)
misc::assertNotReached ();
}
+// ----------------------------------------------------------------------
+
+void splitHeightPreserveAscent (int height, int *ascent, int *descent)
+{
+ *descent = height - *ascent;
+ if (*descent < 0) {
+ *descent = 0;
+ *ascent = height;
+ }
+}
+
+void splitHeightPreserveDescent (int height, int *ascent, int *descent)
+{
+ *ascent = height - *descent;
+ if (*ascent < 0) {
+ *ascent = 0;
+ *descent = height;
+ }
+}
+
} // namespace core
} // namespace dw
diff --git a/dw/widget.hh b/dw/widget.hh
index 34b35efa..0f3e2d37 100644
--- a/dw/widget.hh
+++ b/dw/widget.hh
@@ -27,51 +27,53 @@ class Widget: public lout::identity::IdentifiableObject
protected:
enum Flags {
/**
- * \brief Set, when dw::core::Widget::requisition is not up to date
- * anymore.
+ * \todo Comment this.
*/
- NEEDS_RESIZE = 1 << 0,
+ RESIZE_QUEUED = 1 << 0,
/**
- * \brief Only used internally, set to enforce size allocation.
- *
- * (I've forgotten the case, for which this is necessary.)
+ * \todo Comment this.
*/
- NEEDS_ALLOCATE = 1 << 1,
+ EXTREMES_QUEUED = 1 << 1,
/**
- * \brief Set, when dw::core::Widget::extremes is not up to date
+ * \brief Set, when dw::core::Widget::requisition is not up to date
* anymore.
+ *
+ * \todo Update, see RESIZE_QUEUED.
*/
- EXTREMES_CHANGED = 1 << 2,
+ NEEDS_RESIZE = 1 << 2,
/**
- * \brief Set by the widget itself (in the constructor), when set...
- * methods are implemented.
+ * \brief Only used internally, set to enforce size allocation.
*
- * Will hopefully be removed, after redesigning the size model.
+ * 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.
*/
- USES_HINTS = 1 << 3,
+ NEEDS_ALLOCATE = 1 << 3,
/**
- * \brief Set by the widget itself (in the constructor), when it contains
- * some contents, e.g. an image, as opposed to a horizontal ruler.
- *
- * Will hopefully be removed, after redesigning the size model.
+ * \todo Comment this.
*/
- HAS_CONTENTS = 1 << 4,
+ ALLOCATE_QUEUED = 1 << 4,
/**
- * \brief Set, when a widget was already once allocated,
+ * \brief Set, when dw::core::Widget::extremes is not up to date
+ * anymore.
*
- * The dw::Image widget uses this flag, see dw::Image::setBuffer.
+ * \todo Update, see RESIZE_QUEUED.
*/
- WAS_ALLOCATED = 1 << 5,
+ EXTREMES_CHANGED = 1 << 5,
/**
- * \brief Set for block-level widgets (as opposed to inline widgets)
+ * \brief Set, when a widget was already once allocated,
+ *
+ * The dw::Image widget uses this flag, see dw::Image::setBuffer.
*/
- BLOCK_LEVEL = 1 << 6,
+ WAS_ALLOCATED = 1 << 6,
};
/**
@@ -97,10 +99,32 @@ protected:
WidgetImgRenderer *widgetImgRenderer;
private:
+ static bool adjustMinWidth;
+
/**
* \brief The parent widget, NULL for top-level widgets.
*/
Widget *parent;
+
+ /**
+ * \brief ...
+ */
+ Widget *quasiParent;
+
+ /**
+ * \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;
+
+ /**
+ * \brief The containing widget, equivalent to the "containing
+ * block" defined by CSS. May be NULL, in this case the viewport
+ * is used.
+ */
+ Widget *container;
+
style::Style *style;
Flags flags;
@@ -133,6 +157,11 @@ private:
*/
bool buttonSensitiveSet;
+ void queueResize (int ref, bool extremesChanged, bool fast);
+ inline void queueResizeFast (int ref, bool extremesChanged)
+ { queueResize (ref, extremesChanged, true); }
+ void actualQueueResize (int ref, bool extremesChanged, bool fast);
+
public:
/**
* \brief This value is defined by the parent widget, and used for
@@ -158,16 +187,79 @@ protected:
Layout *layout;
- inline void setFlags (Flags f) { flags = (Flags)(flags | f); }
- inline void unsetFlags (Flags f) { flags = (Flags)(flags & ~f); }
+ /**
+ * \brief Space around the margin box. Allocation is extraSpace +
+ * margin + border + padding + contents;
+ */
+ style::Box extraSpace;
+
+ /*inline void printFlags () {
+ DBG_IF_RTFL {
+ char buf[10 * 3 - 1 + 1];
+ snprintf (buf, sizeof (buf), "%s:%s:%s:%s:%s:%s:%s",
+ (flags & RESIZE_QUEUED) ? "Rq" : "--",
+ (flags & EXTREMES_QUEUED) ? "Eq" : "--",
+ (flags & NEEDS_RESIZE) ? "nR" : "--",
+ (flags & NEEDS_ALLOCATE) ? "nA" : "--",
+ (flags & ALLOCATE_QUEUED) ? "Aq" : "--",
+ (flags & EXTREMES_CHANGED) ? "Ec" : "--",
+ (flags & WAS_ALLOCATED) ? "wA" : "--");
+ DBG_OBJ_SET_SYM ("flags", buf);
+ }
+ }*/
+
+ inline void printFlag (Flags f) {
+ DBG_IF_RTFL {
+ switch (f) {
+ case RESIZE_QUEUED:
+ DBG_OBJ_SET_SYM ("flags.RESIZE_QUEUED",
+ (flags & RESIZE_QUEUED) ? "true" : "false");
+ break;
+
+ case EXTREMES_QUEUED:
+ DBG_OBJ_SET_SYM ("flags.EXTREMES_QUEUED",
+ (flags & EXTREMES_QUEUED) ? "true" : "false");
+ break;
+
+ case NEEDS_RESIZE:
+ DBG_OBJ_SET_SYM ("flags.NEEDS_RESIZE",
+ (flags & NEEDS_RESIZE) ? "true" : "false");
+ break;
+
+ case NEEDS_ALLOCATE:
+ DBG_OBJ_SET_SYM ("flags.NEEDS_ALLOCATE",
+ (flags & NEEDS_ALLOCATE) ? "true" : "false");
+ break;
+
+ case ALLOCATE_QUEUED:
+ DBG_OBJ_SET_SYM ("flags.ALLOCATE_QUEUED",
+ (flags & ALLOCATE_QUEUED) ? "true" : "false");
+ break;
+
+ case EXTREMES_CHANGED:
+ DBG_OBJ_SET_SYM ("flags.EXTREMES_CHANGED",
+ (flags & EXTREMES_CHANGED) ? "true" : "false");
+ break;
+
+ case WAS_ALLOCATED:
+ DBG_OBJ_SET_SYM ("flags.WAS_ALLOCATED",
+ (flags & WAS_ALLOCATED) ? "true" : "false");
+ break;
+ }
+ }
+ }
+
+ inline void setFlags (Flags f)
+ { flags = (Flags)(flags | f); printFlag (f); }
+ inline void unsetFlags (Flags f)
+ { flags = (Flags)(flags & ~f); printFlag (f); }
inline void queueDraw ()
- {
- queueDrawArea (0, 0, allocation.width, getHeight());
- }
+ { queueDrawArea (0, 0, allocation.width, getHeight()); }
void queueDrawArea (int x, int y, int width, int height);
- void queueResize (int ref, bool extremesChanged);
+ inline void queueResize (int ref, bool extremesChanged)
+ { queueResize (ref, extremesChanged, false); }
/**
* \brief See \ref dw-widget-sizes.
@@ -177,7 +269,7 @@ protected:
/**
* \brief See \ref dw-widget-sizes.
*/
- virtual void getExtremesImpl (Extremes *extremes);
+ virtual void getExtremesImpl (Extremes *extremes) = 0;
/**
* \brief See \ref dw-widget-sizes.
@@ -200,6 +292,29 @@ protected:
*/
virtual void markExtremesChange (int ref);
+ int getMinWidth (Extremes *extremes, bool forceValue);
+
+ virtual int getAvailWidthOfChild (Widget *child, bool forceValue);
+ virtual int getAvailHeightOfChild (Widget *child, bool forceValue);
+ virtual void correctRequisitionOfChild (Widget *child,
+ Requisition *requisition,
+ void (*splitHeightFun) (int, int*,
+ int*));
+ void correctReqWidthOfChild (Widget *child, Requisition *requisition);
+ void correctReqHeightOfChild (Widget *child, Requisition *requisition,
+ void (*splitHeightFun) (int, int*, int*));
+ virtual void correctExtremesOfChild (Widget *child, Extremes *extremes);
+
+ virtual void containerSizeChangedForChildren ();
+
+ virtual bool affectedByContainerSizeChange ();
+ virtual bool affectsSizeChangeContainerChild (Widget *child);
+ virtual bool usesAvailWidth ();
+ virtual bool usesAvailHeight ();
+
+ virtual void notifySetAsTopLevel();
+ virtual void notifySetParent();
+
virtual bool buttonPressImpl (EventButton *event);
virtual bool buttonReleaseImpl (EventButton *event);
virtual bool motionNotifyImpl (EventMotion *event);
@@ -216,7 +331,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,32 +364,87 @@ 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;
+ inline static void setAdjustMinWidth (bool adjustMinWidth)
+ { Widget::adjustMinWidth = adjustMinWidth; }
+
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; }
- inline bool hasContents () { return flags & HAS_CONTENTS; }
- inline bool blockLevel () { return flags & BLOCK_LEVEL; }
void setParent (Widget *parent);
+ void setQuasiParent (Widget *quasiParent);
+
+ void setGenerator (Widget *generator) { this->generator = generator; }
inline style::Style *getStyle () { return style; }
/** \todo I do not like this. */
inline Allocation *getAllocation () { return &allocation; }
+ inline int boxOffsetX ()
+ { return extraSpace.left + getStyle()->boxOffsetX (); }
+ inline int boxRestWidth ()
+ { return extraSpace.right + getStyle()->boxRestWidth (); }
+ inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); }
+ inline int boxOffsetY ()
+ { return extraSpace.top + getStyle()->boxOffsetY (); }
+ inline int boxRestHeight ()
+ { return extraSpace.bottom + getStyle()->boxRestHeight (); }
+ inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); }
+
void sizeRequest (Requisition *requisition);
void getExtremes (Extremes *extremes);
void sizeAllocate (Allocation *allocation);
- virtual void setWidth (int width);
- virtual void setAscent (int ascent);
- virtual void setDescent (int descent);
+
+ int getAvailWidth (bool forceValue);
+ int getAvailHeight (bool forceValue);
+ virtual bool getAdjustMinWidth () { return Widget::adjustMinWidth; }
+ void correctRequisition (Requisition *requisition,
+ void (*splitHeightFun) (int, int*, int*));
+ void correctExtremes (Extremes *extremes);
+ int calcWidth (style::Length cssValue, int refWidth, Widget *refWidget,
+ int limitMinWidth, bool forceValue);
+ void calcFinalWidth (style::Style *style, int refWidth, Widget *refWidget,
+ int limitMinWidth, bool forceValue, int *finalWidth);
+ int calcHeight (style::Length cssValue, bool usePercentage, int refHeight,
+ Widget *refWidget, bool forceValue);
+
+ virtual int applyPerWidth (int containerWidth, style::Length perWidth);
+ virtual int applyPerHeight (int containerHeight, style::Length perHeight);
+
+ virtual bool isBlockLevel ();
+ virtual bool isPossibleContainer ();
+
+ void containerSizeChanged ();
bool intersects (Rectangle *area, Rectangle *intersection);
@@ -300,10 +470,14 @@ public:
inline bool isButtonSensitive () { return buttonSensitive; }
inline Widget *getParent () { return parent; }
+ inline Widget *getContainer () { return container; }
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);
@@ -330,6 +504,9 @@ public:
virtual void removeChild (Widget *child);
};
+void splitHeightPreserveAscent (int height, int *ascent, int *descent);
+void splitHeightPreserveDescent (int height, int *ascent, int *descent);
+
} // namespace core
} // namespace dw
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..366a58fa 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,13 @@ Vector::~Vector()
{
clear();
free(array);
+
+ DBG_OBJ_DELETE ();
+}
+
+int Vector::size ()
+{
+ return numElements;
}
void Vector::put(Object *newElement, int newPos)
@@ -188,9 +198,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 +213,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()
@@ -276,6 +301,32 @@ List::~List()
clear();
}
+int List::size ()
+{
+ return numElements;
+}
+
+bool List::equals(Object *other)
+{
+ List *otherList = (List*)other;
+ Node *node1 = first, *node2 = otherList->first;
+ while (node1 != NULL && node2 != NULL ) {
+ if (!node1->object->equals (node2->object))
+ return false;
+ node1 = node1->next;
+ node2 = node2->next;
+ }
+ return node1 == NULL && node2 == NULL;
+}
+
+int List::hashValue()
+{
+ int h = 0;
+ for (Node *node = first; node; node = node->next)
+ h = h ^ node->object->hashValue ();
+ return h;
+}
+
void List::clear()
{
while (first) {
@@ -305,6 +356,28 @@ void List::append(Object *element)
numElements++;
}
+bool List::insertBefore(object::Object *beforeThis, object::Object *neew)
+{
+ Node *beforeCur, *cur;
+
+ for (beforeCur = NULL, cur = first; cur; beforeCur = cur, cur = cur->next) {
+ if (cur->object == beforeThis) {
+ Node *newNode = new Node;
+ newNode->next = cur;
+ newNode->object = neew;
+
+ if (beforeCur)
+ beforeCur->next = newNode;
+ else
+ first = newNode;
+
+ numElements++;
+ return true;
+ }
+ }
+
+ return false;
+}
bool List::remove0(Object *element, bool compare, bool doNotDeleteAtAll)
{
@@ -372,6 +445,8 @@ HashSet::HashSet(bool ownerOfObjects, int tableSize)
table = new Node*[tableSize];
for (int i = 0; i < tableSize; i++)
table[i] = NULL;
+
+ numElements = 0;
}
HashSet::~HashSet()
@@ -399,6 +474,11 @@ HashSet::~HashSet()
delete[] table;
}
+int HashSet::size ()
+{
+ return numElements;
+}
+
HashSet::Node *HashSet::createNode()
{
return new Node;
@@ -427,13 +507,15 @@ HashSet::Node *HashSet::insertNode(Object *object)
{
// Look whether object is already contained.
Node *node = findNode(object);
- if (node)
+ if (node) {
clearNode(node);
- else {
+ numElements--;
+ } else {
int h = calcHashValue(object);
node = createNode ();
node->next = table[h];
table[h] = node;
+ numElements++;
}
node->object = object;
@@ -471,6 +553,7 @@ bool HashSet::remove(Object *object)
clearNode (cur);
delete cur;
+ numElements--;
return true;
}
@@ -642,6 +725,11 @@ Stack::~Stack()
pop ();
}
+int Stack::size ()
+{
+ return numElements;
+}
+
void Stack::push (object::Object *object)
{
Node *newTop = new Node ();
diff --git a/lout/container.hh b/lout/container.hh
index 14803140..3051970e 100644
--- a/lout/container.hh
+++ b/lout/container.hh
@@ -93,6 +93,7 @@ class Collection: public Collection0
public:
void intoStringBuffer(misc::StringBuffer *sb);
inline Iterator iterator() { Iterator it(createIterator()); return it; }
+ virtual int size() = 0;
};
@@ -128,6 +129,8 @@ public:
Vector(int initSize, bool ownerOfObjects);
~Vector();
+ int size();
+
void put(object::Object *newElement, int newPos = -1);
void insert(object::Object *newElement, int pos);
@@ -137,16 +140,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 int insertSorted(object::Object *newElement,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { int pos = bsearch (newElement, false, comparator);
+ insert (newElement, pos); return pos; }
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); }
};
@@ -188,8 +198,14 @@ public:
List(bool ownerOfObjects);
~List();
+ bool equals(Object *other);
+ int hashValue();
+
+ int size ();
+
void clear();
void append(object::Object *element);
+ bool insertBefore(object::Object *beforeThis, object::Object *neew);
inline bool removeRef(object::Object *element)
{ return remove0(element, false, false); }
inline bool remove(object::Object *element)
@@ -218,7 +234,7 @@ protected:
};
Node **table;
- int tableSize;
+ int tableSize, numElements;
bool ownerOfObjects;
inline int calcHashValue(object::Object *object) const
@@ -254,6 +270,8 @@ public:
HashSet(bool ownerOfObjects, int tableSize = 251);
~HashSet();
+ int size ();
+
void put (object::Object *object);
bool contains (object::Object *key) const;
bool remove (object::Object *key);
@@ -288,7 +306,8 @@ public:
};
/**
- * \brief A stack (LIFO).
+ * \brief A stack (LIFO). Can be used as Queue (FIFO) when pushUnder()
+ * is used instead of push().
*
* Note that the iterator returns all elements in the reversed order they have
* been put on the stack.
@@ -326,6 +345,8 @@ public:
Stack (bool ownerOfObjects);
~Stack();
+ int size ();
+
void push (object::Object *object);
void pushUnder (object::Object *object);
inline object::Object *getTop () const { return top ? top->object : NULL; }
@@ -385,11 +406,16 @@ public:
Collection () { this->base = NULL; }
~Collection () { if (this->base) delete this->base; }
+ bool equals(Object *other)
+ { return this->base->equals (((Collection<T>*)other)->base); }
+
+ int hashValue() { return this->base->hashValue (); }
+
void intoStringBuffer(misc::StringBuffer *sb)
{ this->base->intoStringBuffer(sb); }
-
inline Iterator<T> iterator() {
Iterator<T> it; it.base = this->base->iterator(); return it; }
+ inline int size() { return this->base->size (); }
};
@@ -406,16 +432,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 int insertSorted(T *newElement,
+ object::Comparator *comparator =
+ &object::standardComparator)
+ { return ((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); }
};
@@ -431,6 +469,8 @@ public:
inline void clear() { ((untyped::List*)this->base)->clear(); }
inline void append(T *element)
{ ((untyped::List*)this->base)->append(element); }
+ inline bool insertBefore(object::Object *beforeThis, object::Object *neew)
+ { return ((untyped::List*)this->base)->insertBefore(beforeThis, neew); }
inline bool removeRef(T *element) {
return ((untyped::List*)this->base)->removeRef(element); }
inline bool remove(T *element) {
@@ -438,7 +478,6 @@ public:
inline bool detachRef(T *element) {
return ((untyped::List*)this->base)->detachRef(element); }
- inline int size() const { return ((untyped::List*)this->base)->size(); }
inline bool isEmpty() const
{ return ((untyped::List*)this->base)->isEmpty(); }
inline T *getFirst() const
@@ -501,7 +540,6 @@ public:
inline T *getTop () const
{ return (T*)((untyped::Stack*)this->base)->getTop (); }
inline void pop () { ((untyped::Stack*)this->base)->pop (); }
- inline int size() const { return ((untyped::Stack*)this->base)->size(); }
};
} // namespace untyped
diff --git a/lout/debug.hh b/lout/debug.hh
index 083234f8..e2839196 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 { \
@@ -30,7 +32,7 @@
/*
- * See <http://www.dillo.org/~sgeerken/rtfl/>.
+ * See <http://home.gna.org/rtfl/>.
*/
#ifdef DBG_RTFL
@@ -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,56 @@
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_ENTER0(aspect, prio, funname) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:\n", \
+ RTFL_PREFIX_ARGS, this, aspect, prio, funname); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:\n", \
+ RTFL_PREFIX_ARGS, obj, aspect, prio, funname); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) \
+ D_STMT_START { \
+ fflush (stdout); \
+ printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:" fmt "\n", \
+ RTFL_PREFIX_ARGS, this, aspect, prio, funname, __VA_ARGS__); \
+ } D_STMT_END
+
+#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) \
+ D_STMT_START { \
+ fflush (stdout); \
+ printf (RTFL_PREFIX_FMT "obj-enter:%p:%s:%d:%s:" fmt "\n", \
+ RTFL_PREFIX_ARGS, obj, aspect, prio, funname, __VA_ARGS__); \
+ } D_STMT_END
+
+#define DBG_OBJ_LEAVE() \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-leave:%p\n", \
+ RTFL_PREFIX_ARGS, this); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_LEAVE_O(obj) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-leave:%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 +203,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", \
@@ -138,28 +231,105 @@
fflush (stdout); \
} D_STMT_END
+#define DBG_OBJ_SET_PTR_O(obj, var, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%p\n", \
+ RTFL_PREFIX_ARGS, obj, var, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_SET_BOOL(var, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \
+ RTFL_PREFIX_ARGS, this, var, val ? "true" : "false"); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_SET_BOOL_O(obj, var, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \
+ RTFL_PREFIX_ARGS, obj, var, val ? "true" : "false"); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_SET_COL(var, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s:#%06x\n", \
+ RTFL_PREFIX_ARGS, this, var, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
#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_BOOL(var, ind, val) \
+ D_STMT_START { \
+ printf (RTFL_PREFIX_FMT "obj-set:%p:%s.%d:%s\n", \
+ RTFL_PREFIX_ARGS, this, var, ind, val ? "true" : "false"); \
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_BOOL(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 ? "true" : "false"); \
+ 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
-#define DBG_OBJ_COLOR(color, klass) \
+#define DBG_OBJ_COLOR(klass, color) \
D_STMT_START { \
printf (RTFL_PREFIX_FMT "obj-color:%s:%s\n", \
RTFL_PREFIX_ARGS, color, klass); \
@@ -168,23 +338,48 @@
#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_ENTER0(aspect, prio, funname) D_STMT_NOP
+#define DBG_OBJ_ENTER0_O(aspect, prio, obj, funname) D_STMT_NOP
+#define DBG_OBJ_ENTER(aspect, prio, funname, fmt, ...) D_STMT_NOP
+#define DBG_OBJ_ENTER_O(aspect, prio, obj, funname, fmt, ...) D_STMT_NOP
+#define DBG_OBJ_LEAVE() D_STMT_NOP
+#define DBG_OBJ_LEAVE_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_SET_PTR_O(obj, var, val) D_STMT_NOP
+#define DBG_OBJ_SET_BOOL(var, val) D_STMT_NOP
+#define DBG_OBJ_SET_BOOL_O(obj, var, val) D_STMT_NOP
+#define DBG_OBJ_SET_COL(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_ARRSET_BOOL(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_ARRATTRSET_BOOL(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..b362fc2f 100644
--- a/lout/misc.hh
+++ b/lout/misc.hh
@@ -223,6 +223,14 @@ public:
assert (i >= 0 && this->num - i > 0);
this->array[i] = t;
}
+
+ /**
+ * \brief Store an object at the end of the vector.
+ */
+ inline void setLast (T t) {
+ assert (this->num > 0);
+ this->array[this->num - 1] = t;
+ }
};
/**
@@ -379,7 +387,7 @@ public:
this->startExtra = index;
resizeExtra ();
} else {
- if (index < startExtra) {
+ if (index < startExtra) {
consolidate ();
insert (index, numInsert);
} else if (index < startExtra + numExtra) {
@@ -421,14 +429,29 @@ 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) {
+ // The original assertion
+ ///
+ // "assert (i < this->numMain + this->numExtra)"
+ //
+ // causes this warnung in dw::Textblock::breakAdded:
+ //
+ // "assuming signed overflow does not occur when assuming that
+ // (X - c) > X is always false [-Wstrict-overflow]"
+ //
+ // Subtracting numExtra from both sides solves this,
+ // interrestingly.
+
+ assert (i - this->numExtra < this->numMain);
return this->arrayMain + i - this->numExtra;
- else
+ } else
return this->arrayExtra1 + i - this->startExtra;
}
}
@@ -485,6 +508,13 @@ public:
inline void set (int i, T t) {
*(this->getRef(i)) = t;
}
+
+ /**
+ * \brief Store an object at the end of the vector.
+ */
+ inline void setLast (T t) {
+ *(this->getLastRef()) = t;
+ }
};
/**
@@ -515,6 +545,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..e4e0152a 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
// -------------
@@ -194,6 +211,32 @@ int Integer::compareTo(Comparable *other)
return value - ((Integer*)other)->value;
}
+// -------------
+// Boolean
+// -------------
+
+bool Boolean::equals(Object *other)
+{
+ bool value2 = ((Boolean*)other)->value;
+ // TODO Does "==" work?
+ return (value && value2) || (!value && value2);
+}
+
+int Boolean::hashValue()
+{
+ return value ? 1 : 0;
+}
+
+void Boolean::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append(value ? "true" : "false");
+}
+
+int Boolean::compareTo(Comparable *other)
+{
+ return (value ? 1 : 0) - (((Boolean*)other)->value ? 1 : 0);
+}
+
// -----------------
// ConstString
// -----------------
diff --git a/lout/object.hh b/lout/object.hh
index fd612863..3ba7b590 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.
*/
@@ -104,6 +138,23 @@ public:
/**
+ * \brief An object::Object wrapper for bool's.
+ */
+class Boolean: public Comparable
+{
+ bool value;
+
+public:
+ Boolean(bool value) { this->value = value; }
+ bool equals(Object *other);
+ int hashValue();
+ void intoStringBuffer(misc::StringBuffer *sb);
+ int compareTo(Comparable *other);
+ inline bool getValue() { return value; }
+};
+
+
+/**
* \brief An object::Object wrapper for constant strings (char*).
*
* As opposed to object::String, the char array is not copied.
diff --git a/lout/unicode.cc b/lout/unicode.cc
index 4f0f0b3b..9fc2f3d3 100644
--- a/lout/unicode.cc
+++ b/lout/unicode.cc
@@ -1,3 +1,23 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2012, 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 "unicode.hh"
#include "misc.hh"
diff --git a/src/IO/IO.c b/src/IO/IO.c
index a0a8bba5..0addf486 100644
--- a/src/IO/IO.c
+++ b/src/IO/IO.c
@@ -298,6 +298,8 @@ static void IO_fd_write_cb(int fd, void *data)
} else {
if (IO_callback(io) == 0)
a_IOwatch_remove_fd(fd, DIO_WRITE);
+ if (io->Status)
+ a_IO_ccc(OpAbort, 1, FWD, io->Info, NULL, NULL);
}
}
@@ -350,6 +352,7 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
switch (Op) {
case OpStart:
io = IO_new(IOWrite);
+ io->Info = Info;
Info->LocalKey = io;
break;
case OpSend:
@@ -384,6 +387,13 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
} else { /* 1 FWD */
/* Write-data status */
switch (Op) {
+ case OpAbort:
+ io = Info->LocalKey;
+ IO_close_fd(io, IO_StopRdWr);
+ IO_free(io);
+ a_Chain_fcb(OpAbort, Info, NULL, NULL);
+ dFree(Info);
+ break;
default:
MSG_WARN("Unused CCC\n");
break;
@@ -406,9 +416,10 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
IO_submit(io);
}
break;
+ case OpEnd:
case OpAbort:
io = Info->LocalKey;
- IO_close_fd(io, IO_StopRdWr);
+ IO_close_fd(io, Op == OpEnd ? IO_StopRd : IO_StopRdWr);
IO_free(io);
dFree(Info);
break;
diff --git a/src/IO/about.c b/src/IO/about.c
index 5ffe7dff..0cc3b427 100644
--- a/src/IO/about.c
+++ b/src/IO/about.c
@@ -229,29 +229,32 @@ const char *const AboutSplash=
"<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
"<tr>\n"
" <td bgcolor='#CCCCCC'>\n"
-" <h4>Release overview</h4>\n"
-" December 24, 2014\n"
+" <h4>Notes</h4>\n"
"<tr>\n"
" <td bgcolor='#FFFFFF'>\n"
" <table border='0' cellspacing='0' cellpadding='5'>\n"
" <tr>\n"
" <td>\n"
-"<p>\n"
-"The dillo-3.0.4.1 release brings you fixes:\n"
"<ul>\n"
-"<li> for linking with the recently-released fltk-1.3.3\n"
-" (we don't use <tt>fl_oldfocus</tt> anymore).\n"
-"<li> to make sure that windows are resizable with fltk-1.3.3.\n"
-"<li> not to load background images, or follow redirections or meta refresh,\n"
-" in <tt>--local</tt> mode (security).\n"
-"<li> to permit linking on OS X (remove our <tt>Fl_Printer</tt> stub).\n"
-"<li> for a crash when searching from the address bar and no search urls are\n"
-" found in dillorc.\n"
+" <li> There's a\n"
+" <a href='http://www.dillo.org/dillorc'>dillorc</a>\n"
+" (readable config) file inside the tarball. It is well-commented\n"
+" and has plenty of options to customize dillo, so <STRONG>copy\n"
+" it</STRONG> to your <STRONG>~/.dillo/</STRONG> directory, and\n"
+" modify it to your taste.\n"
+" <li> The right mouse button brings up a context-sensitive menu\n"
+" (available on pages, links, images, forms, the Back and Forward buttons,\n"
+" and the bug meter).\n"
+" <li> Cookies are disabled by default for privacy. To log into certain\n"
+" sites, you may need to <a href='http://www.dillo.org/Cookies.txt'>enable\n"
+" cookies selectively</a>.\n"
+" <li> Frames, Java and Javascript are not supported.\n"
+" <li> This release is mainly intended for <strong>developers</strong>\n"
+" and <strong>advanced users</strong>.\n"
+" <li> Documentation for developers is in the <CODE>/doc</CODE>\n"
+" dir inside the tarball; you can find directions on everything\n"
+" else at the home page.\n"
"</ul>\n"
-"<p>\n"
-"...that shouldn't have to wait until dillo-3.1 is ready with its floating\n"
-"elements and assorted good things.\n"
-"<p>\n"
" </table>\n"
"</table>\n"
"</table>\n"
@@ -262,32 +265,29 @@ const char *const AboutSplash=
"<table border='0' cellpadding='5' cellspacing='1' width='100%'>\n"
"<tr>\n"
" <td bgcolor='#CCCCCC'>\n"
-" <h4>Notes</h4>\n"
+" <h4>Release overview</h4>\n"
+" December 24, 2014\n"
"<tr>\n"
" <td bgcolor='#FFFFFF'>\n"
" <table border='0' cellspacing='0' cellpadding='5'>\n"
" <tr>\n"
" <td>\n"
+"<p>\n"
+"The dillo-3.0.4.1 release brings you fixes:\n"
"<ul>\n"
-" <li> There's a\n"
-" <a href='http://www.dillo.org/dillorc'>dillorc</a>\n"
-" (readable config) file inside the tarball. It is well-commented\n"
-" and has plenty of options to customize dillo, so <STRONG>copy\n"
-" it</STRONG> to your <STRONG>~/.dillo/</STRONG> directory, and\n"
-" modify it to your taste.\n"
-" <li> Documentation for developers is in the <CODE>/doc</CODE>\n"
-" dir inside the tarball; you can find directions on everything\n"
-" else at the home page.\n"
-" <li> The right mouse button brings up a context-sensitive menu\n"
-" (available on pages, links, images, forms, the Back and Forward buttons,\n"
-" and the bug meter).\n"
-" <li> Dillo behaves very nicely when browsing local files, images, and HTML.\n"
-" It's also very good for Internet searching.\n"
-" <li> This release is mainly intended for <strong>developers</strong>\n"
-" and <strong>advanced users</strong>.\n"
-" <li> Frames, Java and Javascript are not supported.\n"
+"<li> for linking with the recently-released fltk-1.3.3\n"
+" (we don't use <tt>fl_oldfocus</tt> anymore).\n"
+"<li> to make sure that windows are resizable with fltk-1.3.3.\n"
+"<li> not to load background images, or follow redirections or meta refresh,\n"
+" in <tt>--local</tt> mode (security).\n"
+"<li> to permit linking on OS X (remove our <tt>Fl_Printer</tt> stub).\n"
+"<li> for a crash when searching from the address bar and no search urls are\n"
+" found in dillorc.\n"
"</ul>\n"
-"<br>\n"
+"<p>\n"
+"...that shouldn't have to wait until dillo-3.1 is ready with its floating\n"
+"elements and assorted good things.\n"
+"<p>\n"
" </table>\n"
"</table>\n"
"</table>\n"
diff --git a/src/IO/http.c b/src/IO/http.c
index a0021a9e..49b3a3ac 100644
--- a/src/IO/http.c
+++ b/src/IO/http.c
@@ -48,6 +48,8 @@ D_STMT_START { \
#define _MSG_BW(web, root, ...)
+static const int HTTP_PORT = 80;
+
static const int HTTP_SOCKET_USE_PROXY = 0x1;
static const int HTTP_SOCKET_QUEUED = 0x4;
static const int HTTP_SOCKET_TO_BE_FREED = 0x8;
@@ -67,28 +69,23 @@ typedef struct {
/* Data structures and functions to queue sockets that need to be
* delayed due to the per host connection limit.
*/
-typedef struct SocketQueueEntry {
- SocketData_t* sock;
- struct SocketQueueEntry *next ;
-} SocketQueueEntry_t;
-
-typedef struct {
- SocketQueueEntry_t *head;
- SocketQueueEntry_t *tail;
-} SocketQueue_t;
-
typedef struct {
char *host;
- int active_connections;
- SocketQueue_t queue;
+ int active_conns;
+ Dlist *queue;
} HostConnection_t;
-static void Http_socket_queue_init(SocketQueue_t *sq);
-static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock);
-static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq);
+typedef struct {
+ int fd;
+ int skey;
+} FdMapEntry_t;
+
+static void Http_socket_enqueue(HostConnection_t *hc, SocketData_t* sock);
+static SocketData_t* Http_socket_dequeue(HostConnection_t *hc);
static HostConnection_t *Http_host_connection_get(const char *host);
static void Http_host_connection_remove(HostConnection_t *hc);
static int Http_connect_socket(ChainLink *Info);
+static void Http_send_query(ChainLink *Info, SocketData_t *S);
static void Http_socket_free(int SKey);
/*
@@ -101,6 +98,11 @@ static char *HTTP_Proxy_Auth_base64 = NULL;
static char *HTTP_Language_hdr = NULL;
static Dlist *host_connections;
+/* TODO: If fd_map will stick around in its present form (FDs and SocketData_t)
+ * then consider whether having both this and ValidSocks is necessary.
+ */
+static Dlist *fd_map;
+
/*
* Initialize proxy vars and Accept-Language header
*/
@@ -125,6 +127,7 @@ int a_Http_init(void)
*/
host_connections = dList_new(5);
+ fd_map = dList_new(20);
return 0;
}
@@ -155,14 +158,39 @@ void a_Http_set_proxy_passwd(const char *str)
static int Http_sock_new(void)
{
SocketData_t *S = dNew0(SocketData_t, 1);
+ S->SockFD = -1;
return a_Klist_insert(&ValidSocks, S);
}
+/*
+ * Compare by FD.
+ */
+static int Http_fd_map_cmp(const void *v1, const void *v2)
+{
+ int fd = VOIDP2INT(v2);
+ const FdMapEntry_t *e = v1;
+
+ return (fd == e->fd) ? 0 : 1;
+}
+
+/*
+ * Remove and free entry from fd_map.
+ */
+static void Http_fd_map_remove_entry(int fd)
+{
+ void *data = dList_find_custom(fd_map, INT2VOIDP(fd), Http_fd_map_cmp);
+
+ if (data) {
+ dList_remove_fast(fd_map, data);
+ dFree(data);
+ }
+}
+
static void Http_connect_queued_sockets(HostConnection_t *hc)
{
SocketData_t *sd;
- while (hc->active_connections < prefs.http_max_conns &&
- (sd = Http_socket_dequeue(&hc->queue))) {
+ while (hc->active_conns < prefs.http_max_conns &&
+ (sd = Http_socket_dequeue(hc))) {
sd->flags &= ~HTTP_SOCKET_QUEUED;
@@ -177,8 +205,17 @@ static void Http_connect_queued_sockets(HostConnection_t *hc)
Http_socket_free(VOIDP2INT(Info->LocalKey)); /* free sd */
dFree(Info);
} else {
+ FdMapEntry_t *e = dNew0(FdMapEntry_t, 1);
+
+ e->fd = sd->SockFD;
+ e->skey = VOIDP2INT(sd->Info->LocalKey);
+ dList_append(fd_map, e);
+
+ hc->active_conns++;
+ a_Chain_bcb(OpSend, sd->Info, &sd->SockFD, "FD");
+ a_Chain_fcb(OpSend, sd->Info, &sd->SockFD, "FD");
+ Http_send_query(sd->Info, sd);
sd->connected_to = hc->host;
- hc->active_connections++;
}
}
}
@@ -197,11 +234,13 @@ static void Http_socket_free(int SKey)
if (S->flags & HTTP_SOCKET_QUEUED) {
S->flags |= HTTP_SOCKET_TO_BE_FREED;
} else {
+ if (S->SockFD != -1)
+ Http_fd_map_remove_entry(S->SockFD);
if (S->connected_to) {
HostConnection_t *hc = Http_host_connection_get(S->connected_to);
- hc->active_connections--;
+ hc->active_conns--;
Http_connect_queued_sockets(hc);
- if (hc->active_connections == 0)
+ if (hc->active_conns == 0)
Http_host_connection_remove(hc);
}
dFree(S);
@@ -275,6 +314,10 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester,
web_flags & WEB_Stylesheet ? "text/css,*/*;q=0.1" :
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
+ const char *connection_hdr_val =
+ (prefs.http_persistent_conns == TRUE &&
+ !dStrAsciiCasecmp(URL_SCHEME(url), "http")) ? "keep-alive" : "close";
+
if (use_proxy) {
dStr_sprintfa(request_uri, "%s%s",
URL_STR(url),
@@ -309,15 +352,15 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester,
"DNT: 1\r\n"
"%s" /* proxy auth */
"%s" /* referer */
- "Connection: close\r\n"
+ "Connection: %s\r\n"
"Content-Type: %s\r\n"
"Content-Length: %ld\r\n"
"%s" /* cookies */
"\r\n",
request_uri->str, URL_AUTHORITY(url), prefs.http_user_agent,
accept_hdr_value, HTTP_Language_hdr, auth ? auth : "",
- proxy_auth->str, referer, content_type->str, (long)URL_DATA(url)->len,
- cookies);
+ proxy_auth->str, referer, connection_hdr_val, content_type->str,
+ (long)URL_DATA(url)->len, cookies);
dStr_append_l(query, URL_DATA(url)->str, URL_DATA(url)->len);
dStr_free(content_type, TRUE);
} else {
@@ -333,13 +376,13 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester,
"DNT: 1\r\n"
"%s" /* proxy auth */
"%s" /* referer */
- "Connection: close\r\n"
+ "Connection: %s\r\n"
"%s" /* cache control */
"%s" /* cookies */
"\r\n",
request_uri->str, URL_AUTHORITY(url), prefs.http_user_agent,
accept_hdr_value, HTTP_Language_hdr, auth ? auth : "",
- proxy_auth->str, referer,
+ proxy_auth->str, referer, connection_hdr_val,
(URL_FLAGS(url) & URL_E2EQuery) ?
"Pragma: no-cache\r\nCache-Control: no-cache\r\n" : "",
cookies);
@@ -416,7 +459,7 @@ static int Http_connect_socket(ChainLink *Info)
struct sockaddr_in *sin = (struct sockaddr_in *)&name;
socket_len = sizeof(struct sockaddr_in);
sin->sin_family = dh->af;
- sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
+ sin->sin_port = S->port ? htons(S->port) : htons(HTTP_PORT);
memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
MSG("Connecting to %s\n", inet_ntoa(sin->sin_addr));
@@ -430,7 +473,7 @@ static int Http_connect_socket(ChainLink *Info)
socket_len = sizeof(struct sockaddr_in6);
sin6->sin6_family = dh->af;
sin6->sin6_port =
- S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
+ S->port ? htons(S->port) : htons(HTTP_PORT);
memcpy(&sin6->sin6_addr, dh->data, dh->alen);
inet_ntop(dh->af, dh->data, buf, sizeof(buf));
if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
@@ -447,9 +490,6 @@ static int Http_connect_socket(ChainLink *Info)
dClose(S->SockFD);
MSG("Http_connect_socket ERROR: %s\n", dStrerror(S->Err));
} else {
- a_Chain_bcb(OpSend, Info, &S->SockFD, "FD");
- a_Chain_fcb(OpSend, Info, &S->SockFD, "FD");
- Http_send_query(S->Info, S);
return 0; /* Success */
}
}
@@ -491,7 +531,6 @@ static int Http_must_use_proxy(const DilloUrl *url)
/*
* Return a new string for the request used to tunnel HTTPS through a proxy.
- * As of 2009, the best reference appears to be section 5 of RFC 2817.
*/
char *a_Http_make_connect_str(const DilloUrl *url)
{
@@ -565,11 +604,11 @@ static void Http_dns_cb(int Status, Dlist *addr_list, void *data)
hc = Http_host_connection_get(URL_HOST(HTTP_Proxy));
else
hc = Http_host_connection_get(URL_HOST(S->web->url));
- Http_socket_enqueue(&hc->queue, S);
+ Http_socket_enqueue(hc, S);
Http_connect_queued_sockets(hc);
} else {
/* DNS wasn't able to resolve the hostname */
- MSG_BW(S->web, 0, "ERROR: Dns can't resolve %s",
+ MSG_BW(S->web, 0, "ERROR: DNS can't resolve %s",
(S->flags & HTTP_SOCKET_USE_PROXY) ? URL_HOST_(HTTP_Proxy) :
URL_HOST_(S->web->url));
a_Chain_bfcb(OpAbort, S->Info, NULL, "Both");
@@ -620,6 +659,46 @@ static int Http_get(ChainLink *Info, void *Data1)
}
/*
+ * If any entry in the socket data queue can reuse our connection, set it up
+ * and send off a new query.
+ */
+static void Http_socket_reuse(int SKey)
+{
+ SocketData_t *new_sd, *old_sd = a_Klist_get_data(ValidSocks, SKey);
+ HostConnection_t *hc = Http_host_connection_get(old_sd->connected_to);
+ int i, n = dList_length(hc->queue);
+
+ for (i = 0; i < n; i++) {
+ new_sd = dList_nth_data(hc->queue, i);
+
+ if (a_Web_valid(new_sd->web) && old_sd->port == new_sd->port) {
+ new_sd->SockFD = old_sd->SockFD;
+ Http_fd_map_remove_entry(old_sd->SockFD);
+ a_Klist_remove(ValidSocks, SKey);
+ dFree(old_sd);
+
+ dList_remove(hc->queue, new_sd);
+ new_sd->flags &= ~HTTP_SOCKET_QUEUED;
+ FdMapEntry_t *e = dNew0(FdMapEntry_t, 1);
+ e->fd = new_sd->SockFD;
+ e->skey = VOIDP2INT(new_sd->Info->LocalKey);
+ dList_append(fd_map, e);
+
+ a_Chain_bcb(OpSend, new_sd->Info, &new_sd->SockFD, "FD");
+ a_Chain_fcb(OpSend, new_sd->Info, &new_sd->SockFD, "FD");
+ Http_send_query(new_sd->Info, new_sd);
+ new_sd->connected_to = hc->host;
+ return;
+ }
+ }
+ dClose(old_sd->SockFD);
+ Http_fd_map_remove_entry(old_sd->SockFD);
+ a_Klist_remove(ValidSocks, SKey);
+ hc->active_conns--;
+ dFree(old_sd);
+}
+
+/*
* CCC function for the HTTP module
*/
void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
@@ -648,7 +727,6 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
case OpEnd:
/* finished the HTTP query branch */
a_Chain_bcb(OpEnd, Info, NULL, NULL);
- Http_socket_free(SKey);
dFree(Info);
break;
case OpAbort:
@@ -659,51 +737,97 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
break;
}
} else { /* 1 FWD */
+ SocketData_t *sd;
/* HTTP send-query status branch */
switch (Op) {
+ case OpAbort:
+ if ((sd = a_Klist_get_data(ValidSocks, SKey)))
+ MSG_BW(sd->web, 1, "Can't get %s", URL_STR(sd->web->url));
+ a_Chain_fcb(OpAbort, Info, NULL, "Both");
+ Http_socket_free(SKey);
+ dFree(Info);
+ break;
default:
MSG_WARN("Unused CCC\n");
break;
}
}
+ } else if (Branch == 2) {
+ if (Dir == FWD) {
+ /* Receiving from server */
+ switch (Op) {
+ case OpSend:
+ /* Data1 = dbuf */
+ a_Chain_fcb(OpSend, Info, Data1, "send_page_2eof");
+ break;
+ case OpEnd:
+ a_Chain_fcb(OpEnd, Info, NULL, NULL);
+ Http_socket_free(SKey);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ } else { /* 2 BCK */
+ switch (Op) {
+ case OpStart:
+ a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 2, 2);
+ a_Chain_bcb(OpStart, Info, NULL, NULL); /* IORead */
+ break;
+ case OpSend:
+ if (Data2) {
+ if (!strcmp(Data2, "FD")) {
+ int fd = *(int*)Data1;
+ FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd),
+ Http_fd_map_cmp);
+ Info->LocalKey = INT2VOIDP(fme->skey);
+ a_Chain_bcb(OpSend, Info, Data1, Data2);
+ } else if (!strcmp(Data2, "reply_complete")) {
+ a_Chain_bfcb(OpEnd, Info, NULL, NULL);
+ Http_socket_reuse(SKey);
+ dFree(Info);
+ }
+ }
+ break;
+ case OpAbort:
+ a_Chain_bcb(OpAbort, Info, NULL, NULL);
+ dFree(Info);
+ break;
+ default:
+ MSG_WARN("Unused CCC\n");
+ break;
+ }
+ }
}
}
-
-static void Http_socket_queue_init(SocketQueue_t *sq)
-{
- sq->head = NULL;
- sq->tail = NULL;
-}
-
-static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock)
+/*
+ * Add socket data to the queue. Pages/stylesheets/etc. have higher priority
+ * than images.
+ */
+static void Http_socket_enqueue(HostConnection_t *hc, SocketData_t* sock)
{
- SocketQueueEntry_t *se = dNew(SocketQueueEntry_t, 1);
+ if ((sock->web->flags & WEB_Image) == 0) {
+ int i, n = dList_length(hc->queue);
- se->sock = sock;
- se->next = NULL;
+ for (i = 0; i < n; i++) {
+ SocketData_t *curr = dList_nth_data(hc->queue, i);
- if (sq->tail)
- sq->tail->next = se;
- sq->tail = se;
-
- if (! sq->head)
- sq->head = se;
+ if (a_Web_valid(curr->web) && (curr->web->flags & WEB_Image)) {
+ dList_insert_pos(hc->queue, sock, i);
+ return;
+ }
+ }
+ }
+ dList_append(hc->queue, sock);
}
-static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq)
+static SocketData_t* Http_socket_dequeue(HostConnection_t *hc)
{
- SocketQueueEntry_t *se = sq->head;
- SocketData_t *sd = NULL;
-
- if (se) {
- sq->head = se->next;
- if (sq->tail == se)
- sq->tail = NULL;
- sd = se->sock;
- dFree(se);
- }
+ SocketData_t *sd = dList_nth_data(hc->queue, 0);
+ dList_remove(hc->queue, sd);
return sd;
}
@@ -720,7 +844,7 @@ static HostConnection_t *Http_host_connection_get(const char *host)
}
hc = dNew0(HostConnection_t, 1);
- Http_socket_queue_init(&hc->queue);
+ hc->queue = dList_new(10);
hc->host = dStrdup(host);
dList_append(host_connections, hc);
@@ -729,7 +853,8 @@ static HostConnection_t *Http_host_connection_get(const char *host)
static void Http_host_connection_remove(HostConnection_t *hc)
{
- assert(hc->queue.head == NULL);
+ assert(dList_length(hc->queue) == 0);
+ dList_free(hc->queue);
dList_remove_fast(host_connections, hc);
dFree(hc->host);
dFree(hc);
@@ -738,15 +863,29 @@ static void Http_host_connection_remove(HostConnection_t *hc)
static void Http_host_connection_remove_all()
{
HostConnection_t *hc;
+ SocketData_t *sd;
while (dList_length(host_connections) > 0) {
hc = (HostConnection_t*) dList_nth_data(host_connections, 0);
- while (Http_socket_dequeue(&hc->queue));
+ while ((sd = Http_socket_dequeue(hc)))
+ dFree(sd);
Http_host_connection_remove(hc);
}
dList_free(host_connections);
}
+static void Http_fd_map_remove_all()
+{
+ FdMapEntry_t *fme;
+ int i, n = dList_length(fd_map);
+
+ for (i = 0; i < n; i++) {
+ fme = (FdMapEntry_t *) dList_nth_data(fd_map, i);
+ dFree(fme);
+ }
+ dList_free(fd_map);
+}
+
/*
* Deallocate memory used by http module
* (Call this one at exit time)
@@ -754,6 +893,7 @@ static void Http_host_connection_remove_all()
void a_Http_freeall(void)
{
Http_host_connection_remove_all();
+ Http_fd_map_remove_all();
a_Klist_free(&ValidSocks);
a_Url_free(HTTP_Proxy);
dFree(HTTP_Proxy_Auth_base64);
diff --git a/src/Makefile.am b/src/Makefile.am
index 65a42cad..597a743b 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@
@@ -96,6 +98,7 @@ dillo_SOURCES = \
plain.cc \
html.cc \
html.hh \
+ html_charrefs.h \
html_common.hh \
form.cc \
form.hh \
diff --git a/src/cache.c b/src/cache.c
index 14e862b5..189e18d5 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -55,7 +55,7 @@ typedef struct {
Dstr *Data; /* Pointer to raw data */
Dstr *UTF8Data; /* Data after charset translation */
int DataRefcount; /* Reference count */
- Decode *TransferDecoder; /* Transfer decoder (e.g., chunked) */
+ DecodeTransfer *TransferDecoder; /* Transfer decoder (e.g., chunked) */
Decode *ContentDecoder; /* Data decoder (e.g., gzip) */
Decode *CharsetDecoder; /* Translates text to UTF-8 encoding */
int ExpectedSize; /* Goal size of the HTTP transfer (0 if unknown)*/
@@ -110,7 +110,7 @@ static int Cache_entry_by_url_cmp(const void *v1, const void *v2)
}
/*
- * Initialize dicache data
+ * Initialize cache data
*/
void a_Cache_init(void)
{
@@ -205,7 +205,7 @@ static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url)
NewEntry->CharsetDecoder = NULL;
NewEntry->ExpectedSize = 0;
NewEntry->TransferSize = 0;
- NewEntry->Flags = CA_IsEmpty;
+ NewEntry->Flags = CA_IsEmpty | CA_KeepAlive;
}
/*
@@ -308,7 +308,7 @@ static void Cache_entry_free(CacheEntry_t *entry)
if (entry->CharsetDecoder)
a_Decode_free(entry->CharsetDecoder);
if (entry->TransferDecoder)
- a_Decode_free(entry->TransferDecoder);
+ a_Decode_transfer_free(entry->TransferDecoder);
if (entry->ContentDecoder)
a_Decode_free(entry->ContentDecoder);
dFree(entry);
@@ -498,7 +498,7 @@ const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype,
_MSG("a_Cache_set_content_type {%s} {%s}\n", ctype, URL_STR(url));
curr = Cache_current_content_type(entry);
- if (entry->TypeMeta || (*from == 'h' && entry->TypeHdr) ) {
+ if (entry->TypeMeta || (*from == 'h' && entry->TypeHdr) ) {
/* Type is already been set. Do nothing.
* BTW, META overrides TypeHdr */
} else {
@@ -652,7 +652,8 @@ static Dlist *Cache_parse_multiple_fields(const char *header,
static void Cache_parse_header(CacheEntry_t *entry)
{
char *header = entry->Header->str;
- char *Length, *Type, *location_str, *encoding;
+ bool_t server1point0 = !strncmp(entry->Header->str, "HTTP/1.0", 8);
+ char *Length, *Type, *location_str, *encoding, *connection;
#ifndef DISABLE_COOKIES
Dlist *Cookies;
#endif
@@ -716,6 +717,17 @@ static void Cache_parse_header(CacheEntry_t *entry)
dList_free(warnings);
}
+ if (server1point0)
+ entry->Flags &= ~CA_KeepAlive;
+
+ if ((connection = Cache_parse_field(header, "Connection"))) {
+ if (!dStrAsciiCasecmp(connection, "close"))
+ entry->Flags &= ~CA_KeepAlive;
+ else if (server1point0 && !dStrAsciiCasecmp(connection, "keep-alive"))
+ entry->Flags |= CA_KeepAlive;
+ dFree(connection);
+ }
+
/*
* Get Transfer-Encoding and initialize decoder
*/
@@ -834,6 +846,54 @@ static int Cache_get_header(CacheEntry_t *entry,
return 0;
}
+static void Cache_finish_msg(CacheEntry_t *entry)
+{
+ if (entry->Flags & CA_GotData) {
+ /* already finished */
+ return;
+ }
+
+ if ((entry->ExpectedSize || entry->TransferSize) &&
+ entry->TypeHdr == NULL) {
+ MSG_HTTP("Message with a body lacked Content-Type header.\n");
+ }
+ if ((entry->Flags & CA_GotLength) &&
+ (entry->ExpectedSize != entry->TransferSize)) {
+ MSG_HTTP("Content-Length does NOT match message body at\n"
+ "%s\n", URL_STR_(entry->Url));
+ MSG("Expected size: %d, Transfer size: %d\n",
+ entry->ExpectedSize, entry->TransferSize);
+ }
+ if (!entry->TransferSize && !(entry->Flags & CA_Redirect) &&
+ (entry->Flags & WEB_RootUrl)) {
+ char *eol = strchr(entry->Header->str, '\n');
+ if (eol) {
+ char *status_line = dStrndup(entry->Header->str,
+ eol - entry->Header->str);
+ MSG_HTTP("Body of %s was empty. Server sent status: %s\n",
+ URL_STR_(entry->Url), status_line);
+ dFree(status_line);
+ }
+ }
+ entry->Flags |= CA_GotData;
+ entry->Flags &= ~CA_Stopped; /* it may catch up! */
+ if (entry->TransferDecoder) {
+ a_Decode_transfer_free(entry->TransferDecoder);
+ entry->TransferDecoder = NULL;
+ }
+ if (entry->ContentDecoder) {
+ a_Decode_free(entry->ContentDecoder);
+ entry->ContentDecoder = NULL;
+ }
+ dStr_fit(entry->Data); /* fit buffer size! */
+
+ if ((entry = Cache_process_queue(entry))) {
+ if (entry->Flags & CA_GotHeader) {
+ Cache_unref_data(entry);
+ }
+ }
+}
+
/*
* Receive new data, update the reception buffer (for next read), update the
* cache, and service the client queue.
@@ -842,16 +902,17 @@ static int Cache_get_header(CacheEntry_t *entry,
* 'Op' is the operation to perform
* 'VPtr' is a (void) pointer to the IO control structure
*/
-void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
- const DilloUrl *Url)
+bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
+ const DilloUrl *Url)
{
int offset, len;
const char *str;
Dstr *dstr1, *dstr2, *dstr3;
+ bool_t done = FALSE;
CacheEntry_t *entry = Cache_entry_search(Url);
/* Assert a valid entry (not aborted) */
- dReturn_if_fail (entry != NULL);
+ dReturn_val_if_fail (entry != NULL, FALSE);
_MSG("__a_Cache_process_dbuf__\n");
@@ -875,7 +936,8 @@ void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
/* Decode arrived data (<= 3 stages) */
if (entry->TransferDecoder) {
- dstr1 = a_Decode_process(entry->TransferDecoder, str, len);
+ dstr1 = a_Decode_transfer_process(entry->TransferDecoder, str,len);
+ done = a_Decode_transfer_finished(entry->TransferDecoder);
str = dstr1->str;
len = dstr1->len;
}
@@ -896,51 +958,37 @@ void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
if (entry->Data->len)
entry->Flags &= ~CA_IsEmpty;
+ if ((entry->Flags & CA_GotLength) &&
+ (entry->TransferSize >= entry->ExpectedSize)) {
+ done = TRUE;
+ }
+ if (!(entry->Flags & CA_KeepAlive)) {
+ /* Let IOClose finish it later */
+ done = FALSE;
+ }
+
entry = Cache_process_queue(entry);
+
+ if (entry && done)
+ Cache_finish_msg(entry);
}
} else if (Op == IOClose) {
- if ((entry->ExpectedSize || entry->TransferSize) &&
- entry->TypeHdr == NULL) {
- MSG_HTTP("Message with a body lacked Content-Type header.\n");
- }
- if ((entry->Flags & CA_GotLength) &&
- (entry->ExpectedSize != entry->TransferSize)) {
- MSG_HTTP("Content-Length does NOT match message body at\n"
- "%s\n", URL_STR_(entry->Url));
- MSG("Expected size: %d, Transfer size: %d\n",
- entry->ExpectedSize, entry->TransferSize);
- }
- if (!entry->TransferSize && !(entry->Flags & CA_Redirect) &&
- (entry->Flags & WEB_RootUrl)) {
- char *eol = strchr(entry->Header->str, '\n');
- if (eol) {
- char *status_line = dStrndup(entry->Header->str,
- eol - entry->Header->str);
- MSG_HTTP("Body was empty. Server sent status: %s\n", status_line);
- dFree(status_line);
- }
- }
- entry->Flags |= CA_GotData;
- entry->Flags &= ~CA_Stopped; /* it may catch up! */
- if (entry->TransferDecoder) {
- a_Decode_free(entry->TransferDecoder);
- entry->TransferDecoder = NULL;
- }
- if (entry->ContentDecoder) {
- a_Decode_free(entry->ContentDecoder);
- entry->ContentDecoder = NULL;
- }
- dStr_fit(entry->Data); /* fit buffer size! */
+ Cache_finish_msg(entry);
+ } else if (Op == IOAbort) {
+ int i;
+ CacheClient_t *Client;
- if ((entry = Cache_process_queue(entry))) {
- if (entry->Flags & CA_GotHeader) {
- Cache_unref_data(entry);
+ for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) {
+ if (Client->Url == entry->Url) {
+ DilloWeb *web = (DilloWeb *)Client->Web;
+
+ a_Bw_remove_client(web->bw, Client->Key);
+ Cache_client_dequeue(Client);
+ --i; /* Keep the index value in the next iteration */
}
}
- } else if (Op == IOAbort) {
- /* unused */
- MSG("a_Cache_process_dbuf Op = IOAbort; not implemented!\n");
}
+ return done;
}
/*
@@ -1264,7 +1312,6 @@ static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry)
/* Trigger cleanup when there are no cache clients */
if (dList_length(ClientQueue) == 0) {
- _MSG(" a_Dicache_cleanup()\n");
a_Dicache_cleanup();
}
diff --git a/src/cache.h b/src/cache.h
index c39e4600..f3b064f2 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -33,6 +33,7 @@ extern "C" {
#define CA_InternalUrl 0x800 /* URL content is generated by dillo */
#define CA_HugeFile 0x1000 /* URL content is too big */
#define CA_IsEmpty 0x2000 /* True until a byte of content arrives */
+#define CA_KeepAlive 0x4000
typedef struct CacheClient CacheClient_t;
@@ -67,7 +68,7 @@ const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype,
const char *from);
uint_t a_Cache_get_flags(const DilloUrl *url);
uint_t a_Cache_get_flags_with_redirection(const DilloUrl *url);
-void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
+bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
const DilloUrl *Url);
int a_Cache_download_enabled(const DilloUrl *url);
void a_Cache_entry_remove_by_url(DilloUrl *url);
diff --git a/src/capi.c b/src/capi.c
index 531ae34d..242ae294 100644
--- a/src/capi.c
+++ b/src/capi.c
@@ -345,7 +345,7 @@ static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
/*
* Send the requested URL's source to the "view source" dpi
*/
-static void Capi_dpi_send_source(BrowserWindow *bw, DilloUrl *url)
+static void Capi_dpi_send_source(BrowserWindow *bw, DilloUrl *url)
{
char *p, *buf, *cmd, size_str[32], *server="vsource";
int buf_size;
@@ -385,81 +385,80 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
int safe = 0, ret = 0, use_cache = 0;
/* web->requester is NULL if the action is initiated by user */
- if (!(a_Capi_get_flags(web->url) & CAPI_IsCached ||
- web->requester == NULL ||
- a_Domain_permit(web->requester, web->url))) {
- return 0;
- }
-
- /* reload test */
- reload = (!(a_Capi_get_flags(web->url) & CAPI_IsCached) ||
- (URL_FLAGS(web->url) & URL_E2EQuery));
-
- if (web->flags & WEB_Download) {
- /* download request: if cached save from cache, else
- * for http, ftp or https, use the downloads dpi */
- if (a_Capi_get_flags_with_redirection(web->url) & CAPI_IsCached) {
- if (web->filename) {
- if ((web->stream = fopen(web->filename, "w"))) {
- use_cache = 1;
- } else {
- MSG_WARN("Cannot open \"%s\" for writing.\n", web->filename);
+ if (a_Capi_get_flags(web->url) & CAPI_IsCached ||
+ web->requester == NULL ||
+ a_Domain_permit(web->requester, web->url)) {
+
+ /* reload test */
+ reload = (!(a_Capi_get_flags(web->url) & CAPI_IsCached) ||
+ (URL_FLAGS(web->url) & URL_E2EQuery));
+
+ if (web->flags & WEB_Download) {
+ /* download request: if cached save from cache, else
+ * for http, ftp or https, use the downloads dpi */
+ if (a_Capi_get_flags_with_redirection(web->url) & CAPI_IsCached) {
+ if (web->filename) {
+ if ((web->stream = fopen(web->filename, "w"))) {
+ use_cache = 1;
+ } else {
+ MSG_WARN("Cannot open \"%s\" for writing.\n", web->filename);
+ }
}
+ } else if (a_Cache_download_enabled(web->url)) {
+ server = "downloads";
+ cmd = Capi_dpi_build_cmd(web, server);
+ a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
+ dFree(cmd);
+ } else {
+ MSG_WARN("Ignoring download request for '%s': "
+ "not in cache and not downloadable.\n",
+ URL_STR(web->url));
}
- } else if (a_Cache_download_enabled(web->url)) {
- server = "downloads";
- cmd = Capi_dpi_build_cmd(web, server);
- a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
- dFree(cmd);
- } else {
- MSG_WARN("Ignoring download request for '%s': "
- "not in cache and not downloadable.\n",
- URL_STR(web->url));
- }
-
- } else if (Capi_url_uses_dpi(web->url, &server)) {
- /* dpi request */
- if ((safe = a_Capi_dpi_verify_request(web->bw, web->url))) {
- if (dStrAsciiCasecmp(scheme, "dpi") == 0) {
- if (strcmp(server, "vsource") == 0) {
- /* allow "view source" reload upon user request */
- } else {
- /* make the other "dpi:/" prefixed urls always reload. */
- a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EQuery);
- reload = 1;
+
+ } else if (Capi_url_uses_dpi(web->url, &server)) {
+ /* dpi request */
+ if ((safe = a_Capi_dpi_verify_request(web->bw, web->url))) {
+ if (dStrAsciiCasecmp(scheme, "dpi") == 0) {
+ if (strcmp(server, "vsource") == 0) {
+ /* allow "view source" reload upon user request */
+ } else {
+ /* make the other "dpi:/" prefixed urls always reload. */
+ a_Url_set_flags(web->url, URL_FLAGS(web->url) |URL_E2EQuery);
+ reload = 1;
+ }
+ }
+ if (reload) {
+ a_Capi_conn_abort_by_url(web->url);
+ /* Send dpip command */
+ _MSG("a_Capi_open_url, reload url='%s'\n", URL_STR(web->url));
+ cmd = Capi_dpi_build_cmd(web, server);
+ a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
+ dFree(cmd);
+ if (strcmp(server, "vsource") == 0) {
+ Capi_dpi_send_source(web->bw, web->url);
+ }
}
+ use_cache = 1;
}
+ dFree(server);
+
+ } else if (!dStrAsciiCasecmp(scheme, "http")) {
+ /* http request */
if (reload) {
a_Capi_conn_abort_by_url(web->url);
- /* Send dpip command */
- _MSG("a_Capi_open_url, reload url='%s'\n", URL_STR(web->url));
- cmd = Capi_dpi_build_cmd(web, server);
- a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
- dFree(cmd);
- if (strcmp(server, "vsource") == 0) {
- Capi_dpi_send_source(web->bw, web->url);
- }
+ /* create a new connection and start the CCC operations */
+ conn = Capi_conn_new(web->url, web->bw, "http", "none");
+ /* start the reception branch before the query one because the DNS
+ * may callback immediately. This may avoid a race condition. */
+ a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, "http");
+ a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, web);
}
use_cache = 1;
- }
- dFree(server);
-
- } else if (!dStrAsciiCasecmp(scheme, "http")) {
- /* http request */
- if (reload) {
- a_Capi_conn_abort_by_url(web->url);
- /* create a new connection and start the CCC operations */
- conn = Capi_conn_new(web->url, web->bw, "http", "none");
- /* start the reception branch before the query one because the DNS
- * may callback immediately. This may avoid a race condition. */
- a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, "http");
- a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, web);
- }
- use_cache = 1;
- } else if (!dStrAsciiCasecmp(scheme, "about")) {
- /* internal request */
- use_cache = 1;
+ } else if (!dStrAsciiCasecmp(scheme, "about")) {
+ /* internal request */
+ use_cache = 1;
+ }
}
if (use_cache) {
@@ -681,6 +680,7 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
case OpAbort:
conn = Info->LocalKey;
conn->InfoSend = NULL;
+ a_Cache_process_dbuf(IOAbort, NULL, 0, conn->url);
if (Data2) {
if (!strcmp(Data2, "DpidERROR")) {
a_UIcmd_set_msg(conn->bw,
@@ -714,7 +714,10 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
Capi_conn_ref(conn);
Info->LocalKey = conn;
conn->InfoRecv = Info;
- a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 2);
+ if (strcmp(conn->server, "http") == 0)
+ a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Http_ccc, 2, 2);
+ else
+ a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 2);
a_Chain_bcb(OpStart, Info, NULL, Data2);
break;
case OpSend:
@@ -744,7 +747,15 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
if (strcmp(Data2, "send_page_2eof") == 0) {
/* Data1 = dbuf */
DataBuf *dbuf = Data1;
- a_Cache_process_dbuf(IORead, dbuf->Buf, dbuf->Size, conn->url);
+ bool_t finished = a_Cache_process_dbuf(IORead, dbuf->Buf,
+ dbuf->Size, conn->url);
+ if (finished && Capi_conn_valid(conn) && conn->InfoRecv) {
+ /* If we have a persistent connection where cache tells us
+ * that we've received the full response, and cache didn't
+ * trigger an abort and tear everything down, tell upstream.
+ */
+ a_Chain_bcb(OpSend, conn->InfoRecv, NULL, "reply_complete");
+ }
} else if (strcmp(Data2, "send_status_message") == 0) {
a_UIcmd_set_msg(conn->bw, "%s", Data1);
} else if (strcmp(Data2, "chat") == 0) {
diff --git a/src/colors.c b/src/colors.c
index fe3598eb..237e63a1 100644
--- a/src/colors.c
+++ b/src/colors.c
@@ -303,7 +303,7 @@ int32_t a_Color_parse (const char *str, int32_t default_color, int *err)
static int Color_distance(long c1, long c2)
{
return (labs((c1 & 0x0000ff) - (c2 & 0x0000ff)) +
- labs(((c1 & 0x00ff00) - (c2 & 0x00ff00)) >> 8) +
+ labs(((c1 & 0x00ff00) - (c2 & 0x00ff00)) >> 8) +
labs(((c1 & 0xff0000) - (c2 & 0xff0000)) >> 16)) / 75;
}
#endif
diff --git a/src/cookies.h b/src/cookies.h
index 1cdb82ac..5e4d8c59 100644
--- a/src/cookies.h
+++ b/src/cookies.h
@@ -5,18 +5,17 @@
extern "C" {
#endif /* __cplusplus */
+void a_Cookies_init( void );
#ifdef DISABLE_COOKIES
# define a_Cookies_get_query(url, requester) dStrdup("")
# define a_Cookies_set() ;
-# define a_Cookies_init() ;
# define a_Cookies_freeall() ;
#else
char *a_Cookies_get_query(const DilloUrl *query_url,
const DilloUrl *requester);
void a_Cookies_set(Dlist *cookie_string, const DilloUrl *set_url,
const char *server_date);
- void a_Cookies_init( void );
void a_Cookies_freeall( void );
#endif
diff --git a/src/css.cc b/src/css.cc
index 5bdf4fdb..5c64c619 100644
--- a/src/css.cc
+++ b/src/css.cc
@@ -544,7 +544,7 @@ void CssContext::addRule (CssSelector *sel, CssPropertyList *props,
if (order == CSS_PRIMARY_USER_AGENT) {
userAgentSheet.addRule (rule);
- } else {
+ } else {
sheet[order].addRule (rule);
}
}
diff --git a/src/css.hh b/src/css.hh
index 22e7e700..c2a28770 100644
--- a/src/css.hh
+++ b/src/css.hh
@@ -133,7 +133,7 @@ inline float CSS_LENGTH_VALUE (CssLength l) {
case CSS_LENGTH_TYPE_EX:
case CSS_LENGTH_TYPE_PERCENTAGE:
case CSS_LENGTH_TYPE_RELATIVE:
- return ((float)(l & ~7)) / (1 << 15);
+ return ((float)(l & ~7)) / (1 << 15);
case CSS_LENGTH_TYPE_AUTO:
return 0.0;
default:
diff --git a/src/cssparser.cc b/src/cssparser.cc
index 369dd67f..1487a605 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,14 @@ static const char *const Css_list_style_type_enum_vals[] = {
"katakana-iroha", "none", NULL
};
+static const char *const Css_overflow_enum_vals[] = {
+ "visible", "hidden", "scroll", "auto", 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 +198,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 +210,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},
@@ -227,21 +243,25 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
{CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_AUTO, CSS_TYPE_UNUSED}, NULL},
{"marker-offset", {CSS_TYPE_UNUSED}, NULL},
{"marks", {CSS_TYPE_UNUSED}, NULL},
- {"max-height", {CSS_TYPE_UNUSED}, NULL},
- {"max-width", {CSS_TYPE_UNUSED}, NULL},
- {"min-height", {CSS_TYPE_UNUSED}, NULL},
- {"min-width", {CSS_TYPE_UNUSED}, NULL},
+ {"max-height", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_AUTO, CSS_TYPE_UNUSED},
+ NULL},
+ {"max-width", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_AUTO, CSS_TYPE_UNUSED},
+ NULL},
+ {"min-height", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_AUTO, CSS_TYPE_UNUSED},
+ NULL},
+ {"min-width", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_AUTO, CSS_TYPE_UNUSED},
+ NULL},
{"outline-color", {CSS_TYPE_UNUSED}, NULL},
{"outline-style", {CSS_TYPE_UNUSED}, NULL},
{"outline-width", {CSS_TYPE_UNUSED}, NULL},
- {"overflow", {CSS_TYPE_UNUSED}, NULL},
+ {"overflow", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_overflow_enum_vals},
{"padding-bottom", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
{"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 +269,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},
@@ -277,7 +297,7 @@ typedef struct {
} type;
const CssPropertyName *properties; /* CSS_SHORTHAND_MULTIPLE:
* must be terminated by
- * CSS_PROPERTY_END
+ * CSS_PROPERTY_END
* CSS_SHORTHAND_DIRECTIONS:
* must have length 4
* CSS_SHORTHAND_BORDERS:
@@ -720,7 +740,9 @@ bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type)
dStrAsciiCasecmp(tval, "top") == 0 ||
dStrAsciiCasecmp(tval, "bottom") == 0))
return true;
- // Fall Through (lenght and percentage)
+ if (ttype == CSS_TK_DECINT || ttype == CSS_TK_FLOAT)
+ return true;
+ break;
case CSS_TYPE_LENGTH_PERCENTAGE:
case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER:
case CSS_TYPE_LENGTH:
@@ -766,7 +788,8 @@ bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type)
case CSS_TYPE_URI:
if (ttype == CSS_TK_SYMBOL &&
- dStrAsciiCasecmp(tval, "url") == 0)
+ (dStrAsciiCasecmp(tval, "url") == 0 ||
+ dStrAsciiCasecmp(tval, "none") == 0))
return true;
break;
@@ -1044,12 +1067,16 @@ bool CssParser::parseValue(CssPropertyName prop,
break;
case CSS_TYPE_URI:
- if (ttype == CSS_TK_SYMBOL &&
- dStrAsciiCasecmp(tval, "url") == 0) {
- val->strVal = parseUrl();
- nextToken();
- if (val->strVal)
+ if (ttype == CSS_TK_SYMBOL) {
+ if (dStrAsciiCasecmp(tval, "url") == 0) {
+ val->strVal = parseUrl();
+ if (val->strVal)
+ ret = true;
+ } else if (dStrAsciiCasecmp(tval, "none") == 0) {
+ val->strVal = NULL;
ret = true;
+ }
+ nextToken();
}
break;
@@ -1106,6 +1133,9 @@ bool CssParser::parseValue(CssPropertyName prop,
if (parseValue(prop, CSS_TYPE_LENGTH_PERCENTAGE, &valTmp)) {
pos[i] = valTmp.intVal;
ret = true;
+ } else if (parseValue(prop, CSS_TYPE_SIGNED_LENGTH, &valTmp)) {
+ pos[i] = valTmp.intVal;
+ ret = true;
} else
// ... but something may still fail.
h[i] = v[i] = false;
diff --git a/src/decode.c b/src/decode.c
index 53a0d621..6d838d41 100644
--- a/src/decode.c
+++ b/src/decode.c
@@ -21,9 +21,10 @@
static const int bufsize = 8*1024;
/*
- * Decode chunked data
+ * Decode 'Transfer-Encoding: chunked' data
*/
-static Dstr *Decode_chunked(Decode *dc, const char *instr, int inlen)
+Dstr *a_Decode_transfer_process(DecodeTransfer *dc, const char *instr,
+ int inlen)
{
char *inputPtr, *eol;
int inputRemaining;
@@ -66,6 +67,7 @@ static Dstr *Decode_chunked(Decode *dc, const char *instr, int inlen)
}
if (!(chunkRemaining = strtol(inputPtr, NULL, 0x10))) {
+ dc->finished = TRUE;
break; /* A chunk length of 0 means we're done! */
}
inputRemaining -= (eol - inputPtr) + 1;
@@ -80,10 +82,16 @@ static Dstr *Decode_chunked(Decode *dc, const char *instr, int inlen)
return output;
}
-static void Decode_chunked_free(Decode *dc)
+bool_t a_Decode_transfer_finished(DecodeTransfer *dc)
+{
+ return dc->finished;
+}
+
+void a_Decode_transfer_free(DecodeTransfer *dc)
{
dFree(dc->state);
dStr_free(dc->leftover, 1);
+ dFree(dc);
}
static void Decode_compression_free(Decode *dc)
@@ -208,7 +216,7 @@ static Dstr *Decode_deflate(Decode *dc, const char *instr, int inlen)
dStr_free(output, 1);
(void)inflateEnd(zs);
dFree(dc->state);
- dc->state = zs = dNew(z_stream, 1);;
+ dc->state = zs = dNew(z_stream, 1);
zs->zalloc = NULL;
zs->zfree = NULL;
zs->next_in = NULL;
@@ -280,19 +288,17 @@ static void Decode_charset_free(Decode *dc)
/*
* Initialize transfer decoder. Currently handles "chunked".
*/
-Decode *a_Decode_transfer_init(const char *format)
+DecodeTransfer *a_Decode_transfer_init(const char *format)
{
- Decode *dc = NULL;
+ DecodeTransfer *dc = NULL;
if (format && !dStrAsciiCasecmp(format, "chunked")) {
int *chunk_remaining = dNew(int, 1);
*chunk_remaining = 0;
- dc = dNew(Decode, 1);
+ dc = dNew(DecodeTransfer, 1);
dc->leftover = dStr_new("");
dc->state = chunk_remaining;
- dc->decode = Decode_chunked;
- dc->free = Decode_chunked_free;
- dc->buffer = NULL; /* not used */
+ dc->finished = FALSE;
_MSG("chunked!\n");
}
return dc;
diff --git a/src/decode.h b/src/decode.h
index 279807a6..06c987f6 100644
--- a/src/decode.h
+++ b/src/decode.h
@@ -15,7 +15,21 @@ typedef struct Decode {
void (*free) (struct Decode *dc);
} Decode;
-Decode *a_Decode_transfer_init(const char *format);
+/* I'm not going to shoehorn the decoders into the same form anymore. They
+ * can evolve independently.
+ */
+typedef struct DecodeTransfer {
+ Dstr *leftover;
+ void *state;
+ bool_t finished; /* has the terminating chunk been seen? */
+} DecodeTransfer;
+
+DecodeTransfer *a_Decode_transfer_init(const char *format);
+Dstr *a_Decode_transfer_process(DecodeTransfer *dc, const char *instr,
+ int inlen);
+bool_t a_Decode_transfer_finished(DecodeTransfer *dc);
+void a_Decode_transfer_free(DecodeTransfer *dc);
+
Decode *a_Decode_content_init(const char *format);
Decode *a_Decode_charset_init(const char *format);
Dstr *a_Decode_process(Decode *dc, const char *instr, int inlen);
diff --git a/src/dicache.c b/src/dicache.c
index db3b86b2..a2904c32 100644
--- a/src/dicache.c
+++ b/src/dicache.c
@@ -28,15 +28,10 @@ enum {
DIC_Jpeg
};
-typedef struct {
- int valid; /* flag */
- DilloUrl *url; /* primary "Key" for this dicache entry */
- DICacheEntry *first; /* pointer to the first dicache entry in this list */
-} DICacheNode;
/*
- * List of DICacheNode. One node per URL. Each node may have several
- * versions of the same image in a linked list.
+ * List of DICacheEntry. May hold several versions of the same image,
+ * although most of the time it holds just one.
*/
static Dlist *CachedIMGs = NULL;
@@ -45,24 +40,20 @@ static uint_t dicache_size_total; /* invariant: dicache_size_total is
* of all the images in the dicache. */
/*
- * Compare two dicache nodes
+ * Compare function for image entries
*/
-static int Dicache_node_cmp(const void *v1, const void *v2)
+static int Dicache_entry_cmp(const void *v1, const void *v2)
{
- const DICacheNode *n1 = v1, *n2 = v2;
-
- return a_Url_cmp(n1->url, n2->url);
-}
-
-/*
- * Compare function for searching a node by Url
- */
-static int Dicache_node_by_url_cmp(const void *v1, const void *v2)
-{
- const DICacheNode *node = v1;
- const DilloUrl *url = v2;
-
- return a_Url_cmp(node->url, url);
+ const DICacheEntry *e1 = v1, *e2 = v2;
+
+ int st = a_Url_cmp(e1->url, e2->url);
+ if (st == 0) {
+ if (e2->version == DIC_Last)
+ st = (e1->Flags & DIF_Last ? 0 : -1);
+ else
+ st = (e1->version - e2->version);
+ }
+ return st;
}
/*
@@ -83,6 +74,8 @@ static DICacheEntry *Dicache_entry_new(void)
entry->width = 0;
entry->height = 0;
+ entry->Flags = DIF_Valid;
+ entry->SurvCleanup = 0;
entry->type = DILLO_IMG_TYPE_NOTSET;
entry->cmap = NULL;
entry->v_imgbuf = NULL;
@@ -97,41 +90,29 @@ static DICacheEntry *Dicache_entry_new(void)
entry->DecoderData = NULL;
entry->DecodedSize = 0;
- entry->next = NULL;
-
return entry;
}
/*
* Add a new entry in the dicache
- * (a single node (URL) may have several entries)
+ * (a single URL may have several entries)
*/
static DICacheEntry *Dicache_add_entry(const DilloUrl *Url)
{
- DICacheEntry *entry;
- DICacheNode *node;
+ DICacheEntry e, *entry, *last;
entry = Dicache_entry_new();
-
- if ((node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp))) {
- /* this URL is already in CachedIMGs, add entry at the END of the list */
- DICacheEntry *ptr = node->first;
-
- node->valid = 1;
- for ( ; ptr->next; ptr = ptr->next);
- ptr->next = entry;
- entry->version = ptr->version+1;
- entry->url = node->url;
-
- } else { /* no node yet, so create one */
- DICacheNode *node = dNew(DICacheNode, 1);
-
- node->url = a_Url_dup(Url);
- entry->url = node->url;
- node->first = entry;
- node->valid = 1;
- dList_insert_sorted(CachedIMGs, node, Dicache_node_cmp);
+ e.url = (DilloUrl*)Url;
+ e.version = DIC_Last;
+ last = dList_find_sorted(CachedIMGs, &e, Dicache_entry_cmp);
+ if (last) {
+ /* URL is already in CachedIMGs, make a new version */
+ last->Flags &= ~DIF_Last;
+ entry->version = last->version + 1;
}
+ entry->url = a_Url_dup(Url);
+ entry->Flags |= DIF_Last;
+ dList_insert_sorted(CachedIMGs, entry, Dicache_entry_cmp);
return entry;
}
@@ -145,23 +126,15 @@ static DICacheEntry *Dicache_add_entry(const DilloUrl *Url)
*/
DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url, int version)
{
- DICacheNode *node;
+ DICacheEntry e;
DICacheEntry *entry = NULL;
dReturn_val_if_fail(version != 0, NULL);
-
- node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
- if (node) {
- if (version == DIC_Last) {
- if (node->valid) {
- entry = node->first;
- for ( ; (entry && entry->next); entry = entry->next);
- }
- } else {
- entry = node->first;
- for ( ; entry && entry->version != version; entry = entry->next) ;
- }
- }
+ e.url = (DilloUrl*)Url;
+ e.version = version;
+ entry = dList_find_sorted(CachedIMGs, &e, Dicache_entry_cmp);
+ if (entry && !(entry->Flags & DIF_Valid) && version == DIC_Last)
+ entry = NULL;
return entry;
}
@@ -170,57 +143,48 @@ DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url, int version)
*/
static void Dicache_remove(const DilloUrl *Url, int version)
{
- DICacheNode *node;
- DICacheEntry *entry, *prev;
- _MSG("Dicache_remove url=%s\n", URL_STR(Url));
- node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
- prev = entry = (node) ? node->first : NULL;
+ DICacheEntry e, *entry;
- while (entry && (entry->version != version) ) {
- prev = entry;
- entry = entry->next;
- }
-
- if (entry) {
- _MSG("Dicache_remove Decoder=%p DecoderData=%p\n",
- entry->Decoder, entry->DecoderData);
- /* Eliminate this dicache entry */
- dFree(entry->cmap);
- a_Bitvec_free(entry->BitVec);
- a_Imgbuf_unref(entry->v_imgbuf);
- if (entry->Decoder) {
- entry->Decoder(CA_Abort, entry->DecoderData);
- }
- dicache_size_total -= entry->TotalSize;
-
- if (node->first == entry) {
- if (!entry->next) {
- /* last entry with this URL. Remove the node as well */
- dList_remove(CachedIMGs, node);
- a_Url_free(node->url);
- dFree(node);
- } else
- node->first = entry->next;
- } else {
- prev->next = entry->next;
- }
- dFree(entry);
+ _MSG("Dicache_remove url=%s\n", URL_STR(Url));
+ e.url = (DilloUrl*)Url;
+ e.version = version;
+ entry = dList_find_sorted(CachedIMGs, &e, Dicache_entry_cmp);
+ dReturn_if (entry == NULL);
+
+ _MSG("Dicache_remove Imgbuf=%p Decoder=%p DecoderData=%p\n",
+ entry->v_imgbuf, entry->Decoder, entry->DecoderData);
+ /* Eliminate this dicache entry */
+ dList_remove(CachedIMGs, entry);
+ dicache_size_total -= entry->TotalSize;
+
+ /* entry cleanup */
+ a_Url_free(entry->url);
+ dFree(entry->cmap);
+ a_Bitvec_free(entry->BitVec);
+ a_Imgbuf_unref(entry->v_imgbuf);
+ if (entry->Decoder) {
+ entry->Decoder(CA_Abort, entry->DecoderData);
}
+ dFree(entry);
}
/*
- * Unrefs the counter of a dicache entry, and _if_ no DwImage is acessing
- * this buffer, then we call Dicache_remove() to do the job.
+ * Unrefs the counter of a dicache entry (it counts cache clients).
+ * If there're no clients and no imgbuf, remove the entry.
+ * Otherwise, let a_Dicache_cleanup() do the job later
+ * (keeping it cached meanwhile for e.g. reload, repush, back/fwd).
*/
void a_Dicache_unref(const DilloUrl *Url, int version)
{
DICacheEntry *entry;
- _MSG("a_Dicache_unref\n");
if ((entry = a_Dicache_get_entry(Url, version))) {
- if (--entry->RefCount == 0) {
+ _MSG("a_Dicache_unref: RefCount=%d State=%d ImgbufLastRef=%d\n",
+ entry->RefCount, entry->State,
+ entry->v_imgbuf ? a_Imgbuf_last_reference(entry->v_imgbuf) : -1);
+ if (entry->RefCount > 0) --entry->RefCount;
+ if (entry->RefCount == 0 && entry->v_imgbuf == NULL)
Dicache_remove(Url, version);
- }
}
}
@@ -244,11 +208,9 @@ DICacheEntry* a_Dicache_ref(const DilloUrl *Url, int version)
*/
void a_Dicache_invalidate_entry(const DilloUrl *Url)
{
- DICacheNode *node;
-
- node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
- if (node)
- node->valid = 0;
+ DICacheEntry *entry = a_Dicache_get_entry(Url, DIC_Last);
+ if (entry)
+ entry->Flags &= ~DIF_Valid;
}
@@ -256,7 +218,9 @@ void a_Dicache_invalidate_entry(const DilloUrl *Url)
/*
* Set image's width, height & type
- * (By now, we'll use the image information despite the html tags --Jcid)
+ * - 'width' and 'height' come from the image data.
+ * - HTML width and height attrs are handled with setNonCssHint.
+ * - CSS sizing is handled by the CSS engine.
*/
void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
uint_t width, uint_t height, DilloImgType type,
@@ -269,7 +233,7 @@ void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
/* Find the DicEntry for this Image */
DicEntry = a_Dicache_get_entry(url, version);
dReturn_if_fail ( DicEntry != NULL );
- /* Parameters already set? */
+ /* Parameters already set? Don't do it twice. */
dReturn_if_fail ( DicEntry->State < DIC_SetParms );
_MSG(" RefCount=%d version=%d\n", DicEntry->RefCount, DicEntry->version);
@@ -292,7 +256,7 @@ void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
/*
* Implement the set_cmap method for the Image
*/
-void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
+void a_Dicache_set_cmap(DilloUrl *url, int version, int bg_color,
const uchar_t *cmap, uint_t num_colors,
int num_colors_max, int bg_index)
{
@@ -305,9 +269,9 @@ void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
DicEntry->cmap = dNew0(uchar_t, 3 * num_colors_max);
memcpy(DicEntry->cmap, cmap, 3 * num_colors);
if (bg_index >= 0 && (uint_t)bg_index < num_colors) {
- DicEntry->cmap[bg_index * 3] = (Image->bg_color >> 16) & 0xff;
- DicEntry->cmap[bg_index * 3 + 1] = (Image->bg_color >> 8) & 0xff;
- DicEntry->cmap[bg_index * 3 + 2] = (Image->bg_color) & 0xff;
+ DicEntry->cmap[bg_index * 3] = (bg_color >> 16) & 0xff;
+ DicEntry->cmap[bg_index * 3 + 1] = (bg_color >> 8) & 0xff;
+ DicEntry->cmap[bg_index * 3 + 2] = (bg_color) & 0xff;
}
DicEntry->State = DIC_SetCmap;
@@ -368,6 +332,9 @@ void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client)
/* a_Dicache_unref() may free DicEntry */
_MSG("a_Dicache_close RefCount=%d\n", DicEntry->RefCount - 1);
+ _MSG("a_Dicache_close DIC_Close=%d State=%d\n", DIC_Close, DicEntry->State);
+ _MSG(" a_Dicache_close imgbuf=%p Decoder=%p DecoderData=%p\n",
+ DicEntry->v_imgbuf, DicEntry->Decoder, DicEntry->DecoderData);
if (DicEntry->State < DIC_Close) {
DicEntry->State = DIC_Close;
@@ -410,24 +377,29 @@ static void *Dicache_image(int ImgType, const char *MimeType, void *Ptr,
DicEntry = a_Dicache_get_entry(web->url, DIC_Last);
if (!DicEntry) {
- /* Let's create an entry for this image... */
+ /* Create an entry for this image... */
DicEntry = Dicache_add_entry(web->url);
- DicEntry->DecoderData =
- (ImgType == DIC_Png) ?
- a_Png_new(web->Image, DicEntry->url, DicEntry->version) :
- (ImgType == DIC_Gif) ?
- a_Gif_new(web->Image, DicEntry->url, DicEntry->version) :
- (ImgType == DIC_Jpeg) ?
- a_Jpeg_new(web->Image, DicEntry->url, DicEntry->version) :
- NULL;
+ /* Attach a decoder */
+ if (ImgType == DIC_Jpeg) {
+ DicEntry->Decoder = (CA_Callback_t)a_Jpeg_callback;
+ DicEntry->DecoderData =
+ a_Jpeg_new(web->Image, DicEntry->url, DicEntry->version);
+ } else if (ImgType == DIC_Gif) {
+ DicEntry->Decoder = (CA_Callback_t)a_Gif_callback;
+ DicEntry->DecoderData =
+ a_Gif_new(web->Image, DicEntry->url, DicEntry->version);
+ } else if (ImgType == DIC_Png) {
+ DicEntry->Decoder = (CA_Callback_t)a_Png_callback;
+ DicEntry->DecoderData =
+ a_Png_new(web->Image, DicEntry->url, DicEntry->version);
+ }
} else {
/* Repeated image */
a_Dicache_ref(DicEntry->url, DicEntry->version);
}
- DicEntry->Decoder = (ImgType == DIC_Png) ? (CA_Callback_t)a_Png_callback :
- (ImgType == DIC_Gif) ? (CA_Callback_t)a_Gif_callback :
- (ImgType == DIC_Jpeg) ? (CA_Callback_t)a_Jpeg_callback:
- NULL;
+ /* Survive three cleanup passes (set to zero = old behaviour). */
+ DicEntry->SurvCleanup = 3;
+
*Data = DicEntry->DecoderData;
*Call = (CA_Callback_t) a_Dicache_callback;
@@ -535,52 +507,42 @@ void a_Dicache_callback(int Op, CacheClient_t *Client)
void a_Dicache_cleanup(void)
{
int i;
- DICacheNode *node;
DICacheEntry *entry;
- _MSG("a_Dicache_cleanup\n");
- for (i = 0; i < dList_length(CachedIMGs); ++i) {
- node = dList_nth_data(CachedIMGs, i);
- /* iterate each entry of this node */
- for (entry = node->first; entry; entry = entry->next) {
- if (entry->v_imgbuf &&
- a_Imgbuf_last_reference(entry->v_imgbuf)) {
- /* free this unused entry */
- if (entry->next) {
- Dicache_remove(node->url, entry->version);
- } else {
- Dicache_remove(node->url, entry->version);
- --i;
- break;
- }
- }
+ for (i = 0; (entry = dList_nth_data(CachedIMGs, i)); ++i) {
+ _MSG(" SurvCleanup = %d\n", entry->SurvCleanup);
+ if (entry->RefCount == 0 &&
+ (!entry->v_imgbuf || a_Imgbuf_last_reference(entry->v_imgbuf))) {
+ if (--entry->SurvCleanup >= 0)
+ continue; /* keep the entry one more pass */
+
+ /* free this unused entry */
+ Dicache_remove(entry->url, entry->version);
+ --i; /* adjust counter */
}
}
+ MSG("a_Dicache_cleanup: length = %d\n", dList_length(CachedIMGs));
}
/* ------------------------------------------------------------------------- */
/*
* Deallocate memory used by dicache module
- * (Call this one at exit time)
+ * (Call this one at exit time, with no cache clients queued)
*/
void a_Dicache_freeall(void)
{
- DICacheNode *node;
DICacheEntry *entry;
- /* Remove every dicache node and its entries */
- while ((node = dList_nth_data(CachedIMGs, 0))) {
- while ((entry = node->first)) {
- node->first = entry->next;
- dFree(entry->cmap);
- a_Bitvec_free(entry->BitVec);
- a_Imgbuf_unref(entry->v_imgbuf);
- dicache_size_total -= entry->TotalSize;
- }
- dList_remove_fast(CachedIMGs, node);
- a_Url_free(node->url);
- dFree(node);
+ /* Remove all the dicache entries */
+ while ((entry = dList_nth_data(CachedIMGs, dList_length(CachedIMGs)-1))) {
+ dList_remove_fast(CachedIMGs, entry);
+ a_Url_free(entry->url);
+ dFree(entry->cmap);
+ a_Bitvec_free(entry->BitVec);
+ a_Imgbuf_unref(entry->v_imgbuf);
+ dicache_size_total -= entry->TotalSize;
+ dFree(entry);
}
dList_free(CachedIMGs);
}
diff --git a/src/dicache.h b/src/dicache.h
index df8a8b89..7d5ef6ee 100644
--- a/src/dicache.h
+++ b/src/dicache.h
@@ -12,6 +12,9 @@ extern "C" {
/* Symbolic name to request the last version of an image */
#define DIC_Last -1
+/* Flags: Last version, Valid entry */
+#define DIF_Last 1
+#define DIF_Valid 2
/* These will reflect the entry's "state" */
@@ -26,8 +29,10 @@ typedef enum {
typedef struct DICacheEntry {
DilloUrl *url; /* Image URL for this entry */
- uint_t width, height; /* As taken from image data */
DilloImgType type; /* Image type */
+ uint_t width, height; /* As taken from image data */
+ short Flags; /* See Flags */
+ short SurvCleanup; /* Cleanup-pass survival for unused images */
uchar_t *cmap; /* Color map */
void *v_imgbuf; /* Void pointer to an Imgbuf object */
uint_t TotalSize; /* Amount of memory the image takes up */
@@ -38,11 +43,9 @@ typedef struct DICacheEntry {
int version; /* Version number, used for different
versions of the same URL image */
+ uint_t DecodedSize; /* Size of already decoded data */
CA_Callback_t Decoder; /* Client function */
void *DecoderData; /* Client function data */
- uint_t DecodedSize; /* Size of already decoded data */
-
- struct DICacheEntry *next; /* Link to the next "newer" version */
} DICacheEntry;
@@ -61,7 +64,7 @@ void a_Dicache_callback(int Op, CacheClient_t *Client);
void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
uint_t width, uint_t height, DilloImgType type,
double gamma);
-void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
+void a_Dicache_set_cmap(DilloUrl *url, int version, int bg_color,
const uchar_t *cmap, uint_t num_colors,
int num_colors_max, int bg_index);
void a_Dicache_new_scan(const DilloUrl *url, int version);
diff --git a/src/dillo.cc b/src/dillo.cc
index ee2f4da4..0a68cb59 100644
--- a/src/dillo.cc
+++ b/src/dillo.cc
@@ -56,7 +56,9 @@
#include "lout/debug.hh"
#include "dw/fltkcore.hh"
+#include "dw/widget.hh"
#include "dw/textblock.hh"
+#include "dw/table.hh"
/*
* Command line options structure
@@ -377,10 +379,18 @@ static DilloUrl *makeStartUrl(char *str, bool local)
*/
int main(int argc, char **argv)
{
- DBG_OBJ_COLOR("#c0ff80", "dw::*");
- DBG_OBJ_COLOR("#c0c0ff", "dw::fltk::*");
- DBG_OBJ_COLOR("#ffa0a0", "dw::core::*");
- DBG_OBJ_COLOR("#ffe0a0", "dw::core::style::*");
+ DBG_OBJ_COLOR ("dw::*", "#c0ff80");
+ DBG_OBJ_COLOR ("dw::fltk::*", "#c0c0ff");
+ DBG_OBJ_COLOR ("dw::core::*", "#ffa0a0");
+ DBG_OBJ_COLOR ("dw::core::style::*", "#ffe0a0");
+
+ DBG_OBJ_COLOR ("dw::Image", "#80ffa0");
+ DBG_OBJ_COLOR ("dw::Textblock", "#f0ff80");
+ DBG_OBJ_COLOR ("dw::OutOfFlowMgr", "#d0ff80");
+ DBG_OBJ_COLOR ("dw::AlignedTextblock", "#e0ff80");
+ DBG_OBJ_COLOR ("dw::ListItem", "#b0ff80");
+ DBG_OBJ_COLOR ("dw::TableCell", "#80ff80");
+ DBG_OBJ_COLOR ("dw::Table", "#80ffc0");
uint_t opt_id;
uint_t options_got = 0;
@@ -475,6 +485,8 @@ int main(int argc, char **argv)
a_UIcmd_init();
StyleEngine::init();
+ dw::core::Widget::setAdjustMinWidth (prefs.adjust_min_width);
+ dw::Table::setAdjustTableMinWidth (prefs.adjust_table_min_width);
dw::Textblock::setPenaltyHyphen (prefs.penalty_hyphen);
dw::Textblock::setPenaltyHyphen2 (prefs.penalty_hyphen_2);
dw::Textblock::setPenaltyEmDashLeft (prefs.penalty_em_dash_left);
diff --git a/src/domain.c b/src/domain.c
index ea5c4948..8bff39de 100644
--- a/src/domain.c
+++ b/src/domain.c
@@ -129,7 +129,7 @@ bool_t a_Domain_permit(const DilloUrl *source, const DilloUrl *dest)
ret = source_host[0] == '\0' ||
!dStrAsciiCasecmp(URL_SCHEME(dest), "data");
if (ret == FALSE)
- MSG("Domain: DENIED from %s to %s.\n", source_host, URL_STR(dest));
+ MSG("Domain: DENIED %s -> %s.\n", source_host, URL_STR(dest));
return ret;
}
@@ -151,7 +151,7 @@ bool_t a_Domain_permit(const DilloUrl *source, const DilloUrl *dest)
if (ret == FALSE) {
const char *src = source_host[0] ? source_host : URL_STR(source);
- MSG("Domain: DENIED from %s to %s.\n", src, dest_host);
+ MSG("Domain: DENIED %s -> %s.\n", src, dest_host);
}
return ret;
}
diff --git a/src/form.cc b/src/form.cc
index 07c12815..9e7dc4f6 100644
--- a/src/form.cc
+++ b/src/form.cc
@@ -343,7 +343,7 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize)
HT2TB(html)->addParbreak (9, html->wordStyle ());
if (html->InFlags & IN_FORM) {
- BUG_MSG("nested forms\n");
+ BUG_MSG("Nested <form>.");
return;
}
html->InFlags |= IN_FORM;
@@ -356,14 +356,14 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize)
if (!dStrAsciiCasecmp(attrbuf, "post")) {
method = DILLO_HTML_METHOD_POST;
} else if (dStrAsciiCasecmp(attrbuf, "get")) {
- BUG_MSG("Unknown form submission method \"%s\"\n", attrbuf);
+ BUG_MSG("<form> submission method unknown: '%s'.", attrbuf);
}
}
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "action")))
action = a_Html_url_new(html, attrbuf, NULL, 0);
else {
if (html->DocType != DT_HTML || html->DocTypeVersion <= 4.01f)
- BUG_MSG("action attribute is required for <form>\n");
+ BUG_MSG("<form> requires action attribute.");
action = a_Url_dup(html->base_url);
}
content_type = DILLO_HTML_ENC_URLENCODED;
@@ -417,7 +417,7 @@ static int Html_input_get_size(DilloHtml *html, const char *attrbuf)
if (size < 1 || size > MAX_SIZE) {
int badSize = size;
size = (size < 1 ? 20 : MAX_SIZE);
- BUG_MSG("input size=%d, using size=%d instead\n", badSize, size);
+ BUG_MSG("<input> size=%d, using size=%d instead.", badSize, size);
}
}
return size;
@@ -437,11 +437,11 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
ResourceFactory *factory;
if (html->InFlags & IN_SELECT) {
- BUG_MSG("<input> element inside <select>\n");
+ BUG_MSG("<input> inside <select>.");
return;
}
if (html->InFlags & IN_BUTTON) {
- BUG_MSG("<input> element inside <button>\n");
+ BUG_MSG("<input> inside <button>.");
return;
}
@@ -507,12 +507,12 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
DilloHtmlForm *form = html->getCurrentForm();
if (form->method != DILLO_HTML_METHOD_POST) {
valid = false;
- BUG_MSG("Forms with file input MUST use HTTP POST method\n");
+ BUG_MSG("<form> with file input MUST use HTTP POST method.");
MSG("File input ignored in form not using HTTP POST method\n");
} else if (form->content_type != DILLO_HTML_ENC_MULTIPART) {
valid = false;
- BUG_MSG("Forms with file input MUST use multipart/form-data"
- " encoding\n");
+ BUG_MSG("<form> with file input MUST use multipart/form-data"
+ " encoding.");
MSG("File input ignored in form not using multipart/form-data"
" encoding\n");
}
@@ -641,25 +641,25 @@ void Html_tag_content_textarea(DilloHtml *html, const char *tag, int tagsize)
cols = strtol(attrbuf, NULL, 10);
} else {
if (html->DocType != DT_HTML || html->DocTypeVersion <= 4.01f)
- BUG_MSG("cols attribute is required for <textarea>\n");
+ BUG_MSG("<textarea> requires cols attribute.");
cols = 20;
}
if (cols < 1 || cols > MAX_COLS) {
int badCols = cols;
cols = (cols < 1 ? 20 : MAX_COLS);
- BUG_MSG("textarea cols=%d, using cols=%d instead\n", badCols, cols);
+ BUG_MSG("<textarea> cols=%d, using cols=%d instead.", badCols, cols);
}
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rows"))) {
rows = strtol(attrbuf, NULL, 10);
} else {
if (html->DocType != DT_HTML || html->DocTypeVersion <= 4.01f)
- BUG_MSG("rows attribute is required for <textarea>\n");
+ BUG_MSG("<textarea> requires rows attribute.");
rows = 10;
}
if (rows < 1 || rows > MAX_ROWS) {
int badRows = rows;
rows = (rows < 1 ? 2 : MAX_ROWS);
- BUG_MSG("textarea rows=%d, using rows=%d instead\n", badRows, rows);
+ BUG_MSG("<textarea> rows=%d, using rows=%d instead.", badRows, rows);
}
name = NULL;
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name")))
@@ -799,11 +799,11 @@ void Html_tag_close_select(DilloHtml *html)
void Html_tag_open_optgroup(DilloHtml *html, const char *tag, int tagsize)
{
if (!(html->InFlags & IN_SELECT)) {
- BUG_MSG("<optgroup> element outside <select>\n");
+ BUG_MSG("<optgroup> outside <select>.");
return;
}
if (html->InFlags & IN_OPTGROUP) {
- BUG_MSG("nested <optgroup>\n");
+ BUG_MSG("Nested <optgroup>.");
return;
}
if (html->InFlags & IN_OPTION) {
@@ -821,7 +821,7 @@ void Html_tag_open_optgroup(DilloHtml *html, const char *tag, int tagsize)
bool enabled = (a_Html_get_attr(html, tag, tagsize, "disabled") == NULL);
if (!label) {
- BUG_MSG("label attribute is required for <optgroup>\n");
+ BUG_MSG("<optgroup> requires label attribute.");
label = strdup("");
}
@@ -859,7 +859,7 @@ void Html_tag_close_optgroup(DilloHtml *html)
void Html_tag_open_option(DilloHtml *html, const char *tag, int tagsize)
{
if (!(html->InFlags & IN_SELECT)) {
- BUG_MSG("<option> element outside <select>\n");
+ BUG_MSG("<option> outside <select>.");
return;
}
if (html->InFlags & IN_OPTION)
@@ -918,7 +918,7 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize)
inp_type = DILLO_HTML_INPUT_BUTTON_SUBMIT;
} else {
inp_type = DILLO_HTML_INPUT_UNKNOWN;
- BUG_MSG("Unknown button type: \"%s\"\n", type);
+ BUG_MSG("<button> type unknown: '%s'.", type);
}
if (inp_type != DILLO_HTML_INPUT_UNKNOWN) {
@@ -945,9 +945,7 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize)
embed = new Embed(resource);
// a_Dw_button_set_sensitive (DW_BUTTON (button), FALSE);
- HT2TB(html)->addParbreak (5, html->wordStyle ());
HT2TB(html)->addWidget (embed, html->backgroundStyle ());
- HT2TB(html)->addParbreak (5, html->wordStyle ());
S_TOP(html)->textblock = html->dw = page;
diff --git a/src/gif.c b/src/gif.c
index 7ce1e110..adc9f49d 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -103,9 +103,7 @@ typedef struct {
size_t ColorMap_ofs;
uint_t ColorResolution;
uint_t NumColors;
-#if 0
- int Background;
-#endif
+ int Background;
uint_t spill_line_index;
#if 0
uint_t AspectRatio; /* AspectRatio (not used) */
@@ -165,9 +163,7 @@ void *a_Gif_new(DilloImage *Image, DilloUrl *url, int version)
gif->state = 0;
gif->Start_Ofs = 0;
gif->linebuf = NULL;
-#if 0
- gif->Background = -1;
-#endif
+ gif->Background = Image->bg_color;
gif->transparent = -1;
gif->num_spill_lines_max = 0;
gif->spill_lines = NULL;
@@ -222,7 +218,7 @@ static void Gif_write(DilloGif *gif, void *Buf, uint_t BufSize)
int bufsize, bytes_consumed;
/* Sanity checks */
- if (!Buf || !gif->Image || BufSize == 0)
+ if (!Buf || BufSize == 0)
return;
buf = ((uchar_t *) Buf) + gif->Start_Ofs;
@@ -804,8 +800,8 @@ static size_t Gif_do_img_desc(DilloGif *gif, void *Buf,
if (bsize < 10)
return 0;
- gif->Width = LM_to_uint(buf[4], buf[5]);
- gif->Height = LM_to_uint(buf[6], buf[7]);
+ gif->Width = LM_to_uint(buf[4], buf[5]);
+ gif->Height = LM_to_uint(buf[6], buf[7]);
/* check max image size */
if (gif->Width <= 0 || gif->Height <= 0 ||
@@ -820,6 +816,7 @@ static size_t Gif_do_img_desc(DilloGif *gif, void *Buf,
a_Dicache_set_parms(gif->url, gif->version, gif->Image,
gif->Width, gif->Height, DILLO_IMG_TYPE_INDEXED,
1 / 2.2);
+ gif->Image = NULL; /* safeguard: hereafter it may be freed by its owner */
Flags = buf[8];
@@ -850,8 +847,8 @@ static size_t Gif_do_img_desc(DilloGif *gif, void *Buf,
gif->spill_line_index = 0;
gif->linebuf = dMalloc(gif->Width);
gif->state = 3; /*Process the lzw data next */
- if (gif->Image && gif->ColorMap_ofs) {
- a_Dicache_set_cmap(gif->url, gif->version, gif->Image,
+ if (gif->ColorMap_ofs) {
+ a_Dicache_set_cmap(gif->url, gif->version, gif->Background,
(uchar_t *) Buf + gif->ColorMap_ofs,
gif->NumColors, 256, gif->transparent);
}
diff --git a/src/html.cc b/src/html.cc
index a8c70879..a1452858 100644
--- a/src/html.cc
+++ b/src/html.cc
@@ -26,6 +26,7 @@
#include "msg.h"
#include "binaryconst.h"
#include "colors.h"
+#include "html_charrefs.h"
#include "utf8.hh"
#include "misc.h"
@@ -132,9 +133,11 @@ void DilloHtml::bugMessage(const char *format, ... )
{
va_list argp;
+ if (bw->num_page_bugs)
+ dStr_append_c(bw->page_bugs, '\n');
dStr_sprintfa(bw->page_bugs,
"HTML warning: line %d, ",
- getCurTagLineNumber());
+ getCurrLineNumber());
va_start(argp, format);
dStr_vsprintfa(bw->page_bugs, format, argp);
va_end(argp);
@@ -158,15 +161,15 @@ DilloUrl *a_Html_url_new(DilloHtml *html,
const char *suffix = (n_ic) > 1 ? "s" : "";
n_ic_spc = URL_ILLEGAL_CHARS_SPC(url);
if (n_ic == n_ic_spc) {
- BUG_MSG("URL has %d illegal space%s\n", n_ic, suffix);
+ BUG_MSG("URL has %d illegal space%s ('%s').", n_ic, suffix, url_str);
} else if (n_ic_spc == 0) {
- BUG_MSG("URL has %d illegal character%s in {00-1F, 7F} range\n",
- n_ic, suffix);
+ BUG_MSG("URL has %d illegal byte%s in {00-1F, 7F-FF} range ('%s').",
+ n_ic, suffix, url_str);
} else {
- BUG_MSG("URL has %d illegal character%s: "
- "%d space%s, and %d in {00-1F, 7F} range\n",
+ BUG_MSG("URL has %d illegal byte%s: "
+ "%d space%s and %d in {00-1F, 7F-FF} range ('%s').",
n_ic, suffix,
- n_ic_spc, n_ic_spc > 1 ? "s" : "", n_ic-n_ic_spc);
+ n_ic_spc, n_ic_spc > 1 ? "s" : "", n_ic-n_ic_spc, url_str);
}
}
return url;
@@ -290,7 +293,7 @@ void a_Html_tag_set_align_attr(DilloHtml *html, const char *tag, int tagsize)
TextAlignType textAlignType = TEXT_ALIGN_LEFT;
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("The align attribute is obsolete in HTML5.\n");
+ BUG_MSG("The align attribute is obsolete in HTML5.");
if (dStrAsciiCasecmp (align, "left") == 0)
textAlignType = TEXT_ALIGN_LEFT;
@@ -334,7 +337,7 @@ bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag, int tagsize)
if ((attr = a_Html_get_attr(html, tag, tagsize, "valign"))) {
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("The valign attribute is obsolete in HTML5.\n");
+ BUG_MSG("The valign attribute is obsolete in HTML5.");
if (dStrAsciiCasecmp (attr, "top") == 0)
valign = VALIGN_TOP;
@@ -356,15 +359,24 @@ bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag, int tagsize)
/*
* Create and add a new Textblock to the current Textblock
*/
-static void Html_add_textblock(DilloHtml *html, int space)
+static void Html_add_textblock(DilloHtml *html, bool addBreaks, int breakSpace)
{
Textblock *textblock = new Textblock (prefs.limit_text_width);
- HT2TB(html)->addParbreak (space, html->wordStyle ());
- HT2TB(html)->addWidget (textblock, html->style ());
- HT2TB(html)->addParbreak (space, html->wordStyle ());
+ if (addBreaks)
+ HT2TB(html)->addParbreak (breakSpace, html->wordStyle ());
+ HT2TB(html)->addWidget (textblock, html->style ()); /* Works also for floats
+ etc. */
+ if (addBreaks)
+ HT2TB(html)->addParbreak (breakSpace, html->wordStyle ());
S_TOP(html)->textblock = html->dw = textblock;
- S_TOP(html)->hand_over_break = true;
+ if (addBreaks)
+ S_TOP(html)->hand_over_break = true;
+}
+
+static bool Html_will_textblock_be_out_of_flow(DilloHtml *html)
+{
+ return HT2TB(html)->isStyleOutOfFlow (html->style ());
}
/*
@@ -397,9 +409,8 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
stop_parser = false;
- CurrTagOfs = 0;
- OldTagOfs = 0;
- OldTagLine = 1;
+ CurrOfs = OldOfs = 0;
+ OldLine = 1;
DocType = DT_NONE; /* assume Tag Soup 0.0! :-) */
DocTypeVersion = 0.0f;
@@ -539,10 +550,10 @@ void DilloHtml::write(char *Buf, int BufSize, int Eof)
}
/*
- * Return the line number of the tag being processed by the parser.
+ * Return the line number of the tag/word being processed by the parser.
* Also update the offsets.
*/
-int DilloHtml::getCurTagLineNumber()
+int DilloHtml::getCurrLineNumber()
{
int i, ofs, line;
const char *p = Start_Buf;
@@ -551,13 +562,13 @@ int DilloHtml::getCurTagLineNumber()
/* Disable line counting for META hack. Buffers differ. */
dReturn_val_if((InFlags & IN_META_HACK), -1);
- ofs = CurrTagOfs;
- line = OldTagLine;
- for (i = OldTagOfs; i < ofs; ++i)
+ ofs = CurrOfs;
+ line = OldLine;
+ for (i = OldOfs; i < ofs; ++i)
if (p[i] == '\n' || (p[i] == '\r' && p[i+1] != '\n'))
++line;
- OldTagOfs = CurrTagOfs;
- OldTagLine = line;
+ OldOfs = CurrOfs;
+ OldLine = line;
return line;
}
@@ -787,113 +798,16 @@ void a_Html_stash_init(DilloHtml *html)
dStr_truncate(html->Stash, 0);
}
-/* Entities list from the HTML 4.01 DTD */
-typedef struct {
- const char *entity;
- int isocode;
-} Ent_t;
-
-#define NumEnt 252
-static const Ent_t Entities[NumEnt] = {
- {"AElig",0306}, {"Aacute",0301}, {"Acirc",0302}, {"Agrave",0300},
- {"Alpha",01621},{"Aring",0305}, {"Atilde",0303}, {"Auml",0304},
- {"Beta",01622}, {"Ccedil",0307}, {"Chi",01647}, {"Dagger",020041},
- {"Delta",01624},{"ETH",0320}, {"Eacute",0311}, {"Ecirc",0312},
- {"Egrave",0310},{"Epsilon",01625},{"Eta",01627}, {"Euml",0313},
- {"Gamma",01623},{"Iacute",0315}, {"Icirc",0316}, {"Igrave",0314},
- {"Iota",01631}, {"Iuml",0317}, {"Kappa",01632}, {"Lambda",01633},
- {"Mu",01634}, {"Ntilde",0321}, {"Nu",01635}, {"OElig",0522},
- {"Oacute",0323},{"Ocirc",0324}, {"Ograve",0322}, {"Omega",01651},
- {"Omicron",01637},{"Oslash",0330},{"Otilde",0325},{"Ouml",0326},
- {"Phi",01646}, {"Pi",01640}, {"Prime",020063},{"Psi",01650},
- {"Rho",01641}, {"Scaron",0540}, {"Sigma",01643}, {"THORN",0336},
- {"Tau",01644}, {"Theta",01630}, {"Uacute",0332}, {"Ucirc",0333},
- {"Ugrave",0331},{"Upsilon",01645},{"Uuml",0334}, {"Xi",01636},
- {"Yacute",0335},{"Yuml",0570}, {"Zeta",01626}, {"aacute",0341},
- {"acirc",0342}, {"acute",0264}, {"aelig",0346}, {"agrave",0340},
- {"alefsym",020465},{"alpha",01661},{"amp",38}, {"and",021047},
- {"ang",021040}, {"aring",0345}, {"asymp",021110},{"atilde",0343},
- {"auml",0344}, {"bdquo",020036},{"beta",01662}, {"brvbar",0246},
- {"bull",020042},{"cap",021051}, {"ccedil",0347}, {"cedil",0270},
- {"cent",0242}, {"chi",01707}, {"circ",01306}, {"clubs",023143},
- {"cong",021105},{"copy",0251}, {"crarr",020665},{"cup",021052},
- {"curren",0244},{"dArr",020723}, {"dagger",020040},{"darr",020623},
- {"deg",0260}, {"delta",01664}, {"diams",023146},{"divide",0367},
- {"eacute",0351},{"ecirc",0352}, {"egrave",0350}, {"empty",021005},
- {"emsp",020003},{"ensp",020002}, {"epsilon",01665},{"equiv",021141},
- {"eta",01667}, {"eth",0360}, {"euml",0353}, {"euro",020254},
- {"exist",021003},{"fnof",0622}, {"forall",021000},{"frac12",0275},
- {"frac14",0274},{"frac34",0276}, {"frasl",020104},{"gamma",01663},
- {"ge",021145}, {"gt",62}, {"hArr",020724}, {"harr",020624},
- {"hearts",023145},{"hellip",020046},{"iacute",0355},{"icirc",0356},
- {"iexcl",0241}, {"igrave",0354}, {"image",020421},{"infin",021036},
- {"int",021053}, {"iota",01671}, {"iquest",0277}, {"isin",021010},
- {"iuml",0357}, {"kappa",01672}, {"lArr",020720}, {"lambda",01673},
- {"lang",021451},{"laquo",0253}, {"larr",020620}, {"lceil",021410},
- {"ldquo",020034},{"le",021144}, {"lfloor",021412},{"lowast",021027},
- {"loz",022712}, {"lrm",020016}, {"lsaquo",020071},{"lsquo",020030},
- {"lt",60}, {"macr",0257}, {"mdash",020024},{"micro",0265},
- {"middot",0267},{"minus",021022},{"mu",01674}, {"nabla",021007},
- {"nbsp",0240}, {"ndash",020023},{"ne",021140}, {"ni",021013},
- {"not",0254}, {"notin",021011},{"nsub",021204}, {"ntilde",0361},
- {"nu",01675}, {"oacute",0363}, {"ocirc",0364}, {"oelig",0523},
- {"ograve",0362},{"oline",020076},{"omega",01711}, {"omicron",01677},
- {"oplus",021225},{"or",021050}, {"ordf",0252}, {"ordm",0272},
- {"oslash",0370},{"otilde",0365}, {"otimes",021227},{"ouml",0366},
- {"para",0266}, {"part",021002}, {"permil",020060},{"perp",021245},
- {"phi",01706}, {"pi",01700}, {"piv",01726}, {"plusmn",0261},
- {"pound",0243}, {"prime",020062},{"prod",021017}, {"prop",021035},
- {"psi",01710}, {"quot",34}, {"rArr",020722}, {"radic",021032},
- {"rang",021452},{"raquo",0273}, {"rarr",020622}, {"rceil",021411},
- {"rdquo",020035},{"real",020434},{"reg",0256}, {"rfloor",021413},
- {"rho",01701}, {"rlm",020017}, {"rsaquo",020072},{"rsquo",020031},
- {"sbquo",020032},{"scaron",0541},{"sdot",021305}, {"sect",0247},
- {"shy",0255}, {"sigma",01703}, {"sigmaf",01702},{"sim",021074},
- {"spades",023140},{"sub",021202},{"sube",021206}, {"sum",021021},
- {"sup",021203}, {"sup1",0271}, {"sup2",0262}, {"sup3",0263},
- {"supe",021207},{"szlig",0337}, {"tau",01704}, {"there4",021064},
- {"theta",01670},{"thetasym",01721},{"thinsp",020011},{"thorn",0376},
- {"tilde",01334},{"times",0327}, {"trade",020442},{"uArr",020721},
- {"uacute",0372},{"uarr",020621}, {"ucirc",0373}, {"ugrave",0371},
- {"uml",0250}, {"upsih",01722}, {"upsilon",01705},{"uuml",0374},
- {"weierp",020430},{"xi",01676}, {"yacute",0375}, {"yen",0245},
- {"yuml",0377}, {"zeta",01666}, {"zwj",020015}, {"zwnj",020014}
-};
-
-
-/*
- * Comparison function for binary search
- */
-static int Html_entity_comp(const void *a, const void *b)
-{
- return strcmp(((Ent_t *)a)->entity, ((Ent_t *)b)->entity);
-}
-
-/*
- * Binary search of 'key' in entity list
- */
-static int Html_entity_search(char *key)
-{
- Ent_t *res, EntKey;
-
- EntKey.entity = key;
- res = (Ent_t*) bsearch(&EntKey, Entities, NumEnt,
- sizeof(Ent_t), Html_entity_comp);
- if (res)
- return (res - Entities);
- return -1;
-}
-
/*
* This is M$ non-standard "smart quotes" (w1252). Now even deprecated by them!
*
* SGML for HTML4.01 defines c >= 128 and c <= 159 as UNUSED.
- * TODO: Probably I should remove this hack, and add a HTML warning. --Jcid
+ * TODO: Probably I should remove this hack. --Jcid
*/
-static int Html_ms_stupid_quotes_2ucs(int isocode)
+static int Html_ms_stupid_quotes_2ucs(int codepoint)
{
int ret;
- switch (isocode) {
+ switch (codepoint) {
case 145:
case 146: ret = '\''; break;
case 147:
@@ -901,130 +815,233 @@ static int Html_ms_stupid_quotes_2ucs(int isocode)
case 149: ret = 176; break;
case 150:
case 151: ret = '-'; break;
- default: ret = isocode; break;
+ default: ret = codepoint; break;
}
return ret;
}
/*
- * Given an entity, return the UCS character code.
- * Returns a negative value (error code) if not a valid entity.
- *
- * The first character *token is assumed to be == '&'
- *
- * For valid entities, *entsize is set to the length of the parsed entity.
+ * Parse a numeric character reference (e.g., "&#47;" or "&#x2F;").
+ * The "&#" has already been consumed.
*/
-static int Html_parse_entity(DilloHtml *html, const char *token,
- int toksize, int *entsize)
+static const char *Html_parse_numeric_charref(DilloHtml *html, char *tok,
+ bool_t is_attr, int *entsize)
{
- int isocode, i;
- char *tok, *s, c;
+ static char buf[5];
+ char *s = tok;
+ int n, codepoint = -1;
- token++;
- tok = s = toksize ? dStrndup(token, (uint_t)toksize) : dStrdup(token);
-
- isocode = -1;
-
- if (*s == '#') {
- /* numeric character reference */
- errno = 0;
- if (*++s == 'x' || *s == 'X') {
- if (isxdigit(*++s)) {
- /* strtol with base 16 accepts leading "0x" - we don't */
- if (*s == '0' && s[1] == 'x') {
- s++;
- isocode = 0;
- } else {
- isocode = strtol(s, &s, 16);
- }
+ errno = 0;
+
+ if (*s == 'x' || *s == 'X') {
+ if (isxdigit(*++s)) {
+ /* strtol with base 16 accepts leading "0x" - we don't */
+ if (*s == '0' && s[1] == 'x') {
+ s++;
+ codepoint = 0;
+ } else {
+ codepoint = strtol(s, &s, 16);
}
- } else if (isdigit(*s)) {
- isocode = strtol(s, &s, 10);
}
+ } else if (isdigit(*s)) {
+ codepoint = strtol(s, &s, 10);
+ }
+ if (errno)
+ codepoint = -1;
- if (!isocode || errno || isocode > 0xffff) {
- /* this catches null bytes, errors and codes >= 0xFFFF */
- BUG_MSG("numeric character reference \"%s\" out of range\n", tok);
- isocode = -2;
+ if (*s == ';')
+ s++;
+ else {
+ if (prefs.show_extra_warnings && (html->DocType == DT_XHTML ||
+ (html->DocType == DT_HTML && html->DocTypeVersion <= 4.01f))) {
+ char c = *s;
+ *s = '\0';
+ BUG_MSG("Character reference '&#%s' lacks ';'.", tok);
+ *s = c;
}
-
- if (isocode != -1) {
- if (*s == ';')
- s++;
- else if (prefs.show_extra_warnings)
- BUG_MSG("numeric character reference without trailing ';'\n");
+ /* Don't require ';' for old HTML, except that our current heuristic
+ * is to require it in attributes to avoid cases like "&copy=1" found
+ * in URLs.
+ */
+ if (is_attr || html->DocType == DT_XHTML ||
+ (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)) {
+ return NULL;
}
- } else if (isalpha(*s)) {
- /* character entity reference */
- while (*++s && (isalnum(*s) || strchr(":_.-", *s))) ;
- c = *s;
- *s = 0;
+ }
+ if ((codepoint < 0x20 && codepoint != '\t' && codepoint != '\n' &&
+ codepoint != '\f') ||
+ (codepoint >= 0x7f && codepoint <= 0x9f) ||
+ (codepoint >= 0xd800 && codepoint <= 0xdfff) || codepoint > 0x10ffff ||
+ ((codepoint & 0xfffe) == 0xfffe) ||
+ (!(html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f) &&
+ codepoint > 0xffff)) {
+ /* this catches null bytes, errors, codes out of range, disallowed
+ * control chars, permanently undefined chars, and surrogates.
+ */
+ char c = *s;
+ *s = '\0';
+ BUG_MSG("Numeric character reference '&#%s' is not valid.", tok);
+ *s = c;
- if ((i = Html_entity_search(tok)) >= 0) {
- isocode = Entities[i].isocode;
+ codepoint = (codepoint >= 145 && codepoint <= 151) ?
+ Html_ms_stupid_quotes_2ucs(codepoint) : -1;
+ }
+ if (codepoint != -1) {
+ if (codepoint >= 128) {
+ n = a_Utf8_encode(codepoint, buf);
} else {
- if (html->DocType == DT_XHTML && !strcmp(tok, "apos")) {
- isocode = 0x27;
- } else {
- if ((html->DocType == DT_HTML && html->DocTypeVersion == 4.01f) ||
- html->DocType == DT_XHTML)
- BUG_MSG("undefined character entity '%s'\n", tok);
- isocode = -3;
- }
+ n = 1;
+ buf[0] = (char) codepoint;
+ }
+ assert(n < 5);
+ buf[n] = '\0';
+ *entsize = s-tok+2;
+ return buf;
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Comparison function for binary search
+ */
+static int Html_charref_comp(const void *a, const void *b)
+{
+ return strcmp(((Charref_t *)a)->ref, ((Charref_t *)b)->ref);
+}
+
+/*
+ * Binary search of 'key' in charref list
+ */
+static Charref_t *Html_charref_search(char *key)
+{
+ Charref_t RefKey;
+
+ RefKey.ref = key;
+ return (Charref_t*) bsearch(&RefKey, Charrefs, NumRef,
+ sizeof(Charref_t), Html_charref_comp);
+}
+
+/*
+ * Parse a named character reference (e.g., "&amp;" or "&hellip;").
+ * The "&" has already been consumed.
+ */
+static const char *Html_parse_named_charref(DilloHtml *html, char *tok,
+ bool_t is_attr, int *entsize)
+{
+ Charref_t *p;
+ char c;
+ char *s = tok;
+ const char *ret = NULL;
+
+ while (*++s && (isalnum(*s) || strchr(":_.-", *s))) ;
+ c = *s;
+ *s = '\0';
+ if (c != ';') {
+ if (prefs.show_extra_warnings && (html->DocType == DT_XHTML ||
+ (html->DocType == DT_HTML && html->DocTypeVersion <= 4.01f)))
+ BUG_MSG("Character reference '&%s' lacks ';'.", tok);
+
+ /* Don't require ';' for old HTML, except that our current heuristic
+ * is to require it in attributes to avoid cases like "&copy=1" found
+ * in URLs.
+ */
+ if (is_attr || html->DocType == DT_XHTML ||
+ (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)) {
+ return ret;
}
- if (c == ';')
- s++;
- else if (prefs.show_extra_warnings)
- BUG_MSG("character entity reference without trailing ';'\n");
}
+ if ((p = Html_charref_search(tok))) {
+ ret = (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f) ?
+ p->html5_str : p->html4_str;
+ }
+
+ if (!ret && html->DocType == DT_XHTML && !strcmp(tok, "apos"))
+ ret = "'";
+
+ *s = c;
+ if (c == ';')
+ s++;
+
+ if (!ret) {
+ c = *s;
+ *s = '\0';
+ BUG_MSG("Undefined character reference '&%s'.", tok);
+ *s = c;
+ }
*entsize = s-tok+1;
- dFree(tok);
+ return ret;
+}
+
+/*
+ * Given an entity, return the corresponding string.
+ * Returns NULL if not a valid entity.
+ *
+ * The first character *token is assumed to be == '&'
+ *
+ * For valid entities, *entsize is set to the length of the parsed entity.
+ */
+static const char *Html_parse_entity(DilloHtml *html, const char *token,
+ int toksize, int *entsize, bool_t is_attr)
+{
+ const char *ret = NULL;
+ char *tok;
- if (isocode >= 145 && isocode <= 151) {
- /* TODO: remove this hack. */
- isocode = Html_ms_stupid_quotes_2ucs(isocode);
- } else if (isocode == -1 && prefs.show_extra_warnings)
- BUG_MSG("literal '&'\n");
+ token++;
+ tok = dStrndup(token, (uint_t)toksize);
+
+ if (*tok == '#') {
+ ret = Html_parse_numeric_charref(html, tok+1, is_attr, entsize);
+ } else if (isalpha(*tok)) {
+ ret = Html_parse_named_charref(html, tok, is_attr, entsize);
+ } else if (prefs.show_extra_warnings &&
+ (!(html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f))) {
+ // HTML5 doesn't mind literal '&'s.
+ BUG_MSG("Literal '&'.");
+ }
+ dFree(tok);
- return isocode;
+ return ret;
}
/*
- * Convert all the entities in a token to utf8 encoding. Takes
- * a token and its length, and returns a newly allocated string.
+ * Parse all the entities in a token. Takes the token and its length, and
+ * returns a newly allocated string.
*/
char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize)
{
const char *esc_set = "&";
- char *new_str, buf[4];
- int i, j, k, n, s, isocode, entsize;
-
- new_str = dStrndup(token, toksize);
- s = strcspn(new_str, esc_set);
- if (new_str[s] == 0)
- return new_str;
-
- for (i = j = s; i < toksize; i++) {
- if (token[i] == '&' &&
- (isocode = Html_parse_entity(html, token+i,
- toksize-i, &entsize)) >= 0) {
- if (isocode >= 128) {
- /* multibyte encoding */
- n = a_Utf8_encode(isocode, buf);
- for (k = 0; k < n; ++k)
- new_str[j++] = buf[k];
+ int i, s, entsize;
+ char *str;
+
+ s = strcspn(token, esc_set);
+ if (s >= toksize) {
+ /* no ampersands */
+ str = dStrndup(token, toksize);
+ } else {
+ Dstr *ds = dStr_sized_new(toksize);
+
+ dStr_append_l(ds, token, s);
+
+ for (i = s; i < toksize; i++) {
+ const char *entstr;
+ const bool_t is_attr = FALSE;
+
+ if (token[i] == '&' &&
+ (entstr = Html_parse_entity(html, token+i, toksize-i, &entsize,
+ is_attr))) {
+ dStr_append(ds, entstr);
+ i += entsize-1;
} else {
- new_str[j++] = (char) isocode;
+ dStr_append_c(ds, token[i]);
}
- i += entsize-1;
- } else {
- new_str[j++] = token[i];
}
+ str = ds->str;
+ dStr_free(ds, 0);
}
- new_str[j] = '\0';
- return new_str;
+ return str;
}
/*
@@ -1095,7 +1112,7 @@ static void Html_process_space(DilloHtml *html, const char *space,
break;
case '\t':
if (prefs.show_extra_warnings)
- BUG_MSG("TAB character inside <PRE>\n");
+ BUG_MSG("TAB character inside <pre>.");
offset = TAB_SIZE - html->pre_column % TAB_SIZE;
spaceCnt += offset;
html->pre_column += offset;
@@ -1314,7 +1331,7 @@ static void Html_tag_cleanup_to_idx(DilloHtml *html, int idx)
int toptag_idx = S_TOP(html)->tag_idx;
TagInfo toptag = Tags[toptag_idx];
if (s_sz > idx + 1 && toptag.EndTag != 'O')
- BUG_MSG(" - forcing close of open tag: <%s>\n", toptag.name);
+ BUG_MSG(" - forcing close of open tag: <%s>.", toptag.name);
_MSG("Close: %*s%s\n", size," ", toptag.name);
if (toptag.close)
toptag.close(html);
@@ -1372,10 +1389,10 @@ static void Html_tag_cleanup_at_close(DilloHtml *html, int new_idx)
if (matched) {
Html_tag_cleanup_to_idx(html, stack_idx);
} else if (expected) {
- BUG_MSG("unexpected closing tag: </%s> -- expected </%s>.\n",
+ BUG_MSG("Unexpected closing tag: </%s> -- expected </%s>.",
new_tag.name, Tags[tag_idx].name);
} else {
- BUG_MSG("unexpected closing tag: </%s>.\n", new_tag.name);
+ BUG_MSG("Unexpected closing tag: </%s>.", new_tag.name);
}
}
@@ -1411,7 +1428,7 @@ static void Html_tag_cleanup_nested_inputs(DilloHtml *html, int new_idx)
}
if (matched) {
- BUG_MSG("attempt to nest <%s> element inside <%s> -- closing <%s>\n",
+ BUG_MSG("Attempt to nest <%s> element inside <%s> -- closing <%s>.",
Tags[new_idx].name, Tags[u_idx].name, Tags[u_idx].name);
Html_tag_cleanup_to_idx(html, stack_idx);
} else {
@@ -1481,7 +1498,7 @@ CssLength a_Html_parse_length (DilloHtml *html, const char *attr)
else {
/* allow only whitespaces */
if (*end && !isspace (*end)) {
- BUG_MSG("Garbage after length: %s\n", attr);
+ BUG_MSG("Garbage after length: '%s'.", attr);
l = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
}
}
@@ -1501,7 +1518,7 @@ int32_t a_Html_color_parse(DilloHtml *html, const char *str,
int32_t color = a_Color_parse(str, default_color, &err);
if (err) {
- BUG_MSG("color \"%s\" is not in \"#RRGGBB\" format\n", str);
+ BUG_MSG("Color '%s' is not in \"#RRGGBB\" format.", str);
}
return color;
}
@@ -1518,8 +1535,8 @@ static int
bool valid = *val && !strchr(val, ' ');
if (!valid) {
- BUG_MSG("'%s' value must not be empty and must not contain spaces.\n",
- attrname);
+ BUG_MSG("'%s' value \"%s\" must not be empty and must not contain "
+ "spaces.", attrname, val);
}
return valid ? 1 : 0;
} else {
@@ -1530,8 +1547,8 @@ static int
break;
if (val[i] || !(isascii(val[0]) && isalpha(val[0])))
- BUG_MSG("'%s' value \"%s\" is not of the form "
- "[A-Za-z][A-Za-z0-9:_.-]*\n", attrname, val);
+ BUG_MSG("%s attribute value \"%s\" is not of the form "
+ "'[A-Za-z][A-Za-z0-9:_.-]*'.", attrname, val);
return !(val[i]);
}
@@ -1559,7 +1576,6 @@ static int
static void Html_parse_doctype(DilloHtml *html, const char *tag, int tagsize)
{
static const char HTML_SGML_sig [] = "<!DOCTYPE HTML PUBLIC ";
- static const char HTML5_sig [] = "<!DOCTYPE html>";
static const char HTML20 [] = "-//IETF//DTD HTML";
static const char HTML32 [] = "-//W3C//DTD HTML 3.2";
static const char HTML40 [] = "-//W3C//DTD HTML 4.0";
@@ -1596,7 +1612,7 @@ static void Html_parse_doctype(DilloHtml *html, const char *tag, int tagsize)
_MSG("New: {%s}\n", ntag);
if (html->DocType != DT_NONE)
- BUG_MSG("Multiple DOCTYPE declarations.\n");
+ BUG_MSG("Multiple DOCTYPE declarations.");
/* The default DT_NONE type is TagSoup */
if (i > strlen(HTML_SGML_sig) && // avoid out of bounds reads!
@@ -1624,13 +1640,14 @@ static void Html_parse_doctype(DilloHtml *html, const char *tag, int tagsize)
html->DocType = DT_HTML;
html->DocTypeVersion = 2.0f;
}
- } else if (!dStrAsciiCasecmp(ntag, HTML5_sig)) {
+ } else if (!dStrAsciiCasecmp(ntag, "<!DOCTYPE html>") ||
+ !dStrAsciiCasecmp(ntag, "<!DOCTYPE html >")) {
html->DocType = DT_HTML;
html->DocTypeVersion = 5.0f;
}
if (html->DocType == DT_NONE) {
html->DocType = DT_UNRECOGNIZED;
- BUG_MSG("DOCTYPE not recognized:\n%s.\n", ntag);
+ BUG_MSG("DOCTYPE not recognized: ('%s').", ntag);
}
dFree(ntag);
}
@@ -1649,7 +1666,7 @@ static void Html_tag_open_html(DilloHtml *html, const char *tag, int tagsize)
++html->Num_HTML;
if (html->Num_HTML > 1) {
- BUG_MSG("HTML element was already open\n");
+ BUG_MSG("<html> was already open.");
html->ReqTagClose = true;
}
}
@@ -1668,7 +1685,7 @@ static void Html_tag_close_html(DilloHtml *html)
static void Html_tag_open_head(DilloHtml *html, const char *tag, int tagsize)
{
if (html->InFlags & IN_BODY) {
- BUG_MSG("HEAD element must go before the BODY section\n");
+ BUG_MSG("<head> must go before the BODY section.");
html->ReqTagClose = true;
return;
}
@@ -1676,10 +1693,10 @@ static void Html_tag_open_head(DilloHtml *html, const char *tag, int tagsize)
if (html->Num_HEAD < UCHAR_MAX)
++html->Num_HEAD;
if (html->InFlags & IN_HEAD) {
- BUG_MSG("HEAD element was already open\n");
+ BUG_MSG("<head> was already open.");
html->ReqTagClose = true;
} else if (html->Num_HEAD > 1) {
- BUG_MSG("HEAD section already finished -- ignoring\n");
+ BUG_MSG("<head> already finished -- ignoring.");
html->ReqTagClose = true;
} else {
html->InFlags |= IN_HEAD;
@@ -1696,7 +1713,7 @@ static void Html_tag_close_head(DilloHtml *html)
if (html->Num_HEAD == 1) {
/* match for the well formed start of HEAD section */
if (html->Num_TITLE == 0)
- BUG_MSG("HEAD section lacks the TITLE element\n");
+ BUG_MSG("<head> lacks <title>.");
html->InFlags &= ~IN_HEAD;
@@ -1726,9 +1743,9 @@ static void Html_tag_open_title(DilloHtml *html, const char *tag, int tagsize)
if (html->Num_TITLE < UCHAR_MAX)
++html->Num_TITLE;
if (html->Num_TITLE > 1)
- BUG_MSG("A redundant TITLE element was found\n");
+ BUG_MSG("Redundant <title>.");
} else {
- BUG_MSG("TITLE element must be inside the HEAD section -- ignoring\n");
+ BUG_MSG("<title> must be inside <head> -- ignoring.");
}
}
@@ -1776,7 +1793,7 @@ static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize)
if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) {
if (html->DocType != DT_HTML || html->DocTypeVersion <= 4.01f)
- BUG_MSG("type attribute is required for <style>\n");
+ BUG_MSG("<style> requires type attribute.");
} else if (dStrAsciiCasecmp(attrbuf, "text/css")) {
html->loadCssFromStash = false;
}
@@ -1800,8 +1817,8 @@ static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_style(DilloHtml *html)
{
if (prefs.parse_embedded_css && html->loadCssFromStash)
- html->styleEngine->parse(html, html->base_url, html->Stash->str, html->Stash->len,
- CSS_ORIGIN_AUTHOR);
+ html->styleEngine->parse(html, html->base_url, html->Stash->str,
+ html->Stash->len, CSS_ORIGIN_AUTHOR);
}
/*
@@ -1825,21 +1842,21 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
++html->Num_BODY;
if (html->Num_BODY > 1) {
- BUG_MSG("BODY element was already open\n");
+ BUG_MSG("<body> was already open.");
html->ReqTagClose = true;
return;
}
if (html->InFlags & IN_HEAD) {
/* if we're here, it's bad XHTML, no need to recover */
- BUG_MSG("unclosed HEAD element\n");
+ BUG_MSG("Unclosed <head>.");
}
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
color = a_Html_color_parse(html, attrbuf, -1);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<body> bgcolor attribute is obsolete.\n");
+ BUG_MSG("<body> bgcolor attribute is obsolete.");
if (color != -1)
html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
@@ -1850,7 +1867,7 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
color = a_Html_color_parse(html, attrbuf, -1);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<body> text attribute is obsolete.\n");
+ BUG_MSG("<body> text attribute is obsolete.");
if (color != -1)
html->styleEngine->setNonCssHint (CSS_PROPERTY_COLOR,
@@ -1862,13 +1879,13 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "link"))) {
html->non_css_link_color = a_Html_color_parse(html, attrbuf, -1);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<body> link attribute is obsolete.\n");
+ BUG_MSG("<body> link attribute is obsolete.");
}
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vlink"))) {
html->non_css_visited_color = a_Html_color_parse(html, attrbuf, -1);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<body> vlink attribute is obsolete.\n");
+ BUG_MSG("<body> vlink attribute is obsolete.");
}
html->dw->setStyle (html->style ());
@@ -2012,7 +2029,7 @@ static void Html_tag_content_frameset (DilloHtml *html,
{
HT2TB(html)->addParbreak (9, html->wordStyle ());
HT2TB(html)->addText("--FRAME--", html->wordStyle ());
- Html_add_textblock(html, 5);
+ Html_add_textblock(html, true, 5);
}
/*
@@ -2089,8 +2106,8 @@ void a_Html_common_image_attrs(DilloHtml *html, const char *tag, int tagsize)
{
char *width_ptr, *height_ptr;
const char *attrbuf;
- CssLength l_w = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
- CssLength l_h = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
+ CssLength l_w = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
+ CssLength l_h = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
int w = 0, h = 0;
if (prefs.show_tooltip &&
@@ -2123,7 +2140,7 @@ void a_Html_common_image_attrs(DilloHtml *html, const char *tag, int tagsize)
*/
if (w < 0 || h < 0 ||
w > IMAGE_MAX_AREA || h > IMAGE_MAX_AREA ||
- (h > 0 && w > IMAGE_MAX_AREA / h)) {
+ (h > 0 && w > IMAGE_MAX_AREA / h)) {
dFree(width_ptr);
dFree(height_ptr);
width_ptr = height_ptr = NULL;
@@ -2176,6 +2193,8 @@ DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, int tagsize)
dw::Image *dw = new dw::Image(alt_ptr);
image =
a_Image_new(html->dw->getLayout(), (void*)(dw::core::ImgRenderer*)dw, 0);
+
+ a_Image_ref(image);
if (HT2TB(html)->getBgColor())
image->bg_color = HT2TB(html)->getBgColor()->getColor();
@@ -2192,10 +2211,10 @@ DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, int tagsize)
if (load_now && Html_load_image(html->bw, url, html->page_url, image)) {
// hi->image is NULL if dillo tries to load the image immediately
hi->image = NULL;
+ a_Image_unref(image);
} else {
// otherwise a reference is kept in html->images
hi->image = image;
- a_Image_ref(image);
}
dFree(alt_ptr);
@@ -2338,7 +2357,7 @@ static void Html_tag_content_map(DilloHtml *html, const char *tag, int tagsize)
DilloUrl *url;
if (html->InFlags & IN_MAP) {
- BUG_MSG("nested <map>\n");
+ BUG_MSG("Nested <map>.");
} else {
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
html->InFlags |= IN_MAP;
@@ -2348,7 +2367,7 @@ static void Html_tag_content_map(DilloHtml *html, const char *tag, int tagsize)
a_Url_free (url);
dFree(hash_name);
} else {
- BUG_MSG("name attribute is required for <map>\n");
+ BUG_MSG("<map> requires name attribute.");
}
}
}
@@ -2400,7 +2419,7 @@ misc::SimpleVector<int> *Html_read_coords(DilloHtml *html, const char *str)
if (!*newtail)
break;
if (*newtail != ',') {
- BUG_MSG("area coords must be integers separated by commas.\n");
+ BUG_MSG("<area> coords must be integers separated by commas.");
}
tail = newtail + 1;
}
@@ -2423,7 +2442,7 @@ static void
Shape *shape = NULL;
if (!(html->InFlags & IN_MAP)) {
- BUG_MSG("<area> element not inside <map>\n");
+ BUG_MSG("<area> not inside <map>.");
return;
}
attrbuf = a_Html_get_attr(html, tag, tagsize, "shape");
@@ -2439,7 +2458,7 @@ static void
} else if (dStrnAsciiCasecmp(attrbuf, "poly", 4) == 0) {
type = POLYGON;
} else {
- BUG_MSG("<area> unknown shape: \"%s\"\n", attrbuf);
+ BUG_MSG("<area> unknown shape: '%s'.", attrbuf);
type = UNKNOWN;
}
if (type == RECTANGLE || type == CIRCLE || type == POLYGON) {
@@ -2449,7 +2468,7 @@ static void
if (type == RECTANGLE) {
if (coords->size() != 4)
- BUG_MSG("<area> rectangle must have four coordinate values\n");
+ BUG_MSG("<area> rectangle must have four coordinate values.");
if (coords->size() >= 4)
shape = new Rectangle(coords->get(0),
coords->get(1),
@@ -2457,7 +2476,7 @@ static void
coords->get(3) - coords->get(1));
} else if (type == CIRCLE) {
if (coords->size() != 3)
- BUG_MSG("<area> circle must have three coordinate values\n");
+ BUG_MSG("<area> circle must have three coordinate values.");
if (coords->size() >= 3)
shape = new Circle(coords->get(0), coords->get(1),
coords->get(2));
@@ -2465,7 +2484,7 @@ static void
Polygon *poly;
int i;
if (coords->size() % 2)
- BUG_MSG("<area> polygon with odd number of coordinates\n");
+ BUG_MSG("<area> polygon with odd number of coordinates.");
shape = poly = new Polygon();
for (i = 0; i < (coords->size() / 2); i++)
poly->addPoint(coords->get(2*i), coords->get(2*i + 1));
@@ -2601,11 +2620,11 @@ static void Html_tag_open_source(DilloHtml *html, const char *tag,
const char *attrbuf;
if (!(html->InFlags & IN_MEDIA)) {
- BUG_MSG("<source> element not inside a media element.\n");
+ BUG_MSG("<source> not inside a media element.");
return;
}
if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "src"))) {
- BUG_MSG("src attribute is required in <source> element.\n");
+ BUG_MSG("<source> requires src attribute.");
return;
} else {
DilloUrl *url = a_Html_url_new(html, attrbuf, NULL, 0);
@@ -2682,7 +2701,7 @@ static const char* Html_get_javascript_link(DilloHtml *html)
if ((ch == '"' || ch == '\'') &&
(p2 = strchr(Buf->str + i + 1 , ch))) {
p1 = Buf->str + i;
- BUG_MSG("link depends on javascript()\n");
+ BUG_MSG("Link depends on javascript().");
dStr_truncate(Buf, p2 - Buf->str);
dStr_erase(Buf, 0, p1 - Buf->str + 1);
}
@@ -2697,7 +2716,8 @@ static void Html_add_anchor(DilloHtml *html, const char *name)
{
_MSG("Registering ANCHOR: %s\n", name);
if (!HT2TB(html)->addAnchor (name, html->style ()))
- BUG_MSG("Anchor names must be unique within the document ('%s')\n",name);
+ BUG_MSG("Anchor names must be unique within the document (\"%s\").",
+ name);
/*
* According to Sec. 12.2.1 of the HTML 4.01 spec, "anchor names that
* differ only in case may not appear in the same document", but
@@ -2767,7 +2787,8 @@ static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize)
/* We compare the "id" value with the url-decoded "name" value */
if (!id || strcmp(nameVal, id)) {
if (id)
- BUG_MSG("'id' and 'name' attribute of <a> tag differ\n");
+ BUG_MSG("In <a>, id ('%s') and name ('%s') attributes differ.",
+ id, nameVal);
Html_add_anchor(html, nameVal);
}
@@ -2790,7 +2811,7 @@ static void Html_tag_close_a(DilloHtml *html)
static void Html_tag_open_blockquote(DilloHtml *html,
const char *tag, int tagsize)
{
- Html_add_textblock(html, 9);
+ Html_add_textblock(html, true, 9);
}
/*
@@ -2843,7 +2864,7 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize)
html->styleEngine->setNonCssHint (CSS_PROPERTY_LIST_STYLE_TYPE,
CSS_TYPE_ENUM, list_style_type);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<ul> type attribute is obsolete.\n");
+ BUG_MSG("<ul> type attribute is obsolete.");
}
S_TOP(html)->list_type = HTML_LIST_UNORDERED;
@@ -2865,7 +2886,7 @@ static void Html_tag_open_dir(DilloHtml *html, const char *tag, int tagsize)
S_TOP(html)->ref_list_item = NULL;
if (prefs.show_extra_warnings)
- BUG_MSG("Obsolete list type; use <UL> instead\n");
+ BUG_MSG("Obsolete list type; use <ul> instead.");
}
/*
@@ -2873,7 +2894,16 @@ static void Html_tag_open_dir(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_menu(DilloHtml *html, const char *tag, int tagsize)
{
- Html_tag_open_dir(html, tag, tagsize);
+ /* In another bit of ridiculous mess from the HTML5 world, the menu
+ * element, which was deprecated in HTML4:
+ * - does not appear at all in W3C's HTML5 spec
+ * - appears in WHATWG's HTML5 doc and the W3C's 5.1 draft, where it
+ * means something totally different than it did in the old days
+ * (now it's for popup menus and toolbar menus rather than being a
+ * sort of list).
+ */
+ if (!(html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f))
+ Html_tag_open_dir(html, tag, tagsize);
}
/*
@@ -2906,7 +2936,7 @@ static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize)
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "start")) &&
(n = (int) strtol(attrbuf, NULL, 10)) < 0) {
- BUG_MSG( "illegal '-' character in START attribute; Starting from 0\n");
+ BUG_MSG("Illegal '-' character in START attribute; Starting from 0.");
n = 0;
}
S_TOP(html)->list_number = n;
@@ -2923,7 +2953,7 @@ static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize)
const char *attrbuf;
if (S_TOP(html)->list_type == HTML_LIST_NONE)
- BUG_MSG("<li> outside <ul> or <ol>\n");
+ BUG_MSG("<li> outside <ul> or <ol>.");
html->InFlags |= IN_LI;
@@ -2934,7 +2964,7 @@ static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize)
// ordered
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "value")) &&
(*list_number = strtol(attrbuf, NULL, 10)) < 0) {
- BUG_MSG("illegal negative LIST VALUE attribute; Starting from 0\n");
+ BUG_MSG("Illegal negative list value attribute; Starting from 0.");
*list_number = 0;
}
}
@@ -2961,7 +2991,7 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize)
width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", NULL);
if (width_ptr) {
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<hr> width attribute is obsolete.\n");
+ BUG_MSG("<hr> width attribute is obsolete.");
html->styleEngine->setNonCssHint (CSS_PROPERTY_WIDTH,
CSS_TYPE_LENGTH_PERCENTAGE,
a_Html_parse_length (html, width_ptr));
@@ -2971,7 +3001,7 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize)
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size"))) {
size = strtol(attrbuf, NULL, 10);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<hr> size attribute is obsolete.\n");
+ BUG_MSG("<hr> size attribute is obsolete.");
}
a_Html_tag_set_align_attr(html, tag, tagsize);
@@ -2979,7 +3009,7 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize)
/* TODO: evaluate attribute */
if (a_Html_get_attr(html, tag, tagsize, "noshade")) {
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<hr> noshade attribute is obsolete.\n");
+ BUG_MSG("<hr> noshade attribute is obsolete.");
html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_TOP_STYLE,
CSS_TYPE_ENUM, BORDER_SOLID);
html->styleEngine->setNonCssHint (CSS_PROPERTY_BORDER_BOTTOM_STYLE,
@@ -3045,7 +3075,7 @@ static void Html_tag_open_dt(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_dd(DilloHtml *html, const char *tag, int tagsize)
{
- Html_add_textblock(html, 9);
+ Html_add_textblock(html, true, 9);
}
/*
@@ -3134,7 +3164,7 @@ static void Html_tag_open_meta(DilloHtml *html, const char *tag, int tagsize)
/* only valid inside HEAD */
if (!(html->InFlags & IN_HEAD)) {
- BUG_MSG("META element must be inside the HEAD section\n");
+ BUG_MSG("<meta> must be inside the HEAD section.");
return;
}
@@ -3167,7 +3197,7 @@ static void Html_tag_open_meta(DilloHtml *html, const char *tag, int tagsize)
if (a_Url_cmp(html->base_url, new_url) == 0) {
/* redirection loop, or empty url string: ignore */
- BUG_MSG("META refresh: %s\n",
+ BUG_MSG("<meta> refresh: %s.",
*mr_url ? "redirection loop" : "no target URL");
} else if (delay == 0) {
/* zero-delay redirection */
@@ -3237,27 +3267,26 @@ void a_Html_load_stylesheet(DilloHtml *html, DilloUrl *url)
dReturn_if (url == NULL || ! prefs.load_stylesheets);
_MSG("Html_load_stylesheet: ");
- if (a_Capi_get_buf(url, &data, &len)) {
+ if ((a_Capi_get_flags_with_redirection(url) & CAPI_Completed) &&
+ a_Capi_get_buf(url, &data, &len)) {
_MSG("cached URL=%s len=%d", URL_STR(url), len);
- if (a_Capi_get_flags_with_redirection(url) & CAPI_Completed) {
- if (strncmp("@charset \"", data, 10) == 0) {
- char *endq = strchr(data+10, '"');
-
- if (endq && (endq - data <= 51)) {
- /* IANA limits charset names to 40 characters */
- char *content_type;
-
- *endq = '\0';
- content_type = dStrconcat("text/css; charset=", data+10, NULL);
- *endq = '"';
- a_Capi_unref_buf(url);
- a_Capi_set_content_type(url, content_type, "meta");
- dFree(content_type);
- a_Capi_get_buf(url, &data, &len);
- }
+ if (strncmp("@charset \"", data, 10) == 0) {
+ char *endq = strchr(data+10, '"');
+
+ if (endq && (endq - data <= 51)) {
+ /* IANA limits charset names to 40 characters */
+ char *content_type;
+
+ *endq = '\0';
+ content_type = dStrconcat("text/css; charset=", data+10, NULL);
+ *endq = '"';
+ a_Capi_unref_buf(url);
+ a_Capi_set_content_type(url, content_type, "meta");
+ dFree(content_type);
+ a_Capi_get_buf(url, &data, &len);
}
- html->styleEngine->parse(html, url, data, len, CSS_ORIGIN_AUTHOR);
}
+ html->styleEngine->parse(html, url, data, len, CSS_ORIGIN_AUTHOR);
a_Capi_unref_buf(url);
} else {
/* Fill a Web structure for the cache query */
@@ -3296,7 +3325,7 @@ static void Html_tag_open_link(DilloHtml *html, const char *tag, int tagsize)
/* Ignore LINK outside HEAD */
if (!(html->InFlags & IN_HEAD)) {
- BUG_MSG("LINK element must be inside the HEAD section\n");
+ BUG_MSG("<link> must be inside the HEAD section.");
return;
}
/* Remote stylesheets enabled? */
@@ -3341,12 +3370,12 @@ static void Html_tag_open_base(DilloHtml *html, const char *tag, int tagsize)
a_Url_free(html->base_url);
html->base_url = BaseUrl;
} else {
- BUG_MSG("base URI is relative (it MUST be absolute)\n");
+ BUG_MSG("<base> URI is relative (it MUST be absolute).");
a_Url_free(BaseUrl);
}
}
} else {
- BUG_MSG("the BASE element must appear in the HEAD section\n");
+ BUG_MSG("<base> not inside HEAD section.");
}
}
@@ -3635,10 +3664,10 @@ static int Html_needs_optional_close(int old_idx, int cur_idx)
} else if (old_idx == i_TR) {
/* TR closes TR */
return (cur_idx == i_TR);
- } else if (old_idx == i_DD) {
+ } else if (old_idx == i_DD) {
/* DD is closed by DD and DT */
return (cur_idx == i_DD || cur_idx == i_DT);
- } else if (old_idx == i_OPTION) {
+ } else if (old_idx == i_OPTION) {
return 1; // OPTION always needs close
}
@@ -3684,7 +3713,7 @@ static void Html_stack_cleanup_at_open(DilloHtml *html, int new_idx)
/* we have an inline (or empty) container... */
if (Tags[oldtag_idx].EndTag == 'R') {
- BUG_MSG("<%s> is not allowed to contain <%s>. -- closing <%s>\n",
+ BUG_MSG("<%s> is not allowed to contain <%s>. -- closing <%s>.",
Tags[oldtag_idx].name, Tags[new_idx].name,
Tags[oldtag_idx].name);
}
@@ -3713,7 +3742,7 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
int tag_idx;
if (!(html->InFlags & IN_HTML) && html->DocType == DT_NONE)
- BUG_MSG("the required DOCTYPE declaration is missing.\n");
+ BUG_MSG("The required DOCTYPE declaration is missing.");
if (!(html->InFlags & IN_HTML)) {
tag = "<html>";
@@ -3763,6 +3792,7 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
static void Html_parse_common_attrs(DilloHtml *html, char *tag, int tagsize)
{
const char *attrbuf;
+ char lang[3];
if (tagsize >= 8 && /* length of "<t id=i>" */
(attrbuf = a_Html_get_attr(html, tag, tagsize, "id"))) {
@@ -3788,24 +3818,25 @@ static void Html_parse_common_attrs(DilloHtml *html, char *tag, int tagsize)
html->styleEngine->setStyle (attrbuf);
}
- /* handle "xml:lang" and "lang" attributes */
- int hasXmlLang = 0;
+ /* handle "xml:lang" and "lang" attributes
+ * We use only the first two chars of the value to deal with
+ * extended language tags (see http://www.rfc-editor.org/rfc/bcp/bcp47.txt)
+ */
+ memset(lang, 0, sizeof(lang));
if (tagsize >= 14) {
/* length of "<t xml:lang=i>" */
attrbuf = a_Html_get_attr(html, tag, tagsize, "xml:lang");
- if (attrbuf) {
- html->styleEngine->setNonCssHint(PROPERTY_X_LANG, CSS_TYPE_STRING,
- attrbuf);
- hasXmlLang = 1;
- }
+ if (attrbuf)
+ strncpy(lang, attrbuf, 2);
}
- if (!hasXmlLang && tagsize >= 10) { /* 'xml:lang' prevails over 'lang' */
+ if (!lang[0] && tagsize >= 10) { /* 'xml:lang' prevails over 'lang' */
/* length of "<t lang=i>" */
attrbuf = a_Html_get_attr(html, tag, tagsize, "lang");
if (attrbuf)
- html->styleEngine->setNonCssHint(PROPERTY_X_LANG, CSS_TYPE_STRING,
- attrbuf);
+ strncpy(lang, attrbuf, 2);
}
+ if (lang[0])
+ html->styleEngine->setNonCssHint(PROPERTY_X_LANG, CSS_TYPE_STRING, lang);
}
/*
@@ -3829,7 +3860,7 @@ static void Html_check_html5_obsolete(DilloHtml *html, int ni)
}
for (int i = 0; i < 9; i++) {
if (indexes[i] == ni) {
- BUG_MSG("<%s> is obsolete in HTML5.\n", Tags[ni].name);
+ BUG_MSG("<%s> is obsolete in HTML5.", Tags[ni].name);
break;
}
}
@@ -3837,8 +3868,12 @@ static void Html_check_html5_obsolete(DilloHtml *html, int ni)
static void Html_display_block(DilloHtml *html)
{
- //HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
- Html_add_textblock(html, 0);
+ Html_add_textblock(html, !Html_will_textblock_be_out_of_flow (html), 0);
+}
+
+static void Html_display_inline_block(DilloHtml *html)
+{
+ Html_add_textblock(html, false, 0);
}
static void Html_display_listitem(DilloHtml *html)
@@ -3919,7 +3954,7 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
/* TODO: this is only raising a warning, take some defined action.
* Note: apache uses IMG inside PRE (we could use its "alt"). */
if ((html->InFlags & IN_PRE) && Html_tag_pre_excludes(ni))
- BUG_MSG("<pre> is not allowed to contain <%s>\n", Tags[ni].name);
+ BUG_MSG("<pre> is not allowed to contain <%s>.", Tags[ni].name);
/* Make sure these elements don't nest each other */
if (html->InFlags & (IN_BUTTON | IN_SELECT | IN_TEXTAREA))
@@ -3943,6 +3978,9 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
case DISPLAY_BLOCK:
Html_display_block(html);
break;
+ case DISPLAY_INLINE_BLOCK:
+ Html_display_inline_block(html);
+ break;
case DISPLAY_LIST_ITEM:
Html_display_listitem(html);
break;
@@ -3950,7 +3988,6 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
S_TOP(html)->display_none = true;
break;
case DISPLAY_INLINE:
- case DISPLAY_INLINE_BLOCK: // TODO: implement inline-block
default:
break;
}
@@ -4018,7 +4055,7 @@ static const char *Html_get_attr2(DilloHtml *html,
const char *attrname,
int tag_parsing_flags)
{
- int i, isocode, entsize, Found = 0, delimiter = 0, attr_pos = 0;
+ int i, entsize, Found = 0, delimiter = 0, attr_pos = 0;
Dstr *Buf = html->attr_data;
DilloHtmlTagParsingState state = SEEK_ATTR_START;
@@ -4077,16 +4114,12 @@ static const char *Html_get_attr2(DilloHtml *html,
state = FINISHED;
} else if (tag[i] == '&' &&
(tag_parsing_flags & HTML_ParseEntities)) {
- if ((isocode = Html_parse_entity(html, tag+i,
- tagsize-i, &entsize)) >= 0) {
- if (isocode >= 128) {
- char buf[4];
- int k, n = a_Utf8_encode(isocode, buf);
- for (k = 0; k < n; ++k)
- dStr_append_c(Buf, buf[k]);
- } else {
- dStr_append_c(Buf, (char) isocode);
- }
+ const char *entstr;
+ const bool_t is_attr = TRUE;
+
+ if ((entstr = Html_parse_entity(html, tag+i, tagsize-i, &entsize,
+ is_attr))) {
+ dStr_append(Buf, entstr);
i += entsize-1;
} else {
dStr_append_c(Buf, tag[i]);
@@ -4228,7 +4261,7 @@ static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof)
buf_index = bufsize;
} else {
/* Tag: search end of tag (skipping over quoted strings) */
- html->CurrTagOfs = html->Start_Ofs + token_start;
+ html->CurrOfs = html->Start_Ofs + token_start;
while ( buf_index < bufsize ) {
buf_index++;
@@ -4249,7 +4282,7 @@ static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof)
if (buf[offset] == ch || !buf[offset]) {
buf_index = offset;
} else {
- BUG_MSG("attribute lacks closing quote\n");
+ BUG_MSG("Attribute lacks closing quote.");
break;
}
}
@@ -4257,7 +4290,7 @@ static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof)
/* unterminated tag detected */
p = dStrndup(buf+token_start+1,
strcspn(buf+token_start+1, " <\n\r\t"));
- BUG_MSG("<%s> element lacks its closing '>'\n", p);
+ BUG_MSG("<%s> lacks its closing '>'.", p);
dFree(p);
--buf_index;
break;
@@ -4272,6 +4305,8 @@ static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof)
}
} else {
/* A Word: search for whitespace or tag open */
+ html->CurrOfs = html->Start_Ofs + token_start;
+
while (++buf_index < bufsize) {
buf_index += strcspn(buf + buf_index, " <\n\r\t\f\v");
if (buf[buf_index] == '<' && (ch = buf[buf_index + 1]) &&
diff --git a/src/html_charrefs.h b/src/html_charrefs.h
new file mode 100644
index 00000000..38f3849f
--- /dev/null
+++ b/src/html_charrefs.h
@@ -0,0 +1,2138 @@
+#ifndef HTML_CHARREFS_H
+#define HTML_CHARREFS_H
+
+typedef struct {
+ const char *ref;
+ const char *html5_str;
+ const char *html4_str;
+} Charref_t;
+
+#define NumRef 2125
+static const Charref_t Charrefs[NumRef] = {
+{"AElig", "Æ", "Æ"},
+{"AMP", "&", NULL},
+{"Aacute", "Á", "Á"},
+{"Abreve", "Ă", NULL},
+{"Acirc", "Â", "Â"},
+{"Acy", "А", NULL},
+{"Afr", "𝔄", NULL},
+{"Agrave", "À", "À"},
+{"Alpha", "Α", "Α"},
+{"Amacr", "Ā", NULL},
+{"And", "⩓", NULL},
+{"Aogon", "Ą", NULL},
+{"Aopf", "𝔸", NULL},
+{"ApplyFunction", "⁡", NULL},
+{"Aring", "Å", "Å"},
+{"Ascr", "𝒜", NULL},
+{"Assign", "≔", NULL},
+{"Atilde", "Ã", "Ã"},
+{"Auml", "Ä", "Ä"},
+{"Backslash", "∖", NULL},
+{"Barv", "⫧", NULL},
+{"Barwed", "⌆", NULL},
+{"Bcy", "Б", NULL},
+{"Because", "∵", NULL},
+{"Bernoullis", "ℬ", NULL},
+{"Beta", "Β", "Β"},
+{"Bfr", "𝔅", NULL},
+{"Bopf", "𝔹", NULL},
+{"Breve", "˘", NULL},
+{"Bscr", "ℬ", NULL},
+{"Bumpeq", "≎", NULL},
+{"CHcy", "Ч", NULL},
+{"COPY", "©", NULL},
+{"Cacute", "Ć", NULL},
+{"Cap", "⋒", NULL},
+{"CapitalDifferentialD", "ⅅ", NULL},
+{"Cayleys", "ℭ", NULL},
+{"Ccaron", "Č", NULL},
+{"Ccedil", "Ç", "Ç"},
+{"Ccirc", "Ĉ", NULL},
+{"Cconint", "∰", NULL},
+{"Cdot", "Ċ", NULL},
+{"Cedilla", "¸", NULL},
+{"CenterDot", "·", NULL},
+{"Cfr", "ℭ", NULL},
+{"Chi", "Χ", "Χ"},
+{"CircleDot", "⊙", NULL},
+{"CircleMinus", "⊖", NULL},
+{"CirclePlus", "⊕", NULL},
+{"CircleTimes", "⊗", NULL},
+{"ClockwiseContourIntegral", "∲", NULL},
+{"CloseCurlyDoubleQuote", "”", NULL},
+{"CloseCurlyQuote", "’", NULL},
+{"Colon", "∷", NULL},
+{"Colone", "⩴", NULL},
+{"Congruent", "≡", NULL},
+{"Conint", "∯", NULL},
+{"ContourIntegral", "∮", NULL},
+{"Copf", "ℂ", NULL},
+{"Coproduct", "∐", NULL},
+{"CounterClockwiseContourIntegral", "∳", NULL},
+{"Cross", "⨯", NULL},
+{"Cscr", "𝒞", NULL},
+{"Cup", "⋓", NULL},
+{"CupCap", "≍", NULL},
+{"DD", "ⅅ", NULL},
+{"DDotrahd", "⤑", NULL},
+{"DJcy", "Ђ", NULL},
+{"DScy", "Ѕ", NULL},
+{"DZcy", "Џ", NULL},
+{"Dagger", "‡", "‡"},
+{"Darr", "↡", NULL},
+{"Dashv", "⫤", NULL},
+{"Dcaron", "Ď", NULL},
+{"Dcy", "Д", NULL},
+{"Del", "∇", NULL},
+{"Delta", "Δ", "Δ"},
+{"Dfr", "𝔇", NULL},
+{"DiacriticalAcute", "´", NULL},
+{"DiacriticalDot", "˙", NULL},
+{"DiacriticalDoubleAcute", "˝", NULL},
+{"DiacriticalGrave", "`", NULL},
+{"DiacriticalTilde", "˜", NULL},
+{"Diamond", "⋄", NULL},
+{"DifferentialD", "ⅆ", NULL},
+{"Dopf", "𝔻", NULL},
+{"Dot", "¨", NULL},
+{"DotDot", "⃜", NULL},
+{"DotEqual", "≐", NULL},
+{"DoubleContourIntegral", "∯", NULL},
+{"DoubleDot", "¨", NULL},
+{"DoubleDownArrow", "⇓", NULL},
+{"DoubleLeftArrow", "⇐", NULL},
+{"DoubleLeftRightArrow", "⇔", NULL},
+{"DoubleLeftTee", "⫤", NULL},
+{"DoubleLongLeftArrow", "⟸", NULL},
+{"DoubleLongLeftRightArrow", "⟺", NULL},
+{"DoubleLongRightArrow", "⟹", NULL},
+{"DoubleRightArrow", "⇒", NULL},
+{"DoubleRightTee", "⊨", NULL},
+{"DoubleUpArrow", "⇑", NULL},
+{"DoubleUpDownArrow", "⇕", NULL},
+{"DoubleVerticalBar", "∥", NULL},
+{"DownArrow", "↓", NULL},
+{"DownArrowBar", "⤓", NULL},
+{"DownArrowUpArrow", "⇵", NULL},
+{"DownBreve", "̑", NULL},
+{"DownLeftRightVector", "⥐", NULL},
+{"DownLeftTeeVector", "⥞", NULL},
+{"DownLeftVector", "↽", NULL},
+{"DownLeftVectorBar", "⥖", NULL},
+{"DownRightTeeVector", "⥟", NULL},
+{"DownRightVector", "⇁", NULL},
+{"DownRightVectorBar", "⥗", NULL},
+{"DownTee", "⊤", NULL},
+{"DownTeeArrow", "↧", NULL},
+{"Downarrow", "⇓", NULL},
+{"Dscr", "𝒟", NULL},
+{"Dstrok", "Đ", NULL},
+{"ENG", "Ŋ", NULL},
+{"ETH", "Ð", "Ð"},
+{"Eacute", "É", "É"},
+{"Ecaron", "Ě", NULL},
+{"Ecirc", "Ê", "Ê"},
+{"Ecy", "Э", NULL},
+{"Edot", "Ė", NULL},
+{"Efr", "𝔈", NULL},
+{"Egrave", "È", "È"},
+{"Element", "∈", NULL},
+{"Emacr", "Ē", NULL},
+{"EmptySmallSquare", "◻", NULL},
+{"EmptyVerySmallSquare", "▫", NULL},
+{"Eogon", "Ę", NULL},
+{"Eopf", "𝔼", NULL},
+{"Epsilon", "Ε", "Ε"},
+{"Equal", "⩵", NULL},
+{"EqualTilde", "≂", NULL},
+{"Equilibrium", "⇌", NULL},
+{"Escr", "ℰ", NULL},
+{"Esim", "⩳", NULL},
+{"Eta", "Η", "Η"},
+{"Euml", "Ë", "Ë"},
+{"Exists", "∃", NULL},
+{"ExponentialE", "ⅇ", NULL},
+{"Fcy", "Ф", NULL},
+{"Ffr", "𝔉", NULL},
+{"FilledSmallSquare", "◼", NULL},
+{"FilledVerySmallSquare", "▪", NULL},
+{"Fopf", "𝔽", NULL},
+{"ForAll", "∀", NULL},
+{"Fouriertrf", "ℱ", NULL},
+{"Fscr", "ℱ", NULL},
+{"GJcy", "Ѓ", NULL},
+{"GT", ">", NULL},
+{"Gamma", "Γ", "Γ"},
+{"Gammad", "Ϝ", NULL},
+{"Gbreve", "Ğ", NULL},
+{"Gcedil", "Ģ", NULL},
+{"Gcirc", "Ĝ", NULL},
+{"Gcy", "Г", NULL},
+{"Gdot", "Ġ", NULL},
+{"Gfr", "𝔊", NULL},
+{"Gg", "⋙", NULL},
+{"Gopf", "𝔾", NULL},
+{"GreaterEqual", "≥", NULL},
+{"GreaterEqualLess", "⋛", NULL},
+{"GreaterFullEqual", "≧", NULL},
+{"GreaterGreater", "⪢", NULL},
+{"GreaterLess", "≷", NULL},
+{"GreaterSlantEqual", "⩾", NULL},
+{"GreaterTilde", "≳", NULL},
+{"Gscr", "𝒢", NULL},
+{"Gt", "≫", NULL},
+{"HARDcy", "Ъ", NULL},
+{"Hacek", "ˇ", NULL},
+{"Hat", "^", NULL},
+{"Hcirc", "Ĥ", NULL},
+{"Hfr", "ℌ", NULL},
+{"HilbertSpace", "ℋ", NULL},
+{"Hopf", "ℍ", NULL},
+{"HorizontalLine", "─", NULL},
+{"Hscr", "ℋ", NULL},
+{"Hstrok", "Ħ", NULL},
+{"HumpDownHump", "≎", NULL},
+{"HumpEqual", "≏", NULL},
+{"IEcy", "Е", NULL},
+{"IJlig", "IJ", NULL},
+{"IOcy", "Ё", NULL},
+{"Iacute", "Í", "Í"},
+{"Icirc", "Î", "Î"},
+{"Icy", "И", NULL},
+{"Idot", "İ", NULL},
+{"Ifr", "ℑ", NULL},
+{"Igrave", "Ì", "Ì"},
+{"Im", "ℑ", NULL},
+{"Imacr", "Ī", NULL},
+{"ImaginaryI", "ⅈ", NULL},
+{"Implies", "⇒", NULL},
+{"Int", "∬", NULL},
+{"Integral", "∫", NULL},
+{"Intersection", "⋂", NULL},
+{"InvisibleComma", "⁣", NULL},
+{"InvisibleTimes", "⁢", NULL},
+{"Iogon", "Į", NULL},
+{"Iopf", "𝕀", NULL},
+{"Iota", "Ι", "Ι"},
+{"Iscr", "ℐ", NULL},
+{"Itilde", "Ĩ", NULL},
+{"Iukcy", "І", NULL},
+{"Iuml", "Ï", "Ï"},
+{"Jcirc", "Ĵ", NULL},
+{"Jcy", "Й", NULL},
+{"Jfr", "𝔍", NULL},
+{"Jopf", "𝕁", NULL},
+{"Jscr", "𝒥", NULL},
+{"Jsercy", "Ј", NULL},
+{"Jukcy", "Є", NULL},
+{"KHcy", "Х", NULL},
+{"KJcy", "Ќ", NULL},
+{"Kappa", "Κ", "Κ"},
+{"Kcedil", "Ķ", NULL},
+{"Kcy", "К", NULL},
+{"Kfr", "𝔎", NULL},
+{"Kopf", "𝕂", NULL},
+{"Kscr", "𝒦", NULL},
+{"LJcy", "Љ", NULL},
+{"LT", "<", NULL},
+{"Lacute", "Ĺ", NULL},
+{"Lambda", "Λ", "Λ"},
+{"Lang", "⟪", NULL},
+{"Laplacetrf", "ℒ", NULL},
+{"Larr", "↞", NULL},
+{"Lcaron", "Ľ", NULL},
+{"Lcedil", "Ļ", NULL},
+{"Lcy", "Л", NULL},
+{"LeftAngleBracket", "⟨", NULL},
+{"LeftArrow", "←", NULL},
+{"LeftArrowBar", "⇤", NULL},
+{"LeftArrowRightArrow", "⇆", NULL},
+{"LeftCeiling", "⌈", NULL},
+{"LeftDoubleBracket", "⟦", NULL},
+{"LeftDownTeeVector", "⥡", NULL},
+{"LeftDownVector", "⇃", NULL},
+{"LeftDownVectorBar", "⥙", NULL},
+{"LeftFloor", "⌊", NULL},
+{"LeftRightArrow", "↔", NULL},
+{"LeftRightVector", "⥎", NULL},
+{"LeftTee", "⊣", NULL},
+{"LeftTeeArrow", "↤", NULL},
+{"LeftTeeVector", "⥚", NULL},
+{"LeftTriangle", "⊲", NULL},
+{"LeftTriangleBar", "⧏", NULL},
+{"LeftTriangleEqual", "⊴", NULL},
+{"LeftUpDownVector", "⥑", NULL},
+{"LeftUpTeeVector", "⥠", NULL},
+{"LeftUpVector", "↿", NULL},
+{"LeftUpVectorBar", "⥘", NULL},
+{"LeftVector", "↼", NULL},
+{"LeftVectorBar", "⥒", NULL},
+{"Leftarrow", "⇐", NULL},
+{"Leftrightarrow", "⇔", NULL},
+{"LessEqualGreater", "⋚", NULL},
+{"LessFullEqual", "≦", NULL},
+{"LessGreater", "≶", NULL},
+{"LessLess", "⪡", NULL},
+{"LessSlantEqual", "⩽", NULL},
+{"LessTilde", "≲", NULL},
+{"Lfr", "𝔏", NULL},
+{"Ll", "⋘", NULL},
+{"Lleftarrow", "⇚", NULL},
+{"Lmidot", "Ŀ", NULL},
+{"LongLeftArrow", "⟵", NULL},
+{"LongLeftRightArrow", "⟷", NULL},
+{"LongRightArrow", "⟶", NULL},
+{"Longleftarrow", "⟸", NULL},
+{"Longleftrightarrow", "⟺", NULL},
+{"Longrightarrow", "⟹", NULL},
+{"Lopf", "𝕃", NULL},
+{"LowerLeftArrow", "↙", NULL},
+{"LowerRightArrow", "↘", NULL},
+{"Lscr", "ℒ", NULL},
+{"Lsh", "↰", NULL},
+{"Lstrok", "Ł", NULL},
+{"Lt", "≪", NULL},
+{"Map", "⤅", NULL},
+{"Mcy", "М", NULL},
+{"MediumSpace", " ", NULL},
+{"Mellintrf", "ℳ", NULL},
+{"Mfr", "𝔐", NULL},
+{"MinusPlus", "∓", NULL},
+{"Mopf", "𝕄", NULL},
+{"Mscr", "ℳ", NULL},
+{"Mu", "Μ", "Μ"},
+{"NJcy", "Њ", NULL},
+{"Nacute", "Ń", NULL},
+{"Ncaron", "Ň", NULL},
+{"Ncedil", "Ņ", NULL},
+{"Ncy", "Н", NULL},
+{"NegativeMediumSpace", "​", NULL},
+{"NegativeThickSpace", "​", NULL},
+{"NegativeThinSpace", "​", NULL},
+{"NegativeVeryThinSpace", "​", NULL},
+{"NestedGreaterGreater", "≫", NULL},
+{"NestedLessLess", "≪", NULL},
+{"NewLine", "\n", NULL},
+{"Nfr", "𝔑", NULL},
+{"NoBreak", "⁠", NULL},
+{"NonBreakingSpace", " ", NULL},
+{"Nopf", "ℕ", NULL},
+{"Not", "⫬", NULL},
+{"NotCongruent", "≢", NULL},
+{"NotCupCap", "≭", NULL},
+{"NotDoubleVerticalBar", "∦", NULL},
+{"NotElement", "∉", NULL},
+{"NotEqual", "≠", NULL},
+{"NotEqualTilde", "≂̸", NULL},
+{"NotExists", "∄", NULL},
+{"NotGreater", "≯", NULL},
+{"NotGreaterEqual", "≱", NULL},
+{"NotGreaterFullEqual", "≧̸", NULL},
+{"NotGreaterGreater", "≫̸", NULL},
+{"NotGreaterLess", "≹", NULL},
+{"NotGreaterSlantEqual", "⩾̸", NULL},
+{"NotGreaterTilde", "≵", NULL},
+{"NotHumpDownHump", "≎̸", NULL},
+{"NotHumpEqual", "≏̸", NULL},
+{"NotLeftTriangle", "⋪", NULL},
+{"NotLeftTriangleBar", "⧏̸", NULL},
+{"NotLeftTriangleEqual", "⋬", NULL},
+{"NotLess", "≮", NULL},
+{"NotLessEqual", "≰", NULL},
+{"NotLessGreater", "≸", NULL},
+{"NotLessLess", "≪̸", NULL},
+{"NotLessSlantEqual", "⩽̸", NULL},
+{"NotLessTilde", "≴", NULL},
+{"NotNestedGreaterGreater", "⪢̸", NULL},
+{"NotNestedLessLess", "⪡̸", NULL},
+{"NotPrecedes", "⊀", NULL},
+{"NotPrecedesEqual", "⪯̸", NULL},
+{"NotPrecedesSlantEqual", "⋠", NULL},
+{"NotReverseElement", "∌", NULL},
+{"NotRightTriangle", "⋫", NULL},
+{"NotRightTriangleBar", "⧐̸", NULL},
+{"NotRightTriangleEqual", "⋭", NULL},
+{"NotSquareSubset", "⊏̸", NULL},
+{"NotSquareSubsetEqual", "⋢", NULL},
+{"NotSquareSuperset", "⊐̸", NULL},
+{"NotSquareSupersetEqual", "⋣", NULL},
+{"NotSubset", "⊂⃒", NULL},
+{"NotSubsetEqual", "⊈", NULL},
+{"NotSucceeds", "⊁", NULL},
+{"NotSucceedsEqual", "⪰̸", NULL},
+{"NotSucceedsSlantEqual", "⋡", NULL},
+{"NotSucceedsTilde", "≿̸", NULL},
+{"NotSuperset", "⊃⃒", NULL},
+{"NotSupersetEqual", "⊉", NULL},
+{"NotTilde", "≁", NULL},
+{"NotTildeEqual", "≄", NULL},
+{"NotTildeFullEqual", "≇", NULL},
+{"NotTildeTilde", "≉", NULL},
+{"NotVerticalBar", "∤", NULL},
+{"Nscr", "𝒩", NULL},
+{"Ntilde", "Ñ", "Ñ"},
+{"Nu", "Ν", "Ν"},
+{"OElig", "Œ", "Œ"},
+{"Oacute", "Ó", "Ó"},
+{"Ocirc", "Ô", "Ô"},
+{"Ocy", "О", NULL},
+{"Odblac", "Ő", NULL},
+{"Ofr", "𝔒", NULL},
+{"Ograve", "Ò", "Ò"},
+{"Omacr", "Ō", NULL},
+{"Omega", "Ω", "Ω"},
+{"Omicron", "Ο", "Ο"},
+{"Oopf", "𝕆", NULL},
+{"OpenCurlyDoubleQuote", "“", NULL},
+{"OpenCurlyQuote", "‘", NULL},
+{"Or", "⩔", NULL},
+{"Oscr", "𝒪", NULL},
+{"Oslash", "Ø", "Ø"},
+{"Otilde", "Õ", "Õ"},
+{"Otimes", "⨷", NULL},
+{"Ouml", "Ö", "Ö"},
+{"OverBar", "‾", NULL},
+{"OverBrace", "⏞", NULL},
+{"OverBracket", "⎴", NULL},
+{"OverParenthesis", "⏜", NULL},
+{"PartialD", "∂", NULL},
+{"Pcy", "П", NULL},
+{"Pfr", "𝔓", NULL},
+{"Phi", "Φ", "Φ"},
+{"Pi", "Π", "Π"},
+{"PlusMinus", "±", NULL},
+{"Poincareplane", "ℌ", NULL},
+{"Popf", "ℙ", NULL},
+{"Pr", "⪻", NULL},
+{"Precedes", "≺", NULL},
+{"PrecedesEqual", "⪯", NULL},
+{"PrecedesSlantEqual", "≼", NULL},
+{"PrecedesTilde", "≾", NULL},
+{"Prime", "″", "″"},
+{"Product", "∏", NULL},
+{"Proportion", "∷", NULL},
+{"Proportional", "∝", NULL},
+{"Pscr", "𝒫", NULL},
+{"Psi", "Ψ", "Ψ"},
+{"QUOT", "\"", NULL},
+{"Qfr", "𝔔", NULL},
+{"Qopf", "ℚ", NULL},
+{"Qscr", "𝒬", NULL},
+{"RBarr", "⤐", NULL},
+{"REG", "®", NULL},
+{"Racute", "Ŕ", NULL},
+{"Rang", "⟫", NULL},
+{"Rarr", "↠", NULL},
+{"Rarrtl", "⤖", NULL},
+{"Rcaron", "Ř", NULL},
+{"Rcedil", "Ŗ", NULL},
+{"Rcy", "Р", NULL},
+{"Re", "ℜ", NULL},
+{"ReverseElement", "∋", NULL},
+{"ReverseEquilibrium", "⇋", NULL},
+{"ReverseUpEquilibrium", "⥯", NULL},
+{"Rfr", "ℜ", NULL},
+{"Rho", "Ρ", "Ρ"},
+{"RightAngleBracket", "⟩", NULL},
+{"RightArrow", "→", NULL},
+{"RightArrowBar", "⇥", NULL},
+{"RightArrowLeftArrow", "⇄", NULL},
+{"RightCeiling", "⌉", NULL},
+{"RightDoubleBracket", "⟧", NULL},
+{"RightDownTeeVector", "⥝", NULL},
+{"RightDownVector", "⇂", NULL},
+{"RightDownVectorBar", "⥕", NULL},
+{"RightFloor", "⌋", NULL},
+{"RightTee", "⊢", NULL},
+{"RightTeeArrow", "↦", NULL},
+{"RightTeeVector", "⥛", NULL},
+{"RightTriangle", "⊳", NULL},
+{"RightTriangleBar", "⧐", NULL},
+{"RightTriangleEqual", "⊵", NULL},
+{"RightUpDownVector", "⥏", NULL},
+{"RightUpTeeVector", "⥜", NULL},
+{"RightUpVector", "↾", NULL},
+{"RightUpVectorBar", "⥔", NULL},
+{"RightVector", "⇀", NULL},
+{"RightVectorBar", "⥓", NULL},
+{"Rightarrow", "⇒", NULL},
+{"Ropf", "ℝ", NULL},
+{"RoundImplies", "⥰", NULL},
+{"Rrightarrow", "⇛", NULL},
+{"Rscr", "ℛ", NULL},
+{"Rsh", "↱", NULL},
+{"RuleDelayed", "⧴", NULL},
+{"SHCHcy", "Щ", NULL},
+{"SHcy", "Ш", NULL},
+{"SOFTcy", "Ь", NULL},
+{"Sacute", "Ś", NULL},
+{"Sc", "⪼", NULL},
+{"Scaron", "Š", "Š"},
+{"Scedil", "Ş", NULL},
+{"Scirc", "Ŝ", NULL},
+{"Scy", "С", NULL},
+{"Sfr", "𝔖", NULL},
+{"ShortDownArrow", "↓", NULL},
+{"ShortLeftArrow", "←", NULL},
+{"ShortRightArrow", "→", NULL},
+{"ShortUpArrow", "↑", NULL},
+{"Sigma", "Σ", "Σ"},
+{"SmallCircle", "∘", NULL},
+{"Sopf", "𝕊", NULL},
+{"Sqrt", "√", NULL},
+{"Square", "□", NULL},
+{"SquareIntersection", "⊓", NULL},
+{"SquareSubset", "⊏", NULL},
+{"SquareSubsetEqual", "⊑", NULL},
+{"SquareSuperset", "⊐", NULL},
+{"SquareSupersetEqual", "⊒", NULL},
+{"SquareUnion", "⊔", NULL},
+{"Sscr", "𝒮", NULL},
+{"Star", "⋆", NULL},
+{"Sub", "⋐", NULL},
+{"Subset", "⋐", NULL},
+{"SubsetEqual", "⊆", NULL},
+{"Succeeds", "≻", NULL},
+{"SucceedsEqual", "⪰", NULL},
+{"SucceedsSlantEqual", "≽", NULL},
+{"SucceedsTilde", "≿", NULL},
+{"SuchThat", "∋", NULL},
+{"Sum", "∑", NULL},
+{"Sup", "⋑", NULL},
+{"Superset", "⊃", NULL},
+{"SupersetEqual", "⊇", NULL},
+{"Supset", "⋑", NULL},
+{"THORN", "Þ", "Þ"},
+{"TRADE", "™", NULL},
+{"TSHcy", "Ћ", NULL},
+{"TScy", "Ц", NULL},
+{"Tab", "\t", NULL},
+{"Tau", "Τ", "Τ"},
+{"Tcaron", "Ť", NULL},
+{"Tcedil", "Ţ", NULL},
+{"Tcy", "Т", NULL},
+{"Tfr", "𝔗", NULL},
+{"Therefore", "∴", NULL},
+{"Theta", "Θ", "Θ"},
+{"ThickSpace", "  ", NULL},
+{"ThinSpace", " ", NULL},
+{"Tilde", "∼", NULL},
+{"TildeEqual", "≃", NULL},
+{"TildeFullEqual", "≅", NULL},
+{"TildeTilde", "≈", NULL},
+{"Topf", "𝕋", NULL},
+{"TripleDot", "⃛", NULL},
+{"Tscr", "𝒯", NULL},
+{"Tstrok", "Ŧ", NULL},
+{"Uacute", "Ú", "Ú"},
+{"Uarr", "↟", NULL},
+{"Uarrocir", "⥉", NULL},
+{"Ubrcy", "Ў", NULL},
+{"Ubreve", "Ŭ", NULL},
+{"Ucirc", "Û", "Û"},
+{"Ucy", "У", NULL},
+{"Udblac", "Ű", NULL},
+{"Ufr", "𝔘", NULL},
+{"Ugrave", "Ù", "Ù"},
+{"Umacr", "Ū", NULL},
+{"UnderBar", "_", NULL},
+{"UnderBrace", "⏟", NULL},
+{"UnderBracket", "⎵", NULL},
+{"UnderParenthesis", "⏝", NULL},
+{"Union", "⋃", NULL},
+{"UnionPlus", "⊎", NULL},
+{"Uogon", "Ų", NULL},
+{"Uopf", "𝕌", NULL},
+{"UpArrow", "↑", NULL},
+{"UpArrowBar", "⤒", NULL},
+{"UpArrowDownArrow", "⇅", NULL},
+{"UpDownArrow", "↕", NULL},
+{"UpEquilibrium", "⥮", NULL},
+{"UpTee", "⊥", NULL},
+{"UpTeeArrow", "↥", NULL},
+{"Uparrow", "⇑", NULL},
+{"Updownarrow", "⇕", NULL},
+{"UpperLeftArrow", "↖", NULL},
+{"UpperRightArrow", "↗", NULL},
+{"Upsi", "ϒ", NULL},
+{"Upsilon", "Υ", "Υ"},
+{"Uring", "Ů", NULL},
+{"Uscr", "𝒰", NULL},
+{"Utilde", "Ũ", NULL},
+{"Uuml", "Ü", "Ü"},
+{"VDash", "⊫", NULL},
+{"Vbar", "⫫", NULL},
+{"Vcy", "В", NULL},
+{"Vdash", "⊩", NULL},
+{"Vdashl", "⫦", NULL},
+{"Vee", "⋁", NULL},
+{"Verbar", "‖", NULL},
+{"Vert", "‖", NULL},
+{"VerticalBar", "∣", NULL},
+{"VerticalLine", "|", NULL},
+{"VerticalSeparator", "❘", NULL},
+{"VerticalTilde", "≀", NULL},
+{"VeryThinSpace", " ", NULL},
+{"Vfr", "𝔙", NULL},
+{"Vopf", "𝕍", NULL},
+{"Vscr", "𝒱", NULL},
+{"Vvdash", "⊪", NULL},
+{"Wcirc", "Ŵ", NULL},
+{"Wedge", "⋀", NULL},
+{"Wfr", "𝔚", NULL},
+{"Wopf", "𝕎", NULL},
+{"Wscr", "𝒲", NULL},
+{"Xfr", "𝔛", NULL},
+{"Xi", "Ξ", "Ξ"},
+{"Xopf", "𝕏", NULL},
+{"Xscr", "𝒳", NULL},
+{"YAcy", "Я", NULL},
+{"YIcy", "Ї", NULL},
+{"YUcy", "Ю", NULL},
+{"Yacute", "Ý", "Ý"},
+{"Ycirc", "Ŷ", NULL},
+{"Ycy", "Ы", NULL},
+{"Yfr", "𝔜", NULL},
+{"Yopf", "𝕐", NULL},
+{"Yscr", "𝒴", NULL},
+{"Yuml", "Ÿ", "Ÿ"},
+{"ZHcy", "Ж", NULL},
+{"Zacute", "Ź", NULL},
+{"Zcaron", "Ž", NULL},
+{"Zcy", "З", NULL},
+{"Zdot", "Ż", NULL},
+{"ZeroWidthSpace", "​", NULL},
+{"Zeta", "Ζ", "Ζ"},
+{"Zfr", "ℨ", NULL},
+{"Zopf", "ℤ", NULL},
+{"Zscr", "𝒵", NULL},
+{"aacute", "á", "á"},
+{"abreve", "ă", NULL},
+{"ac", "∾", NULL},
+{"acE", "∾̳", NULL},
+{"acd", "∿", NULL},
+{"acirc", "â", "â"},
+{"acute", "´", "´"},
+{"acy", "а", NULL},
+{"aelig", "æ", "æ"},
+{"af", "⁡", NULL},
+{"afr", "𝔞", NULL},
+{"agrave", "à", "à"},
+{"alefsym", "ℵ", "ℵ"},
+{"aleph", "ℵ", NULL},
+{"alpha", "α", "α"},
+{"amacr", "ā", NULL},
+{"amalg", "⨿", NULL},
+{"amp", "&", "&"},
+{"and", "∧", "∧"},
+{"andand", "⩕", NULL},
+{"andd", "⩜", NULL},
+{"andslope", "⩘", NULL},
+{"andv", "⩚", NULL},
+{"ang", "∠", "∠"},
+{"ange", "⦤", NULL},
+{"angle", "∠", NULL},
+{"angmsd", "∡", NULL},
+{"angmsdaa", "⦨", NULL},
+{"angmsdab", "⦩", NULL},
+{"angmsdac", "⦪", NULL},
+{"angmsdad", "⦫", NULL},
+{"angmsdae", "⦬", NULL},
+{"angmsdaf", "⦭", NULL},
+{"angmsdag", "⦮", NULL},
+{"angmsdah", "⦯", NULL},
+{"angrt", "∟", NULL},
+{"angrtvb", "⊾", NULL},
+{"angrtvbd", "⦝", NULL},
+{"angsph", "∢", NULL},
+{"angst", "Å", NULL},
+{"angzarr", "⍼", NULL},
+{"aogon", "ą", NULL},
+{"aopf", "𝕒", NULL},
+{"ap", "≈", NULL},
+{"apE", "⩰", NULL},
+{"apacir", "⩯", NULL},
+{"ape", "≊", NULL},
+{"apid", "≋", NULL},
+{"apos", "'", NULL},
+{"approx", "≈", NULL},
+{"approxeq", "≊", NULL},
+{"aring", "å", "å"},
+{"ascr", "𝒶", NULL},
+{"ast", "*", NULL},
+{"asymp", "≈", "≈"},
+{"asympeq", "≍", NULL},
+{"atilde", "ã", "ã"},
+{"auml", "ä", "ä"},
+{"awconint", "∳", NULL},
+{"awint", "⨑", NULL},
+{"bNot", "⫭", NULL},
+{"backcong", "≌", NULL},
+{"backepsilon", "϶", NULL},
+{"backprime", "‵", NULL},
+{"backsim", "∽", NULL},
+{"backsimeq", "⋍", NULL},
+{"barvee", "⊽", NULL},
+{"barwed", "⌅", NULL},
+{"barwedge", "⌅", NULL},
+{"bbrk", "⎵", NULL},
+{"bbrktbrk", "⎶", NULL},
+{"bcong", "≌", NULL},
+{"bcy", "б", NULL},
+{"bdquo", "„", "„"},
+{"becaus", "∵", NULL},
+{"because", "∵", NULL},
+{"bemptyv", "⦰", NULL},
+{"bepsi", "϶", NULL},
+{"bernou", "ℬ", NULL},
+{"beta", "β", "β"},
+{"beth", "ℶ", NULL},
+{"between", "≬", NULL},
+{"bfr", "𝔟", NULL},
+{"bigcap", "⋂", NULL},
+{"bigcirc", "◯", NULL},
+{"bigcup", "⋃", NULL},
+{"bigodot", "⨀", NULL},
+{"bigoplus", "⨁", NULL},
+{"bigotimes", "⨂", NULL},
+{"bigsqcup", "⨆", NULL},
+{"bigstar", "★", NULL},
+{"bigtriangledown", "▽", NULL},
+{"bigtriangleup", "△", NULL},
+{"biguplus", "⨄", NULL},
+{"bigvee", "⋁", NULL},
+{"bigwedge", "⋀", NULL},
+{"bkarow", "⤍", NULL},
+{"blacklozenge", "⧫", NULL},
+{"blacksquare", "▪", NULL},
+{"blacktriangle", "▴", NULL},
+{"blacktriangledown", "▾", NULL},
+{"blacktriangleleft", "◂", NULL},
+{"blacktriangleright", "▸", NULL},
+{"blank", "␣", NULL},
+{"blk12", "▒", NULL},
+{"blk14", "░", NULL},
+{"blk34", "▓", NULL},
+{"block", "█", NULL},
+{"bne", "=⃥", NULL},
+{"bnequiv", "≡⃥", NULL},
+{"bnot", "⌐", NULL},
+{"bopf", "𝕓", NULL},
+{"bot", "⊥", NULL},
+{"bottom", "⊥", NULL},
+{"bowtie", "⋈", NULL},
+{"boxDL", "╗", NULL},
+{"boxDR", "╔", NULL},
+{"boxDl", "╖", NULL},
+{"boxDr", "╓", NULL},
+{"boxH", "═", NULL},
+{"boxHD", "╦", NULL},
+{"boxHU", "╩", NULL},
+{"boxHd", "╤", NULL},
+{"boxHu", "╧", NULL},
+{"boxUL", "╝", NULL},
+{"boxUR", "╚", NULL},
+{"boxUl", "╜", NULL},
+{"boxUr", "╙", NULL},
+{"boxV", "║", NULL},
+{"boxVH", "╬", NULL},
+{"boxVL", "╣", NULL},
+{"boxVR", "╠", NULL},
+{"boxVh", "╫", NULL},
+{"boxVl", "╢", NULL},
+{"boxVr", "╟", NULL},
+{"boxbox", "⧉", NULL},
+{"boxdL", "╕", NULL},
+{"boxdR", "╒", NULL},
+{"boxdl", "┐", NULL},
+{"boxdr", "┌", NULL},
+{"boxh", "─", NULL},
+{"boxhD", "╥", NULL},
+{"boxhU", "╨", NULL},
+{"boxhd", "┬", NULL},
+{"boxhu", "┴", NULL},
+{"boxminus", "⊟", NULL},
+{"boxplus", "⊞", NULL},
+{"boxtimes", "⊠", NULL},
+{"boxuL", "╛", NULL},
+{"boxuR", "╘", NULL},
+{"boxul", "┘", NULL},
+{"boxur", "└", NULL},
+{"boxv", "│", NULL},
+{"boxvH", "╪", NULL},
+{"boxvL", "╡", NULL},
+{"boxvR", "╞", NULL},
+{"boxvh", "┼", NULL},
+{"boxvl", "┤", NULL},
+{"boxvr", "├", NULL},
+{"bprime", "‵", NULL},
+{"breve", "˘", NULL},
+{"brvbar", "¦", "¦"},
+{"bscr", "𝒷", NULL},
+{"bsemi", "⁏", NULL},
+{"bsim", "∽", NULL},
+{"bsime", "⋍", NULL},
+{"bsol", "\\", NULL},
+{"bsolb", "⧅", NULL},
+{"bsolhsub", "⟈", NULL},
+{"bull", "•", "•"},
+{"bullet", "•", NULL},
+{"bump", "≎", NULL},
+{"bumpE", "⪮", NULL},
+{"bumpe", "≏", NULL},
+{"bumpeq", "≏", NULL},
+{"cacute", "ć", NULL},
+{"cap", "∩", "∩"},
+{"capand", "⩄", NULL},
+{"capbrcup", "⩉", NULL},
+{"capcap", "⩋", NULL},
+{"capcup", "⩇", NULL},
+{"capdot", "⩀", NULL},
+{"caps", "∩︀", NULL},
+{"caret", "⁁", NULL},
+{"caron", "ˇ", NULL},
+{"ccaps", "⩍", NULL},
+{"ccaron", "č", NULL},
+{"ccedil", "ç", "ç"},
+{"ccirc", "ĉ", NULL},
+{"ccups", "⩌", NULL},
+{"ccupssm", "⩐", NULL},
+{"cdot", "ċ", NULL},
+{"cedil", "¸", "¸"},
+{"cemptyv", "⦲", NULL},
+{"cent", "¢", "¢"},
+{"centerdot", "·", NULL},
+{"cfr", "𝔠", NULL},
+{"chcy", "ч", NULL},
+{"check", "✓", NULL},
+{"checkmark", "✓", NULL},
+{"chi", "χ", "χ"},
+{"cir", "○", NULL},
+{"cirE", "⧃", NULL},
+{"circ", "ˆ", "ˆ"},
+{"circeq", "≗", NULL},
+{"circlearrowleft", "↺", NULL},
+{"circlearrowright", "↻", NULL},
+{"circledR", "®", NULL},
+{"circledS", "Ⓢ", NULL},
+{"circledast", "⊛", NULL},
+{"circledcirc", "⊚", NULL},
+{"circleddash", "⊝", NULL},
+{"cire", "≗", NULL},
+{"cirfnint", "⨐", NULL},
+{"cirmid", "⫯", NULL},
+{"cirscir", "⧂", NULL},
+{"clubs", "♣", "♣"},
+{"clubsuit", "♣", NULL},
+{"colon", ":", NULL},
+{"colone", "≔", NULL},
+{"coloneq", "≔", NULL},
+{"comma", ",", NULL},
+{"commat", "@", NULL},
+{"comp", "∁", NULL},
+{"compfn", "∘", NULL},
+{"complement", "∁", NULL},
+{"complexes", "ℂ", NULL},
+{"cong", "≅", "≅"},
+{"congdot", "⩭", NULL},
+{"conint", "∮", NULL},
+{"copf", "𝕔", NULL},
+{"coprod", "∐", NULL},
+{"copy", "©", "©"},
+{"copysr", "℗", NULL},
+{"crarr", "↵", "↵"},
+{"cross", "✗", NULL},
+{"cscr", "𝒸", NULL},
+{"csub", "⫏", NULL},
+{"csube", "⫑", NULL},
+{"csup", "⫐", NULL},
+{"csupe", "⫒", NULL},
+{"ctdot", "⋯", NULL},
+{"cudarrl", "⤸", NULL},
+{"cudarrr", "⤵", NULL},
+{"cuepr", "⋞", NULL},
+{"cuesc", "⋟", NULL},
+{"cularr", "↶", NULL},
+{"cularrp", "⤽", NULL},
+{"cup", "∪", "∪"},
+{"cupbrcap", "⩈", NULL},
+{"cupcap", "⩆", NULL},
+{"cupcup", "⩊", NULL},
+{"cupdot", "⊍", NULL},
+{"cupor", "⩅", NULL},
+{"cups", "∪︀", NULL},
+{"curarr", "↷", NULL},
+{"curarrm", "⤼", NULL},
+{"curlyeqprec", "⋞", NULL},
+{"curlyeqsucc", "⋟", NULL},
+{"curlyvee", "⋎", NULL},
+{"curlywedge", "⋏", NULL},
+{"curren", "¤", "¤"},
+{"curvearrowleft", "↶", NULL},
+{"curvearrowright", "↷", NULL},
+{"cuvee", "⋎", NULL},
+{"cuwed", "⋏", NULL},
+{"cwconint", "∲", NULL},
+{"cwint", "∱", NULL},
+{"cylcty", "⌭", NULL},
+{"dArr", "⇓", "⇓"},
+{"dHar", "⥥", NULL},
+{"dagger", "†", "†"},
+{"daleth", "ℸ", NULL},
+{"darr", "↓", "↓"},
+{"dash", "‐", NULL},
+{"dashv", "⊣", NULL},
+{"dbkarow", "⤏", NULL},
+{"dblac", "˝", NULL},
+{"dcaron", "ď", NULL},
+{"dcy", "д", NULL},
+{"dd", "ⅆ", NULL},
+{"ddagger", "‡", NULL},
+{"ddarr", "⇊", NULL},
+{"ddotseq", "⩷", NULL},
+{"deg", "°", "°"},
+{"delta", "δ", "δ"},
+{"demptyv", "⦱", NULL},
+{"dfisht", "⥿", NULL},
+{"dfr", "𝔡", NULL},
+{"dharl", "⇃", NULL},
+{"dharr", "⇂", NULL},
+{"diam", "⋄", NULL},
+{"diamond", "⋄", NULL},
+{"diamondsuit", "♦", NULL},
+{"diams", "♦", "♦"},
+{"die", "¨", NULL},
+{"digamma", "ϝ", NULL},
+{"disin", "⋲", NULL},
+{"div", "÷", NULL},
+{"divide", "÷", "÷"},
+{"divideontimes", "⋇", NULL},
+{"divonx", "⋇", NULL},
+{"djcy", "ђ", NULL},
+{"dlcorn", "⌞", NULL},
+{"dlcrop", "⌍", NULL},
+{"dollar", "$", NULL},
+{"dopf", "𝕕", NULL},
+{"dot", "˙", NULL},
+{"doteq", "≐", NULL},
+{"doteqdot", "≑", NULL},
+{"dotminus", "∸", NULL},
+{"dotplus", "∔", NULL},
+{"dotsquare", "⊡", NULL},
+{"doublebarwedge", "⌆", NULL},
+{"downarrow", "↓", NULL},
+{"downdownarrows", "⇊", NULL},
+{"downharpoonleft", "⇃", NULL},
+{"downharpoonright", "⇂", NULL},
+{"drbkarow", "⤐", NULL},
+{"drcorn", "⌟", NULL},
+{"drcrop", "⌌", NULL},
+{"dscr", "𝒹", NULL},
+{"dscy", "ѕ", NULL},
+{"dsol", "⧶", NULL},
+{"dstrok", "đ", NULL},
+{"dtdot", "⋱", NULL},
+{"dtri", "▿", NULL},
+{"dtrif", "▾", NULL},
+{"duarr", "⇵", NULL},
+{"duhar", "⥯", NULL},
+{"dwangle", "⦦", NULL},
+{"dzcy", "џ", NULL},
+{"dzigrarr", "⟿", NULL},
+{"eDDot", "⩷", NULL},
+{"eDot", "≑", NULL},
+{"eacute", "é", "é"},
+{"easter", "⩮", NULL},
+{"ecaron", "ě", NULL},
+{"ecir", "≖", NULL},
+{"ecirc", "ê", "ê"},
+{"ecolon", "≕", NULL},
+{"ecy", "э", NULL},
+{"edot", "ė", NULL},
+{"ee", "ⅇ", NULL},
+{"efDot", "≒", NULL},
+{"efr", "𝔢", NULL},
+{"eg", "⪚", NULL},
+{"egrave", "è", "è"},
+{"egs", "⪖", NULL},
+{"egsdot", "⪘", NULL},
+{"el", "⪙", NULL},
+{"elinters", "⏧", NULL},
+{"ell", "ℓ", NULL},
+{"els", "⪕", NULL},
+{"elsdot", "⪗", NULL},
+{"emacr", "ē", NULL},
+{"empty", "∅", "∅"},
+{"emptyset", "∅", NULL},
+{"emptyv", "∅", NULL},
+{"emsp", " ", " "},
+{"emsp13", " ", NULL},
+{"emsp14", " ", NULL},
+{"eng", "ŋ", NULL},
+{"ensp", " ", " "},
+{"eogon", "ę", NULL},
+{"eopf", "𝕖", NULL},
+{"epar", "⋕", NULL},
+{"eparsl", "⧣", NULL},
+{"eplus", "⩱", NULL},
+{"epsi", "ε", NULL},
+{"epsilon", "ε", "ε"},
+{"epsiv", "ϵ", NULL},
+{"eqcirc", "≖", NULL},
+{"eqcolon", "≕", NULL},
+{"eqsim", "≂", NULL},
+{"eqslantgtr", "⪖", NULL},
+{"eqslantless", "⪕", NULL},
+{"equals", "=", NULL},
+{"equest", "≟", NULL},
+{"equiv", "≡", "≡"},
+{"equivDD", "⩸", NULL},
+{"eqvparsl", "⧥", NULL},
+{"erDot", "≓", NULL},
+{"erarr", "⥱", NULL},
+{"escr", "ℯ", NULL},
+{"esdot", "≐", NULL},
+{"esim", "≂", NULL},
+{"eta", "η", "η"},
+{"eth", "ð", "ð"},
+{"euml", "ë", "ë"},
+{"euro", "€", "€"},
+{"excl", "!", NULL},
+{"exist", "∃", "∃"},
+{"expectation", "ℰ", NULL},
+{"exponentiale", "ⅇ", NULL},
+{"fallingdotseq", "≒", NULL},
+{"fcy", "ф", NULL},
+{"female", "♀", NULL},
+{"ffilig", "ffi", NULL},
+{"fflig", "ff", NULL},
+{"ffllig", "ffl", NULL},
+{"ffr", "𝔣", NULL},
+{"filig", "fi", NULL},
+{"fjlig", "fj", NULL},
+{"flat", "♭", NULL},
+{"fllig", "fl", NULL},
+{"fltns", "▱", NULL},
+{"fnof", "ƒ", "ƒ"},
+{"fopf", "𝕗", NULL},
+{"forall", "∀", "∀"},
+{"fork", "⋔", NULL},
+{"forkv", "⫙", NULL},
+{"fpartint", "⨍", NULL},
+{"frac12", "½", "½"},
+{"frac13", "⅓", NULL},
+{"frac14", "¼", "¼"},
+{"frac15", "⅕", NULL},
+{"frac16", "⅙", NULL},
+{"frac18", "⅛", NULL},
+{"frac23", "⅔", NULL},
+{"frac25", "⅖", NULL},
+{"frac34", "¾", "¾"},
+{"frac35", "⅗", NULL},
+{"frac38", "⅜", NULL},
+{"frac45", "⅘", NULL},
+{"frac56", "⅚", NULL},
+{"frac58", "⅝", NULL},
+{"frac78", "⅞", NULL},
+{"frasl", "⁄", "⁄"},
+{"frown", "⌢", NULL},
+{"fscr", "𝒻", NULL},
+{"gE", "≧", NULL},
+{"gEl", "⪌", NULL},
+{"gacute", "ǵ", NULL},
+{"gamma", "γ", "γ"},
+{"gammad", "ϝ", NULL},
+{"gap", "⪆", NULL},
+{"gbreve", "ğ", NULL},
+{"gcirc", "ĝ", NULL},
+{"gcy", "г", NULL},
+{"gdot", "ġ", NULL},
+{"ge", "≥", "≥"},
+{"gel", "⋛", NULL},
+{"geq", "≥", NULL},
+{"geqq", "≧", NULL},
+{"geqslant", "⩾", NULL},
+{"ges", "⩾", NULL},
+{"gescc", "⪩", NULL},
+{"gesdot", "⪀", NULL},
+{"gesdoto", "⪂", NULL},
+{"gesdotol", "⪄", NULL},
+{"gesl", "⋛︀", NULL},
+{"gesles", "⪔", NULL},
+{"gfr", "𝔤", NULL},
+{"gg", "≫", NULL},
+{"ggg", "⋙", NULL},
+{"gimel", "ℷ", NULL},
+{"gjcy", "ѓ", NULL},
+{"gl", "≷", NULL},
+{"glE", "⪒", NULL},
+{"gla", "⪥", NULL},
+{"glj", "⪤", NULL},
+{"gnE", "≩", NULL},
+{"gnap", "⪊", NULL},
+{"gnapprox", "⪊", NULL},
+{"gne", "⪈", NULL},
+{"gneq", "⪈", NULL},
+{"gneqq", "≩", NULL},
+{"gnsim", "⋧", NULL},
+{"gopf", "𝕘", NULL},
+{"grave", "`", NULL},
+{"gscr", "ℊ", NULL},
+{"gsim", "≳", NULL},
+{"gsime", "⪎", NULL},
+{"gsiml", "⪐", NULL},
+{"gt", ">", ">"},
+{"gtcc", "⪧", NULL},
+{"gtcir", "⩺", NULL},
+{"gtdot", "⋗", NULL},
+{"gtlPar", "⦕", NULL},
+{"gtquest", "⩼", NULL},
+{"gtrapprox", "⪆", NULL},
+{"gtrarr", "⥸", NULL},
+{"gtrdot", "⋗", NULL},
+{"gtreqless", "⋛", NULL},
+{"gtreqqless", "⪌", NULL},
+{"gtrless", "≷", NULL},
+{"gtrsim", "≳", NULL},
+{"gvertneqq", "≩︀", NULL},
+{"gvnE", "≩︀", NULL},
+{"hArr", "⇔", "⇔"},
+{"hairsp", " ", NULL},
+{"half", "½", NULL},
+{"hamilt", "ℋ", NULL},
+{"hardcy", "ъ", NULL},
+{"harr", "↔", "↔"},
+{"harrcir", "⥈", NULL},
+{"harrw", "↭", NULL},
+{"hbar", "ℏ", NULL},
+{"hcirc", "ĥ", NULL},
+{"hearts", "♥", "♥"},
+{"heartsuit", "♥", NULL},
+{"hellip", "…", "…"},
+{"hercon", "⊹", NULL},
+{"hfr", "𝔥", NULL},
+{"hksearow", "⤥", NULL},
+{"hkswarow", "⤦", NULL},
+{"hoarr", "⇿", NULL},
+{"homtht", "∻", NULL},
+{"hookleftarrow", "↩", NULL},
+{"hookrightarrow", "↪", NULL},
+{"hopf", "𝕙", NULL},
+{"horbar", "―", NULL},
+{"hscr", "𝒽", NULL},
+{"hslash", "ℏ", NULL},
+{"hstrok", "ħ", NULL},
+{"hybull", "⁃", NULL},
+{"hyphen", "‐", NULL},
+{"iacute", "í", "í"},
+{"ic", "⁣", NULL},
+{"icirc", "î", "î"},
+{"icy", "и", NULL},
+{"iecy", "е", NULL},
+{"iexcl", "¡", "¡"},
+{"iff", "⇔", NULL},
+{"ifr", "𝔦", NULL},
+{"igrave", "ì", "ì"},
+{"ii", "ⅈ", NULL},
+{"iiiint", "⨌", NULL},
+{"iiint", "∭", NULL},
+{"iinfin", "⧜", NULL},
+{"iiota", "℩", NULL},
+{"ijlig", "ij", NULL},
+{"imacr", "ī", NULL},
+{"image", "ℑ", "ℑ"},
+{"imagline", "ℐ", NULL},
+{"imagpart", "ℑ", NULL},
+{"imath", "ı", NULL},
+{"imof", "⊷", NULL},
+{"imped", "Ƶ", NULL},
+{"in", "∈", NULL},
+{"incare", "℅", NULL},
+{"infin", "∞", "∞"},
+{"infintie", "⧝", NULL},
+{"inodot", "ı", NULL},
+{"int", "∫", "∫"},
+{"intcal", "⊺", NULL},
+{"integers", "ℤ", NULL},
+{"intercal", "⊺", NULL},
+{"intlarhk", "⨗", NULL},
+{"intprod", "⨼", NULL},
+{"iocy", "ё", NULL},
+{"iogon", "į", NULL},
+{"iopf", "𝕚", NULL},
+{"iota", "ι", "ι"},
+{"iprod", "⨼", NULL},
+{"iquest", "¿", "¿"},
+{"iscr", "𝒾", NULL},
+{"isin", "∈", "∈"},
+{"isinE", "⋹", NULL},
+{"isindot", "⋵", NULL},
+{"isins", "⋴", NULL},
+{"isinsv", "⋳", NULL},
+{"isinv", "∈", NULL},
+{"it", "⁢", NULL},
+{"itilde", "ĩ", NULL},
+{"iukcy", "і", NULL},
+{"iuml", "ï", "ï"},
+{"jcirc", "ĵ", NULL},
+{"jcy", "й", NULL},
+{"jfr", "𝔧", NULL},
+{"jmath", "ȷ", NULL},
+{"jopf", "𝕛", NULL},
+{"jscr", "𝒿", NULL},
+{"jsercy", "ј", NULL},
+{"jukcy", "є", NULL},
+{"kappa", "κ", "κ"},
+{"kappav", "ϰ", NULL},
+{"kcedil", "ķ", NULL},
+{"kcy", "к", NULL},
+{"kfr", "𝔨", NULL},
+{"kgreen", "ĸ", NULL},
+{"khcy", "х", NULL},
+{"kjcy", "ќ", NULL},
+{"kopf", "𝕜", NULL},
+{"kscr", "𝓀", NULL},
+{"lAarr", "⇚", NULL},
+{"lArr", "⇐", "⇐"},
+{"lAtail", "⤛", NULL},
+{"lBarr", "⤎", NULL},
+{"lE", "≦", NULL},
+{"lEg", "⪋", NULL},
+{"lHar", "⥢", NULL},
+{"lacute", "ĺ", NULL},
+{"laemptyv", "⦴", NULL},
+{"lagran", "ℒ", NULL},
+{"lambda", "λ", "λ"},
+{"lang", "⟨", "〈"},
+{"langd", "⦑", NULL},
+{"langle", "⟨", NULL},
+{"lap", "⪅", NULL},
+{"laquo", "«", "«"},
+{"larr", "←", "←"},
+{"larrb", "⇤", NULL},
+{"larrbfs", "⤟", NULL},
+{"larrfs", "⤝", NULL},
+{"larrhk", "↩", NULL},
+{"larrlp", "↫", NULL},
+{"larrpl", "⤹", NULL},
+{"larrsim", "⥳", NULL},
+{"larrtl", "↢", NULL},
+{"lat", "⪫", NULL},
+{"latail", "⤙", NULL},
+{"late", "⪭", NULL},
+{"lates", "⪭︀", NULL},
+{"lbarr", "⤌", NULL},
+{"lbbrk", "❲", NULL},
+{"lbrace", "{", NULL},
+{"lbrack", "[", NULL},
+{"lbrke", "⦋", NULL},
+{"lbrksld", "⦏", NULL},
+{"lbrkslu", "⦍", NULL},
+{"lcaron", "ľ", NULL},
+{"lcedil", "ļ", NULL},
+{"lceil", "⌈", "⌈"},
+{"lcub", "{", NULL},
+{"lcy", "л", NULL},
+{"ldca", "⤶", NULL},
+{"ldquo", "“", "“"},
+{"ldquor", "„", NULL},
+{"ldrdhar", "⥧", NULL},
+{"ldrushar", "⥋", NULL},
+{"ldsh", "↲", NULL},
+{"le", "≤", "≤"},
+{"leftarrow", "←", NULL},
+{"leftarrowtail", "↢", NULL},
+{"leftharpoondown", "↽", NULL},
+{"leftharpoonup", "↼", NULL},
+{"leftleftarrows", "⇇", NULL},
+{"leftrightarrow", "↔", NULL},
+{"leftrightarrows", "⇆", NULL},
+{"leftrightharpoons", "⇋", NULL},
+{"leftrightsquigarrow", "↭", NULL},
+{"leftthreetimes", "⋋", NULL},
+{"leg", "⋚", NULL},
+{"leq", "≤", NULL},
+{"leqq", "≦", NULL},
+{"leqslant", "⩽", NULL},
+{"les", "⩽", NULL},
+{"lescc", "⪨", NULL},
+{"lesdot", "⩿", NULL},
+{"lesdoto", "⪁", NULL},
+{"lesdotor", "⪃", NULL},
+{"lesg", "⋚︀", NULL},
+{"lesges", "⪓", NULL},
+{"lessapprox", "⪅", NULL},
+{"lessdot", "⋖", NULL},
+{"lesseqgtr", "⋚", NULL},
+{"lesseqqgtr", "⪋", NULL},
+{"lessgtr", "≶", NULL},
+{"lesssim", "≲", NULL},
+{"lfisht", "⥼", NULL},
+{"lfloor", "⌊", "⌊"},
+{"lfr", "𝔩", NULL},
+{"lg", "≶", NULL},
+{"lgE", "⪑", NULL},
+{"lhard", "↽", NULL},
+{"lharu", "↼", NULL},
+{"lharul", "⥪", NULL},
+{"lhblk", "▄", NULL},
+{"ljcy", "љ", NULL},
+{"ll", "≪", NULL},
+{"llarr", "⇇", NULL},
+{"llcorner", "⌞", NULL},
+{"llhard", "⥫", NULL},
+{"lltri", "◺", NULL},
+{"lmidot", "ŀ", NULL},
+{"lmoust", "⎰", NULL},
+{"lmoustache", "⎰", NULL},
+{"lnE", "≨", NULL},
+{"lnap", "⪉", NULL},
+{"lnapprox", "⪉", NULL},
+{"lne", "⪇", NULL},
+{"lneq", "⪇", NULL},
+{"lneqq", "≨", NULL},
+{"lnsim", "⋦", NULL},
+{"loang", "⟬", NULL},
+{"loarr", "⇽", NULL},
+{"lobrk", "⟦", NULL},
+{"longleftarrow", "⟵", NULL},
+{"longleftrightarrow", "⟷", NULL},
+{"longmapsto", "⟼", NULL},
+{"longrightarrow", "⟶", NULL},
+{"looparrowleft", "↫", NULL},
+{"looparrowright", "↬", NULL},
+{"lopar", "⦅", NULL},
+{"lopf", "𝕝", NULL},
+{"loplus", "⨭", NULL},
+{"lotimes", "⨴", NULL},
+{"lowast", "∗", "∗"},
+{"lowbar", "_", NULL},
+{"loz", "◊", "◊"},
+{"lozenge", "◊", NULL},
+{"lozf", "⧫", NULL},
+{"lpar", "(", NULL},
+{"lparlt", "⦓", NULL},
+{"lrarr", "⇆", NULL},
+{"lrcorner", "⌟", NULL},
+{"lrhar", "⇋", NULL},
+{"lrhard", "⥭", NULL},
+{"lrm", "‎", "‎"},
+{"lrtri", "⊿", NULL},
+{"lsaquo", "‹", "‹"},
+{"lscr", "𝓁", NULL},
+{"lsh", "↰", NULL},
+{"lsim", "≲", NULL},
+{"lsime", "⪍", NULL},
+{"lsimg", "⪏", NULL},
+{"lsqb", "[", NULL},
+{"lsquo", "‘", "‘"},
+{"lsquor", "‚", NULL},
+{"lstrok", "ł", NULL},
+{"lt", "<", "<"},
+{"ltcc", "⪦", NULL},
+{"ltcir", "⩹", NULL},
+{"ltdot", "⋖", NULL},
+{"lthree", "⋋", NULL},
+{"ltimes", "⋉", NULL},
+{"ltlarr", "⥶", NULL},
+{"ltquest", "⩻", NULL},
+{"ltrPar", "⦖", NULL},
+{"ltri", "◃", NULL},
+{"ltrie", "⊴", NULL},
+{"ltrif", "◂", NULL},
+{"lurdshar", "⥊", NULL},
+{"luruhar", "⥦", NULL},
+{"lvertneqq", "≨︀", NULL},
+{"lvnE", "≨︀", NULL},
+{"mDDot", "∺", NULL},
+{"macr", "¯", "¯"},
+{"male", "♂", NULL},
+{"malt", "✠", NULL},
+{"maltese", "✠", NULL},
+{"map", "↦", NULL},
+{"mapsto", "↦", NULL},
+{"mapstodown", "↧", NULL},
+{"mapstoleft", "↤", NULL},
+{"mapstoup", "↥", NULL},
+{"marker", "▮", NULL},
+{"mcomma", "⨩", NULL},
+{"mcy", "м", NULL},
+{"mdash", "—", "—"},
+{"measuredangle", "∡", NULL},
+{"mfr", "𝔪", NULL},
+{"mho", "℧", NULL},
+{"micro", "µ", "µ"},
+{"mid", "∣", NULL},
+{"midast", "*", NULL},
+{"midcir", "⫰", NULL},
+{"middot", "·", "·"},
+{"minus", "−", "−"},
+{"minusb", "⊟", NULL},
+{"minusd", "∸", NULL},
+{"minusdu", "⨪", NULL},
+{"mlcp", "⫛", NULL},
+{"mldr", "…", NULL},
+{"mnplus", "∓", NULL},
+{"models", "⊧", NULL},
+{"mopf", "𝕞", NULL},
+{"mp", "∓", NULL},
+{"mscr", "𝓂", NULL},
+{"mstpos", "∾", NULL},
+{"mu", "μ", "μ"},
+{"multimap", "⊸", NULL},
+{"mumap", "⊸", NULL},
+{"nGg", "⋙̸", NULL},
+{"nGt", "≫⃒", NULL},
+{"nGtv", "≫̸", NULL},
+{"nLeftarrow", "⇍", NULL},
+{"nLeftrightarrow", "⇎", NULL},
+{"nLl", "⋘̸", NULL},
+{"nLt", "≪⃒", NULL},
+{"nLtv", "≪̸", NULL},
+{"nRightarrow", "⇏", NULL},
+{"nVDash", "⊯", NULL},
+{"nVdash", "⊮", NULL},
+{"nabla", "∇", "∇"},
+{"nacute", "ń", NULL},
+{"nang", "∠⃒", NULL},
+{"nap", "≉", NULL},
+{"napE", "⩰̸", NULL},
+{"napid", "≋̸", NULL},
+{"napos", "ʼn", NULL},
+{"napprox", "≉", NULL},
+{"natur", "♮", NULL},
+{"natural", "♮", NULL},
+{"naturals", "ℕ", NULL},
+{"nbsp", " ", " "},
+{"nbump", "≎̸", NULL},
+{"nbumpe", "≏̸", NULL},
+{"ncap", "⩃", NULL},
+{"ncaron", "ň", NULL},
+{"ncedil", "ņ", NULL},
+{"ncong", "≇", NULL},
+{"ncongdot", "⩭̸", NULL},
+{"ncup", "⩂", NULL},
+{"ncy", "н", NULL},
+{"ndash", "–", "–"},
+{"ne", "≠", "≠"},
+{"neArr", "⇗", NULL},
+{"nearhk", "⤤", NULL},
+{"nearr", "↗", NULL},
+{"nearrow", "↗", NULL},
+{"nedot", "≐̸", NULL},
+{"nequiv", "≢", NULL},
+{"nesear", "⤨", NULL},
+{"nesim", "≂̸", NULL},
+{"nexist", "∄", NULL},
+{"nexists", "∄", NULL},
+{"nfr", "𝔫", NULL},
+{"ngE", "≧̸", NULL},
+{"nge", "≱", NULL},
+{"ngeq", "≱", NULL},
+{"ngeqq", "≧̸", NULL},
+{"ngeqslant", "⩾̸", NULL},
+{"nges", "⩾̸", NULL},
+{"ngsim", "≵", NULL},
+{"ngt", "≯", NULL},
+{"ngtr", "≯", NULL},
+{"nhArr", "⇎", NULL},
+{"nharr", "↮", NULL},
+{"nhpar", "⫲", NULL},
+{"ni", "∋", "∋"},
+{"nis", "⋼", NULL},
+{"nisd", "⋺", NULL},
+{"niv", "∋", NULL},
+{"njcy", "њ", NULL},
+{"nlArr", "⇍", NULL},
+{"nlE", "≦̸", NULL},
+{"nlarr", "↚", NULL},
+{"nldr", "‥", NULL},
+{"nle", "≰", NULL},
+{"nleftarrow", "↚", NULL},
+{"nleftrightarrow", "↮", NULL},
+{"nleq", "≰", NULL},
+{"nleqq", "≦̸", NULL},
+{"nleqslant", "⩽̸", NULL},
+{"nles", "⩽̸", NULL},
+{"nless", "≮", NULL},
+{"nlsim", "≴", NULL},
+{"nlt", "≮", NULL},
+{"nltri", "⋪", NULL},
+{"nltrie", "⋬", NULL},
+{"nmid", "∤", NULL},
+{"nopf", "𝕟", NULL},
+{"not", "¬", "¬"},
+{"notin", "∉", "∉"},
+{"notinE", "⋹̸", NULL},
+{"notindot", "⋵̸", NULL},
+{"notinva", "∉", NULL},
+{"notinvb", "⋷", NULL},
+{"notinvc", "⋶", NULL},
+{"notni", "∌", NULL},
+{"notniva", "∌", NULL},
+{"notnivb", "⋾", NULL},
+{"notnivc", "⋽", NULL},
+{"npar", "∦", NULL},
+{"nparallel", "∦", NULL},
+{"nparsl", "⫽⃥", NULL},
+{"npart", "∂̸", NULL},
+{"npolint", "⨔", NULL},
+{"npr", "⊀", NULL},
+{"nprcue", "⋠", NULL},
+{"npre", "⪯̸", NULL},
+{"nprec", "⊀", NULL},
+{"npreceq", "⪯̸", NULL},
+{"nrArr", "⇏", NULL},
+{"nrarr", "↛", NULL},
+{"nrarrc", "⤳̸", NULL},
+{"nrarrw", "↝̸", NULL},
+{"nrightarrow", "↛", NULL},
+{"nrtri", "⋫", NULL},
+{"nrtrie", "⋭", NULL},
+{"nsc", "⊁", NULL},
+{"nsccue", "⋡", NULL},
+{"nsce", "⪰̸", NULL},
+{"nscr", "𝓃", NULL},
+{"nshortmid", "∤", NULL},
+{"nshortparallel", "∦", NULL},
+{"nsim", "≁", NULL},
+{"nsime", "≄", NULL},
+{"nsimeq", "≄", NULL},
+{"nsmid", "∤", NULL},
+{"nspar", "∦", NULL},
+{"nsqsube", "⋢", NULL},
+{"nsqsupe", "⋣", NULL},
+{"nsub", "⊄", "⊄"},
+{"nsubE", "⫅̸", NULL},
+{"nsube", "⊈", NULL},
+{"nsubset", "⊂⃒", NULL},
+{"nsubseteq", "⊈", NULL},
+{"nsubseteqq", "⫅̸", NULL},
+{"nsucc", "⊁", NULL},
+{"nsucceq", "⪰̸", NULL},
+{"nsup", "⊅", NULL},
+{"nsupE", "⫆̸", NULL},
+{"nsupe", "⊉", NULL},
+{"nsupset", "⊃⃒", NULL},
+{"nsupseteq", "⊉", NULL},
+{"nsupseteqq", "⫆̸", NULL},
+{"ntgl", "≹", NULL},
+{"ntilde", "ñ", "ñ"},
+{"ntlg", "≸", NULL},
+{"ntriangleleft", "⋪", NULL},
+{"ntrianglelefteq", "⋬", NULL},
+{"ntriangleright", "⋫", NULL},
+{"ntrianglerighteq", "⋭", NULL},
+{"nu", "ν", "ν"},
+{"num", "#", NULL},
+{"numero", "№", NULL},
+{"numsp", " ", NULL},
+{"nvDash", "⊭", NULL},
+{"nvHarr", "⤄", NULL},
+{"nvap", "≍⃒", NULL},
+{"nvdash", "⊬", NULL},
+{"nvge", "≥⃒", NULL},
+{"nvgt", ">⃒", NULL},
+{"nvinfin", "⧞", NULL},
+{"nvlArr", "⤂", NULL},
+{"nvle", "≤⃒", NULL},
+{"nvlt", "<⃒", NULL},
+{"nvltrie", "⊴⃒", NULL},
+{"nvrArr", "⤃", NULL},
+{"nvrtrie", "⊵⃒", NULL},
+{"nvsim", "∼⃒", NULL},
+{"nwArr", "⇖", NULL},
+{"nwarhk", "⤣", NULL},
+{"nwarr", "↖", NULL},
+{"nwarrow", "↖", NULL},
+{"nwnear", "⤧", NULL},
+{"oS", "Ⓢ", NULL},
+{"oacute", "ó", "ó"},
+{"oast", "⊛", NULL},
+{"ocir", "⊚", NULL},
+{"ocirc", "ô", "ô"},
+{"ocy", "о", NULL},
+{"odash", "⊝", NULL},
+{"odblac", "ő", NULL},
+{"odiv", "⨸", NULL},
+{"odot", "⊙", NULL},
+{"odsold", "⦼", NULL},
+{"oelig", "œ", "œ"},
+{"ofcir", "⦿", NULL},
+{"ofr", "𝔬", NULL},
+{"ogon", "˛", NULL},
+{"ograve", "ò", "ò"},
+{"ogt", "⧁", NULL},
+{"ohbar", "⦵", NULL},
+{"ohm", "Ω", NULL},
+{"oint", "∮", NULL},
+{"olarr", "↺", NULL},
+{"olcir", "⦾", NULL},
+{"olcross", "⦻", NULL},
+{"oline", "‾", "‾"},
+{"olt", "⧀", NULL},
+{"omacr", "ō", NULL},
+{"omega", "ω", "ω"},
+{"omicron", "ο", "ο"},
+{"omid", "⦶", NULL},
+{"ominus", "⊖", NULL},
+{"oopf", "𝕠", NULL},
+{"opar", "⦷", NULL},
+{"operp", "⦹", NULL},
+{"oplus", "⊕", "⊕"},
+{"or", "∨", "∨"},
+{"orarr", "↻", NULL},
+{"ord", "⩝", NULL},
+{"order", "ℴ", NULL},
+{"orderof", "ℴ", NULL},
+{"ordf", "ª", "ª"},
+{"ordm", "º", "º"},
+{"origof", "⊶", NULL},
+{"oror", "⩖", NULL},
+{"orslope", "⩗", NULL},
+{"orv", "⩛", NULL},
+{"oscr", "ℴ", NULL},
+{"oslash", "ø", "ø"},
+{"osol", "⊘", NULL},
+{"otilde", "õ", "õ"},
+{"otimes", "⊗", "⊗"},
+{"otimesas", "⨶", NULL},
+{"ouml", "ö", "ö"},
+{"ovbar", "⌽", NULL},
+{"par", "∥", NULL},
+{"para", "¶", "¶"},
+{"parallel", "∥", NULL},
+{"parsim", "⫳", NULL},
+{"parsl", "⫽", NULL},
+{"part", "∂", "∂"},
+{"pcy", "п", NULL},
+{"percnt", "%", NULL},
+{"period", ".", NULL},
+{"permil", "‰", "‰"},
+{"perp", "⊥", "⊥"},
+{"pertenk", "‱", NULL},
+{"pfr", "𝔭", NULL},
+{"phi", "φ", "φ"},
+{"phiv", "ϕ", NULL},
+{"phmmat", "ℳ", NULL},
+{"phone", "☎", NULL},
+{"pi", "π", "π"},
+{"pitchfork", "⋔", NULL},
+{"piv", "ϖ", "ϖ"},
+{"planck", "ℏ", NULL},
+{"planckh", "ℎ", NULL},
+{"plankv", "ℏ", NULL},
+{"plus", "+", NULL},
+{"plusacir", "⨣", NULL},
+{"plusb", "⊞", NULL},
+{"pluscir", "⨢", NULL},
+{"plusdo", "∔", NULL},
+{"plusdu", "⨥", NULL},
+{"pluse", "⩲", NULL},
+{"plusmn", "±", "±"},
+{"plussim", "⨦", NULL},
+{"plustwo", "⨧", NULL},
+{"pm", "±", NULL},
+{"pointint", "⨕", NULL},
+{"popf", "𝕡", NULL},
+{"pound", "£", "£"},
+{"pr", "≺", NULL},
+{"prE", "⪳", NULL},
+{"prap", "⪷", NULL},
+{"prcue", "≼", NULL},
+{"pre", "⪯", NULL},
+{"prec", "≺", NULL},
+{"precapprox", "⪷", NULL},
+{"preccurlyeq", "≼", NULL},
+{"preceq", "⪯", NULL},
+{"precnapprox", "⪹", NULL},
+{"precneqq", "⪵", NULL},
+{"precnsim", "⋨", NULL},
+{"precsim", "≾", NULL},
+{"prime", "′", "′"},
+{"primes", "ℙ", NULL},
+{"prnE", "⪵", NULL},
+{"prnap", "⪹", NULL},
+{"prnsim", "⋨", NULL},
+{"prod", "∏", "∏"},
+{"profalar", "⌮", NULL},
+{"profline", "⌒", NULL},
+{"profsurf", "⌓", NULL},
+{"prop", "∝", "∝"},
+{"propto", "∝", NULL},
+{"prsim", "≾", NULL},
+{"prurel", "⊰", NULL},
+{"pscr", "𝓅", NULL},
+{"psi", "ψ", "ψ"},
+{"puncsp", " ", NULL},
+{"qfr", "𝔮", NULL},
+{"qint", "⨌", NULL},
+{"qopf", "𝕢", NULL},
+{"qprime", "⁗", NULL},
+{"qscr", "𝓆", NULL},
+{"quaternions", "ℍ", NULL},
+{"quatint", "⨖", NULL},
+{"quest", "?", NULL},
+{"questeq", "≟", NULL},
+{"quot", "\"", "\""},
+{"rAarr", "⇛", NULL},
+{"rArr", "⇒", "⇒"},
+{"rAtail", "⤜", NULL},
+{"rBarr", "⤏", NULL},
+{"rHar", "⥤", NULL},
+{"race", "∽̱", NULL},
+{"racute", "ŕ", NULL},
+{"radic", "√", "√"},
+{"raemptyv", "⦳", NULL},
+{"rang", "⟩", "〉"},
+{"rangd", "⦒", NULL},
+{"range", "⦥", NULL},
+{"rangle", "⟩", NULL},
+{"raquo", "»", "»"},
+{"rarr", "→", "→"},
+{"rarrap", "⥵", NULL},
+{"rarrb", "⇥", NULL},
+{"rarrbfs", "⤠", NULL},
+{"rarrc", "⤳", NULL},
+{"rarrfs", "⤞", NULL},
+{"rarrhk", "↪", NULL},
+{"rarrlp", "↬", NULL},
+{"rarrpl", "⥅", NULL},
+{"rarrsim", "⥴", NULL},
+{"rarrtl", "↣", NULL},
+{"rarrw", "↝", NULL},
+{"ratail", "⤚", NULL},
+{"ratio", "∶", NULL},
+{"rationals", "ℚ", NULL},
+{"rbarr", "⤍", NULL},
+{"rbbrk", "❳", NULL},
+{"rbrace", "}", NULL},
+{"rbrack", "]", NULL},
+{"rbrke", "⦌", NULL},
+{"rbrksld", "⦎", NULL},
+{"rbrkslu", "⦐", NULL},
+{"rcaron", "ř", NULL},
+{"rcedil", "ŗ", NULL},
+{"rceil", "⌉", "⌉"},
+{"rcub", "}", NULL},
+{"rcy", "р", NULL},
+{"rdca", "⤷", NULL},
+{"rdldhar", "⥩", NULL},
+{"rdquo", "”", "”"},
+{"rdquor", "”", NULL},
+{"rdsh", "↳", NULL},
+{"real", "ℜ", "ℜ"},
+{"realine", "ℛ", NULL},
+{"realpart", "ℜ", NULL},
+{"reals", "ℝ", NULL},
+{"rect", "▭", NULL},
+{"reg", "®", "®"},
+{"rfisht", "⥽", NULL},
+{"rfloor", "⌋", "⌋"},
+{"rfr", "𝔯", NULL},
+{"rhard", "⇁", NULL},
+{"rharu", "⇀", NULL},
+{"rharul", "⥬", NULL},
+{"rho", "ρ", "ρ"},
+{"rhov", "ϱ", NULL},
+{"rightarrow", "→", NULL},
+{"rightarrowtail", "↣", NULL},
+{"rightharpoondown", "⇁", NULL},
+{"rightharpoonup", "⇀", NULL},
+{"rightleftarrows", "⇄", NULL},
+{"rightleftharpoons", "⇌", NULL},
+{"rightrightarrows", "⇉", NULL},
+{"rightsquigarrow", "↝", NULL},
+{"rightthreetimes", "⋌", NULL},
+{"ring", "˚", NULL},
+{"risingdotseq", "≓", NULL},
+{"rlarr", "⇄", NULL},
+{"rlhar", "⇌", NULL},
+{"rlm", "‏", "‏"},
+{"rmoust", "⎱", NULL},
+{"rmoustache", "⎱", NULL},
+{"rnmid", "⫮", NULL},
+{"roang", "⟭", NULL},
+{"roarr", "⇾", NULL},
+{"robrk", "⟧", NULL},
+{"ropar", "⦆", NULL},
+{"ropf", "𝕣", NULL},
+{"roplus", "⨮", NULL},
+{"rotimes", "⨵", NULL},
+{"rpar", ")", NULL},
+{"rpargt", "⦔", NULL},
+{"rppolint", "⨒", NULL},
+{"rrarr", "⇉", NULL},
+{"rsaquo", "›", "›"},
+{"rscr", "𝓇", NULL},
+{"rsh", "↱", NULL},
+{"rsqb", "]", NULL},
+{"rsquo", "’", "’"},
+{"rsquor", "’", NULL},
+{"rthree", "⋌", NULL},
+{"rtimes", "⋊", NULL},
+{"rtri", "▹", NULL},
+{"rtrie", "⊵", NULL},
+{"rtrif", "▸", NULL},
+{"rtriltri", "⧎", NULL},
+{"ruluhar", "⥨", NULL},
+{"rx", "℞", NULL},
+{"sacute", "ś", NULL},
+{"sbquo", "‚", "‚"},
+{"sc", "≻", NULL},
+{"scE", "⪴", NULL},
+{"scap", "⪸", NULL},
+{"scaron", "š", "š"},
+{"sccue", "≽", NULL},
+{"sce", "⪰", NULL},
+{"scedil", "ş", NULL},
+{"scirc", "ŝ", NULL},
+{"scnE", "⪶", NULL},
+{"scnap", "⪺", NULL},
+{"scnsim", "⋩", NULL},
+{"scpolint", "⨓", NULL},
+{"scsim", "≿", NULL},
+{"scy", "с", NULL},
+{"sdot", "⋅", "⋅"},
+{"sdotb", "⊡", NULL},
+{"sdote", "⩦", NULL},
+{"seArr", "⇘", NULL},
+{"searhk", "⤥", NULL},
+{"searr", "↘", NULL},
+{"searrow", "↘", NULL},
+{"sect", "§", "§"},
+{"semi", ";", NULL},
+{"seswar", "⤩", NULL},
+{"setminus", "∖", NULL},
+{"setmn", "∖", NULL},
+{"sext", "✶", NULL},
+{"sfr", "𝔰", NULL},
+{"sfrown", "⌢", NULL},
+{"sharp", "♯", NULL},
+{"shchcy", "щ", NULL},
+{"shcy", "ш", NULL},
+{"shortmid", "∣", NULL},
+{"shortparallel", "∥", NULL},
+{"shy", "­", "­"},
+{"sigma", "σ", "σ"},
+{"sigmaf", "ς", "ς"},
+{"sigmav", "ς", NULL},
+{"sim", "∼", "∼"},
+{"simdot", "⩪", NULL},
+{"sime", "≃", NULL},
+{"simeq", "≃", NULL},
+{"simg", "⪞", NULL},
+{"simgE", "⪠", NULL},
+{"siml", "⪝", NULL},
+{"simlE", "⪟", NULL},
+{"simne", "≆", NULL},
+{"simplus", "⨤", NULL},
+{"simrarr", "⥲", NULL},
+{"slarr", "←", NULL},
+{"smallsetminus", "∖", NULL},
+{"smashp", "⨳", NULL},
+{"smeparsl", "⧤", NULL},
+{"smid", "∣", NULL},
+{"smile", "⌣", NULL},
+{"smt", "⪪", NULL},
+{"smte", "⪬", NULL},
+{"smtes", "⪬︀", NULL},
+{"softcy", "ь", NULL},
+{"sol", "/", NULL},
+{"solb", "⧄", NULL},
+{"solbar", "⌿", NULL},
+{"sopf", "𝕤", NULL},
+{"spades", "♠", "♠"},
+{"spadesuit", "♠", NULL},
+{"spar", "∥", NULL},
+{"sqcap", "⊓", NULL},
+{"sqcaps", "⊓︀", NULL},
+{"sqcup", "⊔", NULL},
+{"sqcups", "⊔︀", NULL},
+{"sqsub", "⊏", NULL},
+{"sqsube", "⊑", NULL},
+{"sqsubset", "⊏", NULL},
+{"sqsubseteq", "⊑", NULL},
+{"sqsup", "⊐", NULL},
+{"sqsupe", "⊒", NULL},
+{"sqsupset", "⊐", NULL},
+{"sqsupseteq", "⊒", NULL},
+{"squ", "□", NULL},
+{"square", "□", NULL},
+{"squarf", "▪", NULL},
+{"squf", "▪", NULL},
+{"srarr", "→", NULL},
+{"sscr", "𝓈", NULL},
+{"ssetmn", "∖", NULL},
+{"ssmile", "⌣", NULL},
+{"sstarf", "⋆", NULL},
+{"star", "☆", NULL},
+{"starf", "★", NULL},
+{"straightepsilon", "ϵ", NULL},
+{"straightphi", "ϕ", NULL},
+{"strns", "¯", NULL},
+{"sub", "⊂", "⊂"},
+{"subE", "⫅", NULL},
+{"subdot", "⪽", NULL},
+{"sube", "⊆", "⊆"},
+{"subedot", "⫃", NULL},
+{"submult", "⫁", NULL},
+{"subnE", "⫋", NULL},
+{"subne", "⊊", NULL},
+{"subplus", "⪿", NULL},
+{"subrarr", "⥹", NULL},
+{"subset", "⊂", NULL},
+{"subseteq", "⊆", NULL},
+{"subseteqq", "⫅", NULL},
+{"subsetneq", "⊊", NULL},
+{"subsetneqq", "⫋", NULL},
+{"subsim", "⫇", NULL},
+{"subsub", "⫕", NULL},
+{"subsup", "⫓", NULL},
+{"succ", "≻", NULL},
+{"succapprox", "⪸", NULL},
+{"succcurlyeq", "≽", NULL},
+{"succeq", "⪰", NULL},
+{"succnapprox", "⪺", NULL},
+{"succneqq", "⪶", NULL},
+{"succnsim", "⋩", NULL},
+{"succsim", "≿", NULL},
+{"sum", "∑", "∑"},
+{"sung", "♪", NULL},
+{"sup", "⊃", "⊃"},
+{"sup1", "¹", "¹"},
+{"sup2", "²", "²"},
+{"sup3", "³", "³"},
+{"supE", "⫆", NULL},
+{"supdot", "⪾", NULL},
+{"supdsub", "⫘", NULL},
+{"supe", "⊇", "⊇"},
+{"supedot", "⫄", NULL},
+{"suphsol", "⟉", NULL},
+{"suphsub", "⫗", NULL},
+{"suplarr", "⥻", NULL},
+{"supmult", "⫂", NULL},
+{"supnE", "⫌", NULL},
+{"supne", "⊋", NULL},
+{"supplus", "⫀", NULL},
+{"supset", "⊃", NULL},
+{"supseteq", "⊇", NULL},
+{"supseteqq", "⫆", NULL},
+{"supsetneq", "⊋", NULL},
+{"supsetneqq", "⫌", NULL},
+{"supsim", "⫈", NULL},
+{"supsub", "⫔", NULL},
+{"supsup", "⫖", NULL},
+{"swArr", "⇙", NULL},
+{"swarhk", "⤦", NULL},
+{"swarr", "↙", NULL},
+{"swarrow", "↙", NULL},
+{"swnwar", "⤪", NULL},
+{"szlig", "ß", "ß"},
+{"target", "⌖", NULL},
+{"tau", "τ", "τ"},
+{"tbrk", "⎴", NULL},
+{"tcaron", "ť", NULL},
+{"tcedil", "ţ", NULL},
+{"tcy", "т", NULL},
+{"tdot", "⃛", NULL},
+{"telrec", "⌕", NULL},
+{"tfr", "𝔱", NULL},
+{"there4", "∴", "∴"},
+{"therefore", "∴", NULL},
+{"theta", "θ", "θ"},
+{"thetasym", "ϑ", "ϑ"},
+{"thetav", "ϑ", NULL},
+{"thickapprox", "≈", NULL},
+{"thicksim", "∼", NULL},
+{"thinsp", " ", " "},
+{"thkap", "≈", NULL},
+{"thksim", "∼", NULL},
+{"thorn", "þ", "þ"},
+{"tilde", "˜", "˜"},
+{"times", "×", "×"},
+{"timesb", "⊠", NULL},
+{"timesbar", "⨱", NULL},
+{"timesd", "⨰", NULL},
+{"tint", "∭", NULL},
+{"toea", "⤨", NULL},
+{"top", "⊤", NULL},
+{"topbot", "⌶", NULL},
+{"topcir", "⫱", NULL},
+{"topf", "𝕥", NULL},
+{"topfork", "⫚", NULL},
+{"tosa", "⤩", NULL},
+{"tprime", "‴", NULL},
+{"trade", "™", "™"},
+{"triangle", "▵", NULL},
+{"triangledown", "▿", NULL},
+{"triangleleft", "◃", NULL},
+{"trianglelefteq", "⊴", NULL},
+{"triangleq", "≜", NULL},
+{"triangleright", "▹", NULL},
+{"trianglerighteq", "⊵", NULL},
+{"tridot", "◬", NULL},
+{"trie", "≜", NULL},
+{"triminus", "⨺", NULL},
+{"triplus", "⨹", NULL},
+{"trisb", "⧍", NULL},
+{"tritime", "⨻", NULL},
+{"trpezium", "⏢", NULL},
+{"tscr", "𝓉", NULL},
+{"tscy", "ц", NULL},
+{"tshcy", "ћ", NULL},
+{"tstrok", "ŧ", NULL},
+{"twixt", "≬", NULL},
+{"twoheadleftarrow", "↞", NULL},
+{"twoheadrightarrow", "↠", NULL},
+{"uArr", "⇑", "⇑"},
+{"uHar", "⥣", NULL},
+{"uacute", "ú", "ú"},
+{"uarr", "↑", "↑"},
+{"ubrcy", "ў", NULL},
+{"ubreve", "ŭ", NULL},
+{"ucirc", "û", "û"},
+{"ucy", "у", NULL},
+{"udarr", "⇅", NULL},
+{"udblac", "ű", NULL},
+{"udhar", "⥮", NULL},
+{"ufisht", "⥾", NULL},
+{"ufr", "𝔲", NULL},
+{"ugrave", "ù", "ù"},
+{"uharl", "↿", NULL},
+{"uharr", "↾", NULL},
+{"uhblk", "▀", NULL},
+{"ulcorn", "⌜", NULL},
+{"ulcorner", "⌜", NULL},
+{"ulcrop", "⌏", NULL},
+{"ultri", "◸", NULL},
+{"umacr", "ū", NULL},
+{"uml", "¨", "¨"},
+{"uogon", "ų", NULL},
+{"uopf", "𝕦", NULL},
+{"uparrow", "↑", NULL},
+{"updownarrow", "↕", NULL},
+{"upharpoonleft", "↿", NULL},
+{"upharpoonright", "↾", NULL},
+{"uplus", "⊎", NULL},
+{"upsi", "υ", NULL},
+{"upsih", "ϒ", "ϒ"},
+{"upsilon", "υ", "υ"},
+{"upuparrows", "⇈", NULL},
+{"urcorn", "⌝", NULL},
+{"urcorner", "⌝", NULL},
+{"urcrop", "⌎", NULL},
+{"uring", "ů", NULL},
+{"urtri", "◹", NULL},
+{"uscr", "𝓊", NULL},
+{"utdot", "⋰", NULL},
+{"utilde", "ũ", NULL},
+{"utri", "▵", NULL},
+{"utrif", "▴", NULL},
+{"uuarr", "⇈", NULL},
+{"uuml", "ü", "ü"},
+{"uwangle", "⦧", NULL},
+{"vArr", "⇕", NULL},
+{"vBar", "⫨", NULL},
+{"vBarv", "⫩", NULL},
+{"vDash", "⊨", NULL},
+{"vangrt", "⦜", NULL},
+{"varepsilon", "ϵ", NULL},
+{"varkappa", "ϰ", NULL},
+{"varnothing", "∅", NULL},
+{"varphi", "ϕ", NULL},
+{"varpi", "ϖ", NULL},
+{"varpropto", "∝", NULL},
+{"varr", "↕", NULL},
+{"varrho", "ϱ", NULL},
+{"varsigma", "ς", NULL},
+{"varsubsetneq", "⊊︀", NULL},
+{"varsubsetneqq", "⫋︀", NULL},
+{"varsupsetneq", "⊋︀", NULL},
+{"varsupsetneqq", "⫌︀", NULL},
+{"vartheta", "ϑ", NULL},
+{"vartriangleleft", "⊲", NULL},
+{"vartriangleright", "⊳", NULL},
+{"vcy", "в", NULL},
+{"vdash", "⊢", NULL},
+{"vee", "∨", NULL},
+{"veebar", "⊻", NULL},
+{"veeeq", "≚", NULL},
+{"vellip", "⋮", NULL},
+{"verbar", "|", NULL},
+{"vert", "|", NULL},
+{"vfr", "𝔳", NULL},
+{"vltri", "⊲", NULL},
+{"vnsub", "⊂⃒", NULL},
+{"vnsup", "⊃⃒", NULL},
+{"vopf", "𝕧", NULL},
+{"vprop", "∝", NULL},
+{"vrtri", "⊳", NULL},
+{"vscr", "𝓋", NULL},
+{"vsubnE", "⫋︀", NULL},
+{"vsubne", "⊊︀", NULL},
+{"vsupnE", "⫌︀", NULL},
+{"vsupne", "⊋︀", NULL},
+{"vzigzag", "⦚", NULL},
+{"wcirc", "ŵ", NULL},
+{"wedbar", "⩟", NULL},
+{"wedge", "∧", NULL},
+{"wedgeq", "≙", NULL},
+{"weierp", "℘", "℘"},
+{"wfr", "𝔴", NULL},
+{"wopf", "𝕨", NULL},
+{"wp", "℘", NULL},
+{"wr", "≀", NULL},
+{"wreath", "≀", NULL},
+{"wscr", "𝓌", NULL},
+{"xcap", "⋂", NULL},
+{"xcirc", "◯", NULL},
+{"xcup", "⋃", NULL},
+{"xdtri", "▽", NULL},
+{"xfr", "𝔵", NULL},
+{"xhArr", "⟺", NULL},
+{"xharr", "⟷", NULL},
+{"xi", "ξ", "ξ"},
+{"xlArr", "⟸", NULL},
+{"xlarr", "⟵", NULL},
+{"xmap", "⟼", NULL},
+{"xnis", "⋻", NULL},
+{"xodot", "⨀", NULL},
+{"xopf", "𝕩", NULL},
+{"xoplus", "⨁", NULL},
+{"xotime", "⨂", NULL},
+{"xrArr", "⟹", NULL},
+{"xrarr", "⟶", NULL},
+{"xscr", "𝓍", NULL},
+{"xsqcup", "⨆", NULL},
+{"xuplus", "⨄", NULL},
+{"xutri", "△", NULL},
+{"xvee", "⋁", NULL},
+{"xwedge", "⋀", NULL},
+{"yacute", "ý", "ý"},
+{"yacy", "я", NULL},
+{"ycirc", "ŷ", NULL},
+{"ycy", "ы", NULL},
+{"yen", "¥", "¥"},
+{"yfr", "𝔶", NULL},
+{"yicy", "ї", NULL},
+{"yopf", "𝕪", NULL},
+{"yscr", "𝓎", NULL},
+{"yucy", "ю", NULL},
+{"yuml", "ÿ", "ÿ"},
+{"zacute", "ź", NULL},
+{"zcaron", "ž", NULL},
+{"zcy", "з", NULL},
+{"zdot", "ż", NULL},
+{"zeetrf", "ℨ", NULL},
+{"zeta", "ζ", "ζ"},
+{"zfr", "𝔷", NULL},
+{"zhcy", "ж", NULL},
+{"zigrarr", "⇝", NULL},
+{"zopf", "𝕫", NULL},
+{"zscr", "𝓏", NULL},
+{"zwj", "‍", "‍"},
+{"zwnj", "‌", "‌"},
+};
+#endif /* HTML_CHARREFS_H */
diff --git a/src/html_common.hh b/src/html_common.hh
index 147807b3..68ed0d08 100644
--- a/src/html_common.hh
+++ b/src/html_common.hh
@@ -156,8 +156,7 @@ public: //BUG: for now everything is public
char *content_type, *charset;
bool stop_parser;
- size_t CurrTagOfs;
- size_t OldTagOfs, OldTagLine;
+ size_t CurrOfs, OldOfs, OldLine;
DilloHtmlDocumentType DocType; /* as given by DOCTYPE tag */
float DocTypeVersion; /* HTML or XHTML version number */
@@ -211,7 +210,7 @@ public:
void bugMessage(const char *format, ... );
void connectSignals(dw::core::Widget *dw);
void write(char *Buf, int BufSize, int Eof);
- int getCurTagLineNumber();
+ int getCurrLineNumber();
void finishParsing(int ClientKey);
int formNew(DilloHtmlMethod method, const DilloUrl *action,
DilloHtmlEnc enc, const char *charset);
diff --git a/src/image.cc b/src/image.cc
index 9915023a..97270eef 100644
--- a/src/image.cc
+++ b/src/image.cc
@@ -106,9 +106,11 @@ void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
int version, uint_t width, uint_t height,
DilloImgType type)
{
- _MSG("a_Image_set_parms: width=%d height=%d\n", width, height);
+ _MSG("a_Image_set_parms: width=%d height=%d iw=%d ih=%d\n",
+ width, height, Image->width, Image->height);
- bool resize = (Image->width != width || Image->height != height);
+ /* Resize from 0,0 to width,height */
+ bool resize = true;
I2IR(Image)->setBuffer((Imgbuf*)v_imgbuf, resize);
if (!Image->BitVec)
diff --git a/src/jpeg.c b/src/jpeg.c
index 625808fb..5652aa56 100644
--- a/src/jpeg.c
+++ b/src/jpeg.c
@@ -304,6 +304,7 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
(uint_t)jpeg->cinfo.image_width,
(uint_t)jpeg->cinfo.image_height,
type, 1 / 2.2);
+ jpeg->Image = NULL; /* safeguard: may be freed by its owner later */
/* decompression step 4 (see libjpeg.doc) */
jpeg->state = DILLO_JPEG_STARTING;
diff --git a/src/klist.c b/src/klist.c
index 813269a3..e5e695e2 100644
--- a/src/klist.c
+++ b/src/klist.c
@@ -74,7 +74,7 @@ int a_Klist_insert(Klist_t **Klist, void *Data)
a_Klist_get_data((*Klist), (*Klist)->Counter));
Node = dNew(KlistNode_t, 1);
- Node->Key = (*Klist)->Counter;
+ Node->Key = (*Klist)->Counter;
Node->Data = Data;
dList_insert_sorted((*Klist)->List, Node, Klist_node_by_node_cmp);
return (*Klist)->Counter;
diff --git a/src/menu.cc b/src/menu.cc
index b93106e1..e86c3a06 100644
--- a/src/menu.cc
+++ b/src/menu.cc
@@ -232,17 +232,31 @@ static void Menu_stylesheet_cb(Fl_Widget*, void *vUrl)
}
}
+static void Menu_bugmeter_validate(const char *validator_url)
+{
+ if (popup_url &&
+ dStrAsciiCasecmp(URL_SCHEME(popup_url), "dpi")) {
+ const char *popup_str = URL_STR(popup_url),
+ *ptr = strrchr(popup_str, '#');
+ char *no_fragment = ptr ? dStrndup(popup_str, ptr - popup_str)
+ : dStrdup(popup_str);
+ char *encoded = a_Url_encode_hex_str(no_fragment);
+ Dstr *dstr = dStr_sized_new(128);
+
+ dStr_sprintf(dstr, validator_url, encoded);
+ a_UIcmd_open_urlstr(popup_bw, dstr->str);
+ dStr_free(dstr, 1);
+ dFree(encoded);
+ dFree(no_fragment);
+ }
+}
+
/*
* Validate URL with the W3C
*/
static void Menu_bugmeter_validate_w3c_cb(Fl_Widget*, void*)
{
- Dstr *dstr = dStr_sized_new(128);
-
- dStr_sprintf(dstr, "http://validator.w3.org/check?uri=%s",
- URL_STR(popup_url));
- a_UIcmd_open_urlstr(popup_bw, dstr->str);
- dStr_free(dstr, 1);
+ Menu_bugmeter_validate("http://validator.w3.org/check?uri=%s");
}
/*
@@ -250,13 +264,8 @@ static void Menu_bugmeter_validate_w3c_cb(Fl_Widget*, void*)
*/
static void Menu_bugmeter_validate_wdg_cb(Fl_Widget*, void*)
{
- Dstr *dstr = dStr_sized_new(128);
-
- dStr_sprintf(dstr,
- "http://www.htmlhelp.org/cgi-bin/validate.cgi?url=%s&warnings=yes",
- URL_STR(popup_url));
- a_UIcmd_open_urlstr(popup_bw, dstr->str);
- dStr_free(dstr, 1);
+ Menu_bugmeter_validate(
+ "http://www.htmlhelp.org/cgi-bin/validate.cgi?url=%s&warnings=yes");
}
/*
diff --git a/src/paths.cc b/src/paths.cc
index 32478728..bb233de8 100644
--- a/src/paths.cc
+++ b/src/paths.cc
@@ -38,16 +38,16 @@ void Paths::init(void)
oldWorkingDir = dGetcwd();
rc = chdir("/tmp");
if (rc == -1) {
- MSG("paths: error changing directory to /tmp: %s\n",
+ MSG("paths: Error changing directory to /tmp: %s\n",
dStrerror(errno));
}
path = dStrconcat(dGethomedir(), "/.dillo", NULL);
if (stat(path, &st) == -1) {
if (errno == ENOENT) {
- MSG("paths: creating directory %s.\n", path);
+ MSG("paths: Creating directory '%s/'\n", path);
if (mkdir(path, 0700) < 0) {
- MSG("paths: error creating directory %s: %s\n",
+ MSG("paths: Error creating directory %s: %s\n",
path, dStrerror(errno));
}
} else {
diff --git a/src/png.c b/src/png.c
index 4f5da1c2..652e861e 100644
--- a/src/png.c
+++ b/src/png.c
@@ -62,6 +62,7 @@ typedef struct {
DilloImage *Image; /* Image meta data */
DilloUrl *url; /* Primary Key for the dicache */
int version; /* Secondary Key for the dicache */
+ int bgcolor; /* Parent widget background color */
png_uint_32 width; /* png image width */
png_uint_32 height; /* png image height */
@@ -102,8 +103,8 @@ void Png_error_handling(png_structp png_ptr, png_const_charp msg)
{
DilloPng *png;
- MSG("Png_error_handling: %s\n", msg);
png = png_get_error_ptr(png_ptr);
+ MSG("Png_error_handling: %s: %s\n", URL_STR(png->url), msg);
png->error = 1;
png->state = IS_finished;
@@ -204,6 +205,7 @@ Png_datainfo_callback(png_structp png_ptr, png_infop info_ptr)
a_Dicache_set_parms(png->url, png->version, png->Image,
(uint_t)png->width, (uint_t)png->height,
DILLO_IMG_TYPE_RGB, file_gamma);
+ png->Image = NULL; /* safeguard: hereafter it may be freed by its owner */
}
static void
@@ -244,9 +246,9 @@ static void
/* TODO: maybe change prefs.bg_color to `a_Dw_widget_get_bg_color`,
* when background colors are correctly implementated */
- bg_blue = (png->Image->bg_color) & 0xFF;
- bg_green = (png->Image->bg_color>>8) & 0xFF;
- bg_red = (png->Image->bg_color>>16) & 0xFF;
+ bg_blue = (png->bgcolor) & 0xFF;
+ bg_green = (png->bgcolor>>8) & 0xFF;
+ bg_red = (png->bgcolor>>16) & 0xFF;
for (i = 0; i < png->width; i++) {
a = *(data+3);
@@ -433,6 +435,7 @@ void *a_Png_new(DilloImage *Image, DilloUrl *url, int version)
png->Image = Image;
png->url = url;
png->version = version;
+ png->bgcolor = Image->bg_color;
png->error = 0;
png->ipbuf = NULL;
png->ipbufstart = 0;
diff --git a/src/prefs.c b/src/prefs.c
index fbd17f33..abcbfcd0 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -63,10 +63,13 @@ void a_Prefs_init(void)
prefs.http_language = NULL;
prefs.http_proxy = NULL;
prefs.http_max_conns = 6;
+ prefs.http_persistent_conns = FALSE;
prefs.http_proxyuser = NULL;
prefs.http_referer = dStrdup(PREFS_HTTP_REFERER);
prefs.http_user_agent = dStrdup(PREFS_HTTP_USER_AGENT);
prefs.limit_text_width = FALSE;
+ prefs.adjust_min_width = FALSE;
+ prefs.adjust_table_min_width = TRUE;
prefs.load_images=TRUE;
prefs.load_background_images=FALSE;
prefs.load_stylesheets=TRUE;
diff --git a/src/prefs.h b/src/prefs.h
index bb97651e..ac52786e 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -66,6 +66,8 @@ typedef struct {
int panel_size;
bool_t small_icons;
bool_t limit_text_width;
+ bool_t adjust_min_width;
+ bool_t adjust_table_min_width;
bool_t w3c_plus_heuristics;
bool_t focus_new_tab;
double font_factor;
@@ -91,6 +93,7 @@ typedef struct {
bool_t load_background_images;
bool_t load_stylesheets;
bool_t parse_embedded_css;
+ bool_t http_persistent_conns;
int32_t buffered_drawing;
char *font_serif;
char *font_sans_serif;
diff --git a/src/prefsparser.cc b/src/prefsparser.cc
index 81b097e7..d54d017b 100644
--- a/src/prefsparser.cc
+++ b/src/prefsparser.cc
@@ -73,11 +73,14 @@ int PrefsParser::parseOption(char *name, char *value)
{ "home", &prefs.home, PREFS_URL },
{ "http_language", &prefs.http_language, PREFS_STRING },
{ "http_max_conns", &prefs.http_max_conns, PREFS_INT32 },
+ { "http_persistent_conns", &prefs.http_persistent_conns, PREFS_BOOL },
{ "http_proxy", &prefs.http_proxy, PREFS_URL },
{ "http_proxyuser", &prefs.http_proxyuser, PREFS_STRING },
{ "http_referer", &prefs.http_referer, PREFS_STRING },
{ "http_user_agent", &prefs.http_user_agent, PREFS_STRING },
{ "limit_text_width", &prefs.limit_text_width, PREFS_BOOL },
+ { "adjust_min_width", &prefs.adjust_min_width, PREFS_BOOL },
+ { "adjust_table_min_width", &prefs.adjust_table_min_width, PREFS_BOOL },
{ "load_images", &prefs.load_images, PREFS_BOOL },
{ "load_background_images", &prefs.load_background_images, PREFS_BOOL },
{ "load_stylesheets", &prefs.load_stylesheets, PREFS_BOOL },
diff --git a/src/styleengine.cc b/src/styleengine.cc
index e07ab3d5..98b02b69 100644
--- a/src/styleengine.cc
+++ b/src/styleengine.cc
@@ -71,6 +71,7 @@ StyleEngine::StyleEngine (dw::core::Layout *layout,
this->pageUrl = pageUrl ? a_Url_dup(pageUrl) : NULL;
this->baseUrl = baseUrl ? a_Url_dup(baseUrl) : NULL;
importDepth = 0;
+ dpmm = layout->dpiX () / 25.4; /* assume dpiX == dpiY */
stackPush ();
Node *n = stack->getLastRef ();
@@ -112,7 +113,7 @@ StyleEngine::~StyleEngine () {
void StyleEngine::stackPush () {
static const Node emptyNode = {
- NULL, NULL, NULL, NULL, NULL, NULL, false, NULL
+ NULL, NULL, NULL, NULL, NULL, NULL, false, false, NULL
};
stack->setSize (stack->size () + 1, emptyNode);
@@ -145,6 +146,8 @@ void StyleEngine::startElement (int element, BrowserWindow *bw) {
dn->element = element;
n->doctreeNode = dn;
+ if (stack->size () > 1)
+ n->displayNone = stack->getRef (stack->size () - 2)->displayNone;
}
void StyleEngine::startElement (const char *tagname, BrowserWindow *bw) {
@@ -152,7 +155,7 @@ void StyleEngine::startElement (const char *tagname, BrowserWindow *bw) {
}
void StyleEngine::setId (const char *id) {
- DoctreeNode *dn = doctree->top ();
+ DoctreeNode *dn = doctree->top ();
assert (dn->id == NULL);
dn->id = dStrdup (id);
}
@@ -364,8 +367,8 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
DilloUrl *imgUrl = NULL;
/* Determine font first so it can be used to resolve relative lengths. */
- for (int i = 0; i < props->size (); i++) {
- CssProperty *p = props->getRef (i);
+ for (int j = 0; j < props->size (); j++) {
+ CssProperty *p = props->getRef (j);
switch (p->name) {
case CSS_PROPERTY_FONT_FAMILY:
@@ -512,8 +515,8 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
attrs->font = Font::create (layout, &fontAttrs);
- for (int i = 0; i < props->size (); i++) {
- CssProperty *p = props->getRef (i);
+ for (int j = 0; j < props->size (); j++) {
+ CssProperty *p = props->getRef (j);
switch (p->name) {
/* \todo missing cases */
@@ -588,6 +591,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;
@@ -596,6 +605,14 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
break;
case CSS_PROPERTY_DISPLAY:
attrs->display = (DisplayType) p->value.intVal;
+ if (attrs->display == DISPLAY_NONE)
+ stack->getRef (i)->displayNone = true;
+ 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"
@@ -636,6 +653,9 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
if (attrs->margin.top < 0) // \todo fix negative margins in dw/*
attrs->margin.top = 0;
break;
+ case CSS_PROPERTY_OVERFLOW:
+ attrs->overflow = (Overflow) p->value.intVal;
+ break;
case CSS_PROPERTY_PADDING_TOP:
computeValue (&attrs->padding.top, p->value.intVal, attrs->font);
break;
@@ -648,6 +668,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 +686,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;
@@ -687,6 +716,18 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
else if (attrs->wordSpacing < -1000)
attrs->wordSpacing = -1000;
break;
+ case CSS_PROPERTY_MIN_WIDTH:
+ computeLength (&attrs->minWidth, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_MAX_WIDTH:
+ computeLength (&attrs->maxWidth, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_MIN_HEIGHT:
+ computeLength (&attrs->minHeight, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_MAX_HEIGHT:
+ computeLength (&attrs->maxHeight, p->value.intVal, attrs->font);
+ break;
case PROPERTY_X_LINK:
attrs->x_link = p->value.intVal;
break;
@@ -709,7 +750,7 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
}
if (imgUrl && prefs.load_background_images &&
- attrs->display != DISPLAY_NONE &&
+ !stack->getRef (i)->displayNone &&
!(URL_FLAGS(pageUrl) & URL_SpamSafe))
{
attrs->backgroundImage = StyleImage::create();
@@ -741,11 +782,6 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
* \brief Resolve relative lengths to absolute values.
*/
bool StyleEngine::computeValue (int *dest, CssLength value, Font *font) {
- static float dpmm;
-
- if (dpmm == 0.0)
- dpmm = layout->dpiX () / 25.4; /* assume dpiX == dpiY */
-
switch (CSS_LENGTH_TYPE (value)) {
case CSS_LENGTH_TYPE_PX:
*dest = (int) CSS_LENGTH_VALUE (value);
diff --git a/src/styleengine.hh b/src/styleengine.hh
index 41f892d7..db3e3b85 100644
--- a/src/styleengine.hh
+++ b/src/styleengine.hh
@@ -27,6 +27,7 @@ class StyleEngine {
dw::core::style::Style *wordStyle;
dw::core::style::Style *backgroundStyle;
bool inheritBackgroundColor;
+ bool displayNone;
DoctreeNode *doctreeNode;
};
@@ -35,6 +36,7 @@ class StyleEngine {
CssContext *cssContext;
Doctree *doctree;
int importDepth;
+ float dpmm;
DilloUrl *pageUrl, *baseUrl;
void stackPush ();
diff --git a/src/table.cc b/src/table.cc
index a3002ebf..188becbc 100644
--- a/src/table.cc
+++ b/src/table.cc
@@ -15,6 +15,7 @@
#include "dw/style.hh"
#include "dw/textblock.hh"
#include "dw/table.hh"
+#include "dw/simpletablecell.hh"
#include "prefs.h"
#include "msg.h"
@@ -48,13 +49,13 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellspacing"))) {
cellspacing = strtol (attrbuf, NULL, 10);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<table> cellspacing attribute is obsolete.\n");
+ BUG_MSG("<table> cellspacing attribute is obsolete.");
}
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellpadding"))) {
cellpadding = strtol (attrbuf, NULL, 10);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<table> cellpadding attribute is obsolete.\n");
+ BUG_MSG("<table> cellpadding attribute is obsolete.");
}
if (border != -1) {
@@ -88,7 +89,7 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
CSS_TYPE_LENGTH_PERCENTAGE,
a_Html_parse_length (html, attrbuf));
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<table> width attribute is obsolete.\n");
+ BUG_MSG("<table> width attribute is obsolete.");
}
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) {
@@ -102,7 +103,7 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN,
CSS_TYPE_ENUM, TEXT_ALIGN_CENTER);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<table> align attribute is obsolete.\n");
+ BUG_MSG("<table> align attribute is obsolete.");
}
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
@@ -111,7 +112,7 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
CSS_TYPE_COLOR, bgcolor);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<table> bgcolor attribute is obsolete.\n");
+ BUG_MSG("<table> bgcolor attribute is obsolete.");
}
html->style (); // evaluate now, so we can build non-css hints for the cells
@@ -192,7 +193,7 @@ void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize)
html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
CSS_TYPE_COLOR, bgcolor);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<tr> bgcolor attribute is obsolete.\n");
+ BUG_MSG("<tr> bgcolor attribute is obsolete.");
}
if (a_Html_get_attr (html, tag, tagsize, "align")) {
@@ -379,7 +380,7 @@ static void Html_tag_open_table_cell(DilloHtml *html,
}
if (a_Html_get_attr(html, tag, tagsize, "nowrap")) {
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<t%c> nowrap attribute is obsolete.\n",
+ BUG_MSG("<t%c> nowrap attribute is obsolete.",
(tagsize >=3 && (D_ASCII_TOLOWER(tag[2]) == 'd')) ? 'd' : 'h');
html->styleEngine->setNonCssHint(CSS_PROPERTY_WHITE_SPACE,
CSS_TYPE_ENUM, WHITE_SPACE_NOWRAP);
@@ -392,7 +393,7 @@ static void Html_tag_open_table_cell(DilloHtml *html,
CSS_TYPE_LENGTH_PERCENTAGE,
a_Html_parse_length (html, attrbuf));
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<t%c> width attribute is obsolete.\n",
+ BUG_MSG("<t%c> width attribute is obsolete.",
(tagsize >=3 && (D_ASCII_TOLOWER(tag[2]) == 'd')) ? 'd' : 'h');
}
@@ -404,7 +405,7 @@ static void Html_tag_open_table_cell(DilloHtml *html,
html->styleEngine->setNonCssHint (CSS_PROPERTY_BACKGROUND_COLOR,
CSS_TYPE_COLOR, bgcolor);
if (html->DocType == DT_HTML && html->DocTypeVersion >= 5.0f)
- BUG_MSG("<t%c> bgcolor attribute is obsolete.\n",
+ BUG_MSG("<t%c> bgcolor attribute is obsolete.",
(tagsize >=3 && (D_ASCII_TOLOWER(tag[2]) == 'd')) ? 'd' : 'h');
}
@@ -423,12 +424,12 @@ static void Html_tag_content_table_cell(DilloHtml *html,
switch (S_TOP(html)->table_mode) {
case DILLO_HTML_TABLE_MODE_NONE:
- BUG_MSG("<t%c> outside <table>\n",
+ BUG_MSG("<t%c> outside <table>.",
(tagsize >=3 && (D_ASCII_TOLOWER(tag[2]) == 'd')) ? 'd' : 'h');
return;
case DILLO_HTML_TABLE_MODE_TOP:
- BUG_MSG("<t%c> outside <tr>\n",
+ BUG_MSG("<t%c> outside <tr>.",
(tagsize >=3 && (D_ASCII_TOLOWER(tag[2]) == 'd')) ? 'd' : 'h');
/* a_Dw_table_add_cell takes care that dillo does not crash. */
/* continues */
@@ -445,11 +446,11 @@ static void Html_tag_content_table_cell(DilloHtml *html,
rowspan = MAX(1, strtol (attrbuf, NULL, 10));
if (html->style ()->textAlign
== TEXT_ALIGN_STRING)
- col_tb = new dw::TableCell (
+ col_tb = new AlignedTableCell (
((dw::Table*)S_TOP(html)->table)->getCellRef (),
prefs.limit_text_width);
else
- col_tb = new Textblock (prefs.limit_text_width);
+ col_tb = new SimpleTableCell (prefs.limit_text_width);
if (html->style()->borderCollapse == BORDER_MODEL_COLLAPSE){
Html_set_collapsing_border_model(html, col_tb);
diff --git a/src/ui.cc b/src/ui.cc
index d3bc7ff6..a5785576 100644
--- a/src/ui.cc
+++ b/src/ui.cc
@@ -816,7 +816,7 @@ void UI::set_location(const char *str)
{
if (!str) str = "";
Location->value(str);
- Location->position(strlen(str));
+ Location->position((Fl::focus() == Location) ? strlen(str) : 0);
}
/*
diff --git a/src/uicmd.cc b/src/uicmd.cc
index e1100219..5225be75 100644
--- a/src/uicmd.cc
+++ b/src/uicmd.cc
@@ -71,6 +71,7 @@ static const char *save_dir = "";
static BrowserWindow *UIcmd_tab_new(CustTabs *tabs, UI *old_ui, int focus);
static void close_tab_btn_cb (Fl_Widget *w, void *cb_data);
static char *UIcmd_make_search_str(const char *str);
+static void UIcmd_set_window_labels(Fl_Window *win, const char *str);
//----------------------------------------------------------------------------
@@ -433,7 +434,7 @@ void CustTabs::switch_tab(CustTabButton *cbtn)
// Update window title
if ((bw = a_UIcmd_get_bw_by_widget(cbtn->ui()))) {
const char *title = (cbtn->ui())->label();
- cbtn->window()->copy_label(title ? title : "");
+ UIcmd_set_window_labels(cbtn->window(), title ? title : "");
}
// Update focus priority
increase_focus_counter();
@@ -568,6 +569,18 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh,
}
/*
+ * Set the window name and icon name.
+ */
+static void UIcmd_set_window_labels(Fl_Window *win, const char *str)
+{
+ const char *copy;
+
+ win->Fl_Widget::copy_label(str);
+ copy = win->label();
+ win->label(copy, copy);
+}
+
+/*
* Create a new Tab button, UI and its associated BrowserWindow data
* structure.
*/
@@ -606,7 +619,7 @@ static BrowserWindow *UIcmd_tab_new(CustTabs *tabs, UI *old_ui, int focus)
// Clear the window title
if (focus)
- new_ui->window()->copy_label(new_ui->label());
+ UIcmd_set_window_labels(new_ui->window(), new_ui->label());
// WORKAROUND: see findbar_toggle()
new_ui->findbar_toggle(0);
@@ -1413,7 +1426,7 @@ void a_UIcmd_set_page_title(BrowserWindow *bw, const char *label)
if (a_UIcmd_get_bw_by_widget(BW2UI(bw)->tabs()->wizard()->value()) == bw) {
// This is the focused bw, set window title
- BW2UI(bw)->window()->copy_label(title);
+ UIcmd_set_window_labels(BW2UI(bw)->window(), title);
}
}
diff --git a/src/url.c b/src/url.c
index 9d3e14b2..c1a8396d 100644
--- a/src/url.c
+++ b/src/url.c
@@ -366,23 +366,23 @@ DilloUrl* a_Url_new(const char *url_str, const char *base_url)
dReturn_val_if_fail (url_str != NULL, NULL);
- /* Count illegal characters (0x00-0x1F, 0x7F and space) */
+ /* Count illegal characters (0x00-0x1F, 0x7F-0xFF and space) */
n_ic = n_ic_spc = 0;
for (p = (char*)url_str; *p; p++) {
n_ic_spc += (*p == ' ') ? 1 : 0;
- n_ic += (*p != ' ' && *p > 0x1F && *p != 0x7F) ? 0 : 1;
+ n_ic += (*p != ' ' && *p > 0x1F && *p < 0x7F) ? 0 : 1;
}
if (n_ic) {
/* Encode illegal characters (they could also be stripped).
* There's no standard for illegal chars; we chose to encode. */
p = str1 = dNew(char, strlen(url_str) + 2*n_ic + 1);
for (i = 0; url_str[i]; ++i)
- if (url_str[i] > 0x1F && url_str[i] != 0x7F && url_str[i] != ' ')
+ if (url_str[i] > 0x1F && url_str[i] < 0x7F && url_str[i] != ' ')
*p++ = url_str[i];
- else {
- *p++ = '%';
- *p++ = HEX[(url_str[i] >> 4) & 15];
- *p++ = HEX[url_str[i] & 15];
+ else {
+ *p++ = '%';
+ *p++ = HEX[(url_str[i] >> 4) & 15];
+ *p++ = HEX[url_str[i] & 15];
}
*p = 0;
urlstr = str1;
@@ -509,7 +509,7 @@ void a_Url_set_ismap_coords(DilloUrl *u, char *coord_str)
if (!u->ismap_url_len) {
/* Save base-url length (without coords) */
- u->ismap_url_len = URL_STR_(u) ? u->url_string->len : 0;
+ u->ismap_url_len = URL_STR_(u) ? u->url_string->len : 0;
a_Url_set_flags(u, URL_FLAGS(u) | URL_Ismap);
}
if (u->url_string) {
@@ -611,7 +611,7 @@ char *a_Url_encode_hex_str(const char *str)
/*
* RFC-3986 suggests this stripping when "importing" URLs from other media.
* Strip: "URL:", enclosing < >, and embedded whitespace.
- * (We also strip illegal chars: 00-1F and 7F)
+ * (We also strip illegal chars: 00-1F and 7F-FF)
*/
char *a_Url_string_strip_delimiters(const char *str)
{
@@ -626,7 +626,7 @@ char *a_Url_string_strip_delimiters(const char *str)
text++;
for (p = new_str; *text; text++)
- if (*text > 0x1F && *text != 0x7F && *text != ' ')
+ if (*text > 0x1F && *text < 0x7F && *text != ' ')
*p++ = *text;
if (p > new_str && p[-1] == '>')
--p;
@@ -688,14 +688,17 @@ static uint_t Url_host_public_internal_dots(const char *host)
if (tld_len > 0) {
/* These TLDs were chosen by examining the current publicsuffix list
- * in February 2014 and picking out those where it was simplest for
+ * in October 2014 and picking out those where it was simplest for
* them to describe the situation by beginning with a "*.[tld]" rule
* or every rule was "[something].[tld]".
+ *
+ * TODO: Consider the old publicsuffix code again. This TLD list has
+ * shrunk and shrunk over the years, and has become a poorer and
+ * poorer approximation of administrative boundaries.
*/
- const char *const tlds[] = {"bd","bn","ck","cy","er","et","fj","fk",
+ const char *const tlds[] = {"bd","bn","ck","cy","er","fj","fk",
"gu","il","jm","ke","kh","kw","mm","mz",
- "ni","np","nz","pg","tr","uk","ye","za",
- "zm","zw"};
+ "ni","np","pg","ye","za","zm","zw"};
uint_t i, tld_num = sizeof(tlds) / sizeof(tlds[0]);
for (i = 0; i < tld_num; i++) {
diff --git a/src/url.h b/src/url.h
index bb20d789..ef532f76 100644
--- a/src/url.h
+++ b/src/url.h
@@ -13,15 +13,6 @@
#include "../dlib/dlib.h"
-#define DILLO_URL_HTTP_PORT 80
-#define DILLO_URL_HTTPS_PORT 443
-#define DILLO_URL_FTP_PORT 21
-#define DILLO_URL_MAILTO_PORT 25
-#define DILLO_URL_NEWS_PORT 119
-#define DILLO_URL_TELNET_PORT 23
-#define DILLO_URL_GOPHER_PORT 70
-
-
/*
* Values for DilloUrl->flags.
* Specifies which which action to perform with an URL.
diff --git a/src/web.cc b/src/web.cc
index b835610c..a175ddb9 100644
--- a/src/web.cc
+++ b/src/web.cc
@@ -128,7 +128,7 @@ DilloWeb* a_Web_new(BrowserWindow *bw, const DilloUrl *url,
web->flags = 0;
web->Image = NULL;
web->filename = NULL;
- web->stream = NULL;
+ web->stream = NULL;
web->SavedBytes = 0;
web->bgColor = 0x000000; /* Dummy value will be overwritten
* in a_Web_dispatch_by_type. */
diff --git a/test/Makefile.am b/test/Makefile.am
index 156c8667..3b474466 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 \
@@ -14,6 +17,7 @@ noinst_PROGRAMS = \
dw-images-scaled \
dw-images-scaled2 \
dw-lists \
+ dw-simple-container-test \
dw-table-aligned \
dw-table \
dw-border-test \
@@ -34,7 +38,7 @@ dw_anchors_test_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_example_SOURCES = dw_example.cc
dw_example_LDADD = \
@@ -42,7 +46,7 @@ dw_example_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_find_test_SOURCES = dw_find_test.cc
dw_find_test_LDADD = \
@@ -50,7 +54,15 @@ dw_find_test_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_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@ @LIBX11_LIBS@
dw_links_SOURCES = dw_links.cc
dw_links_LDADD = \
@@ -58,7 +70,7 @@ dw_links_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_links2_SOURCES = dw_links2.cc
dw_links2_LDADD = \
@@ -66,7 +78,7 @@ dw_links2_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_image_background_SOURCES = dw_image_background.cc
dw_image_background_LDADD = \
@@ -74,7 +86,7 @@ dw_image_background_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_images_simple_SOURCES = dw_images_simple.cc
dw_images_simple_LDADD = \
@@ -82,7 +94,7 @@ dw_images_simple_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_images_scaled_SOURCES = dw_images_scaled.cc
dw_images_scaled_LDADD = \
@@ -90,7 +102,7 @@ dw_images_scaled_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_images_scaled2_SOURCES = dw_images_scaled2.cc
dw_images_scaled2_LDADD = \
@@ -98,7 +110,7 @@ dw_images_scaled2_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_lists_SOURCES = dw_lists.cc
dw_lists_LDADD = \
@@ -106,6 +118,17 @@ dw_lists_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
+
+dw_simple_container_test_SOURCES = \
+ dw_simple_container.hh \
+ dw_simple_container.cc \
+ dw_simple_container_test.cc
+dw_simple_container_test_LDADD = \
+ $(top_builddir)/dw/libDw-widgets.a \
+ $(top_builddir)/dw/libDw-fltk.a \
+ $(top_builddir)/dw/libDw-core.a \
+ $(top_builddir)/lout/liblout.a \
@LIBFLTK_LIBS@
dw_table_aligned_SOURCES = dw_table_aligned.cc
@@ -114,7 +137,7 @@ dw_table_aligned_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_table_SOURCES = dw_table.cc
dw_table_LDADD = \
@@ -122,7 +145,7 @@ dw_table_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_border_test_SOURCES = dw_border_test.cc
dw_border_test_LDADD = \
@@ -130,7 +153,7 @@ dw_border_test_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_imgbuf_mem_test_SOURCES = dw_imgbuf_mem_test.cc
@@ -139,7 +162,7 @@ dw_imgbuf_mem_test_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_resource_test_SOURCES = dw_resource_test.cc
dw_resource_test_LDADD = \
@@ -147,7 +170,7 @@ dw_resource_test_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
dw_ui_test_SOURCES = \
dw_ui_test.cc \
@@ -158,7 +181,7 @@ dw_ui_test_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
shapes_SOURCES = shapes.cc
shapes_LDADD = \
@@ -180,7 +203,7 @@ liang_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
trie_SOURCES = trie.cc
@@ -189,7 +212,7 @@ trie_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
notsosimplevector_SOURCES = notsosimplevector.cc
@@ -199,4 +222,4 @@ unicode_test_SOURCES = unicode_test.cc
unicode_test_LDADD = \
$(top_builddir)/lout/liblout.a \
- @LIBFLTK_LIBS@
+ @LIBFLTK_LIBS@ @LIBX11_LIBS@
diff --git a/test/containers.cc b/test/containers.cc
index 993a299d..af317d7e 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,48 @@ 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));
+}
+
+void testStackAsQueue ()
+{
+ puts ("--- testStackAsQueue ---");
+
+ Stack<Integer> s (true);
+
+ for (int i = 1; i <= 10; i++)
+ s.pushUnder (new Integer (i));
+
+ while (s.size () > 0) {
+ Integer *i = s.getTop ();
+ printf ("%d\n", i->getValue ());
+ s.pop ();
+ }
+}
+
int main (int argc, char *argv[])
{
testHashSet ();
testHashTable ();
testVector1 ();
testVector2 ();
+ testVector3 ();
+ testStackAsQueue ();
return 0;
}
diff --git a/test/cookies.c b/test/cookies.c
index 40661650..ff744c97 100644
--- a/test/cookies.c
+++ b/test/cookies.c
@@ -880,17 +880,17 @@ int main()
path();
/* LEADING/TRAILING DOTS AND A LITTLE PUBLIC SUFFIX */
- a_Cookies_set("name=val; domain=co.uk", "www.co.uk", "/", NULL);
- expect(__LINE__, "", "http", "www.co.uk", "/");
+ a_Cookies_set("name=val; domain=co.il", "www.co.il", "/", NULL);
+ expect(__LINE__, "", "http", "www.co.il", "/");
- a_Cookies_set("name=val; domain=.co.uk", "www.co.uk", "/", NULL);
- expect(__LINE__, "", "http", "www.co.uk", "/");
+ a_Cookies_set("name=val; domain=.co.il", "www.co.il", "/", NULL);
+ expect(__LINE__, "", "http", "www.co.il", "/");
- a_Cookies_set("name=val; domain=co.uk.", "www.co.uk.", "/", NULL);
- expect(__LINE__, "", "http", "www.co.uk.", "/");
+ a_Cookies_set("name=val; domain=co.il.", "www.co.il.", "/", NULL);
+ expect(__LINE__, "", "http", "www.co.il.", "/");
- a_Cookies_set("name=val; domain=.co.uk.", "www.co.uk.", "/", NULL);
- expect(__LINE__, "", "http", ".www.co.uk.", "/");
+ a_Cookies_set("name=val; domain=.co.il.", "www.co.il.", "/", NULL);
+ expect(__LINE__, "", "http", ".www.co.il.", "/");
a_Cookies_set("name=val; domain=co.org", "www.co.org", "/", NULL);
expect(__LINE__, "Cookie: name=val\r\n", "http", "www.co.org", "/");
diff --git a/test/dw_float_test.cc b/test/dw_float_test.cc
new file mode 100644
index 00000000..807002b2
--- /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/dw_simple_container.cc b/test/dw_simple_container.cc
new file mode 100644
index 00000000..e19511a6
--- /dev/null
+++ b/test/dw_simple_container.cc
@@ -0,0 +1,241 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2014 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 <math.h>
+
+#include "dw_simple_container.hh"
+
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace lout::misc;
+
+namespace dw {
+
+int SimpleContainer::CLASS_ID = -1;
+
+// ----------------------------------------------------------------------
+
+SimpleContainer::SimpleContainerIterator::SimpleContainerIterator
+ (SimpleContainer *simpleContainer, Content::Type mask, bool atEnd) :
+ Iterator (simpleContainer, mask, atEnd)
+{
+ content.type = atEnd ? Content::END : Content::START;
+}
+
+lout::object::Object *SimpleContainer::SimpleContainerIterator::clone ()
+{
+ SimpleContainerIterator *sci =
+ new SimpleContainerIterator ((SimpleContainer*)getWidget(),
+ getMask(), false);
+ sci->content = content;
+ return sci;
+}
+
+int SimpleContainer::SimpleContainerIterator::index ()
+{
+ switch (content.type) {
+ case Content::START:
+ return 0;
+ case Content::WIDGET_IN_FLOW:
+ return 1;
+ case Content::END:
+ return 2;
+ default:
+ assertNotReached ();
+ return 0;
+ }
+}
+
+int SimpleContainer::SimpleContainerIterator::compareTo
+ (lout::object::Comparable *other)
+{
+ return index () - ((SimpleContainerIterator*)other)->index ();
+}
+
+bool SimpleContainer::SimpleContainerIterator::next ()
+{
+ SimpleContainer *simpleContainer = (SimpleContainer*)getWidget();
+
+ if (content.type == Content::END)
+ return false;
+
+ // simple containers only contain widgets:
+ if ((getMask() & Content::WIDGET_IN_FLOW) == 0) {
+ content.type = Content::END;
+ return false;
+ }
+
+ if (content.type == Content::START) {
+ if (simpleContainer->child != NULL) {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = simpleContainer->child;
+ return true;
+ } else {
+ content.type = Content::END;
+ return false;
+ }
+ } else /* if (content.type == Content::WIDGET) */ {
+ content.type = Content::END;
+ return false;
+ }
+}
+
+bool SimpleContainer::SimpleContainerIterator::prev ()
+{
+ SimpleContainer *simpleContainer = (SimpleContainer*)getWidget();
+
+ if (content.type == Content::START)
+ return false;
+
+ // simple containers only contain widgets:
+ if ((getMask() & Content::WIDGET_IN_FLOW) == 0) {
+ content.type = Content::START;
+ return false;
+ }
+
+ if (content.type == Content::END) {
+ if (simpleContainer->child != NULL) {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = simpleContainer->child;
+ return true;
+ } else {
+ content.type = Content::START;
+ return false;
+ }
+ } else /* if (content.type == Content::WIDGET) */ {
+ content.type = Content::START;
+ return false;
+ }
+}
+
+void SimpleContainer::SimpleContainerIterator::highlight (int start,
+ int end,
+ HighlightLayer layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void SimpleContainer::SimpleContainerIterator::unhighlight (int direction,
+ HighlightLayer
+ layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void SimpleContainer::SimpleContainerIterator::getAllocation (int start,
+ int end,
+ Allocation
+ *allocation)
+{
+ /** \bug Not implemented. */
+}
+
+// ----------------------------------------------------------------------
+
+SimpleContainer::SimpleContainer ()
+{
+ registerName ("dw::SimpleContainer", &CLASS_ID);
+ child = NULL;
+}
+
+SimpleContainer::~SimpleContainer ()
+{
+ if (child)
+ delete child;
+}
+
+void SimpleContainer::sizeRequestImpl (Requisition *requisition)
+{
+ Requisition childReq;
+ if (child)
+ child->sizeRequest (&childReq);
+ else
+ childReq.width = childReq.ascent = childReq.descent = 0;
+
+ requisition->width = childReq.width + boxDiffWidth ();
+ requisition->ascent = childReq.ascent + boxOffsetY ();
+ requisition->descent = childReq.descent + boxRestHeight ();
+
+ correctRequisition (requisition, splitHeightPreserveAscent);
+}
+
+
+void SimpleContainer::getExtremesImpl (Extremes *extremes)
+{
+ Extremes childExtr;
+ if (child)
+ child->getExtremes (&childExtr);
+ else
+ childExtr.minWidth = childExtr.maxWidth = 0;
+
+ extremes->minWidth = childExtr.minWidth + boxDiffWidth ();
+ extremes->maxWidth = childExtr.maxWidth + boxDiffWidth ();
+
+ correctExtremes (extremes);
+}
+
+
+void SimpleContainer::sizeAllocateImpl (Allocation *allocation)
+{
+ Allocation childAlloc;
+
+ if (child) {
+ childAlloc.x = allocation->x + boxOffsetX ();
+ childAlloc.y = allocation->y + boxOffsetY ();
+ childAlloc.width = allocation->width - boxDiffWidth ();
+ childAlloc.ascent = allocation->ascent - boxOffsetY ();
+ childAlloc.descent = allocation->descent - boxRestHeight ();
+ child->sizeAllocate (&childAlloc);
+ }
+}
+
+void SimpleContainer::draw (View *view, Rectangle *area)
+{
+ drawWidgetBox (view, area, false);
+ Rectangle childArea;
+ if (child && child->intersects (area, &childArea))
+ child->draw (view, &childArea);
+}
+
+Iterator *SimpleContainer::iterator (Content::Type mask, bool atEnd)
+{
+ return new SimpleContainerIterator (this, mask, atEnd);
+}
+
+void SimpleContainer::removeChild (Widget *child)
+{
+ assert (child == this->child);
+ this->child = NULL;
+
+ queueResize (0, true);
+}
+
+void SimpleContainer::setChild (Widget *child)
+{
+ if (this->child)
+ delete this->child;
+
+ this->child = child;
+ if (this->child)
+ this->child->setParent (this);
+
+ queueResize (0, true);
+}
+
+} // namespace dw
diff --git a/test/dw_simple_container.hh b/test/dw_simple_container.hh
new file mode 100644
index 00000000..fdb67bec
--- /dev/null
+++ b/test/dw_simple_container.hh
@@ -0,0 +1,56 @@
+#ifndef __DW_SIMPLE_CONTAINER_HH__
+#define __DW_SIMPLE_CONTAINER_HH__
+
+#include "dw/core.hh"
+
+namespace dw {
+
+/**
+ * Simple widget used for testing concepts.
+ */
+class SimpleContainer: public core::Widget
+{
+private:
+ class SimpleContainerIterator: public core::Iterator
+ {
+ private:
+ int index ();
+
+ public:
+ SimpleContainerIterator (SimpleContainer *simpleContainer,
+ core::Content::Type mask,
+ bool atEnd);
+
+ lout::object::Object *clone ();
+ int compareTo (lout::object::Comparable *other);
+
+ bool next ();
+ bool prev ();
+ 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);
+ };
+
+ Widget *child;
+
+protected:
+ void sizeRequestImpl (core::Requisition *requisition);
+ void getExtremesImpl (core::Extremes *extremes);
+ void sizeAllocateImpl (core::Allocation *allocation);
+
+public:
+ static int CLASS_ID;
+
+ SimpleContainer ();
+ ~SimpleContainer ();
+
+ void draw (core::View *view, core::Rectangle *area);
+ core::Iterator *iterator (core::Content::Type mask, bool atEnd);
+ void removeChild (Widget *child);
+
+ void setChild (core::Widget *child);
+};
+
+} // namespace dw
+
+#endif // __DW_SIMPLE_CONTAINER_HH__
diff --git a/test/dw_simple_container_test.cc b/test/dw_simple_container_test.cc
new file mode 100644
index 00000000..83d0a77f
--- /dev/null
+++ b/test/dw_simple_container_test.cc
@@ -0,0 +1,114 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2014 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 <FL/Fl_Window.H>
+#include <FL/Fl.H>
+
+#include "../dw/core.hh"
+#include "../dw/fltkcore.hh"
+#include "../dw/fltkviewport.hh"
+#include "dw_simple_container.hh"
+#include "../dw/textblock.hh"
+
+using namespace dw;
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace dw::fltk;
+
+int main(int argc, char **argv)
+{
+ FltkPlatform *platform = new FltkPlatform ();
+ Layout *layout = new Layout (platform);
+
+ Fl_Window *window = new Fl_Window(200, 300, "Dw Example");
+ window->box(FL_NO_BOX);
+ window->begin();
+
+ FltkViewport *viewport = new FltkViewport (0, 0, 200, 300);
+ 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;
+ fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
+ styleAttrs.font = style::Font::create (layout, &fontAttrs);
+
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
+
+ Style *textblockStyle1 = Style::create (&styleAttrs);
+
+ styleAttrs.backgroundColor = NULL;
+ styleAttrs.margin.setVal (0);
+
+ Style *textblockStyle2 = Style::create (&styleAttrs);
+ Style *wordStyle = Style::create (&styleAttrs);
+
+ styleAttrs.borderWidth.setVal (5);
+ styleAttrs.setBorderColor (Color::create (layout, 0x800080));
+ styleAttrs.setBorderStyle (BORDER_DASHED);
+ styleAttrs.padding.setVal (5);
+
+ Style *containerStyle = Style::create (&styleAttrs);
+
+ Textblock *textblock1 = new Textblock (false);
+ textblock1->setStyle (textblockStyle1);
+ layout->setWidget (textblock1);
+
+ SimpleContainer *simpleContainer = new SimpleContainer ();
+ simpleContainer->setStyle (containerStyle);
+ textblock1->addWidget (simpleContainer, containerStyle);
+
+ Textblock *textblock2 = new Textblock (false);
+ textblock2->setStyle (textblockStyle2);
+ simpleContainer->setChild (textblock2);
+
+ const char *words[] = { "This", "is", "only", "a", "short", "paragraph.",
+ NULL };
+
+ for(int j = 0; words[j]; j++) {
+ textblock2->addText(words[j], wordStyle);
+ textblock2->addSpace(wordStyle);
+ }
+
+ textblockStyle1->unref();
+ textblockStyle2->unref();
+ containerStyle->unref();
+ wordStyle->unref();
+
+ textblock1->flush ();
+ textblock2->flush ();
+
+ window->resizable(viewport);
+ window->show();
+ int errorCode = Fl::run();
+
+ delete layout;
+
+ return errorCode;
+}
diff --git a/test/dw_table.cc b/test/dw_table.cc
index 5416d05b..9bec1a09 100644
--- a/test/dw_table.cc
+++ b/test/dw_table.cc
@@ -26,7 +26,6 @@
#include "../dw/fltkcore.hh"
#include "../dw/fltkviewport.hh"
#include "../dw/table.hh"
-#include "../dw/tablecell.hh"
using namespace dw;
using namespace dw::core;
diff --git a/test/dw_table_aligned.cc b/test/dw_table_aligned.cc
index 96cb0602..bef3d521 100644
--- a/test/dw_table_aligned.cc
+++ b/test/dw_table_aligned.cc
@@ -26,7 +26,7 @@
#include "../dw/fltkcore.hh"
#include "../dw/fltkviewport.hh"
#include "../dw/table.hh"
-#include "../dw/tablecell.hh"
+#include "../dw/alignedtablecell.hh"
using namespace dw;
using namespace dw::core;
@@ -87,10 +87,10 @@ int main(int argc, char **argv)
Style *wordStyle = Style::create (&styleAttrs);
- TableCell *ref = NULL;
+ AlignedTableCell *ref = NULL;
for(int i = 0; i < 10; i++) {
//for(int i = 0; i < 1; i++) {
- TableCell *cell = new TableCell (ref, false);
+ AlignedTableCell *cell = new AlignedTableCell (ref, false);
cell->setStyle (cellStyle);
ref = cell;
table->addRow (wordStyle);
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?