aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore1
-rw-r--r--doc/dw-images-and-backgrounds.doc266
-rw-r--r--dw/Makefile.am2
-rw-r--r--dw/core.hh1
-rw-r--r--dw/image.cc10
-rw-r--r--dw/image.hh5
-rw-r--r--dw/imgrenderer.cc48
-rw-r--r--dw/imgrenderer.hh87
-rw-r--r--dw/layout.cc118
-rw-r--r--dw/layout.hh31
-rw-r--r--dw/style.cc400
-rw-r--r--dw/style.hh152
-rw-r--r--dw/textblock.cc191
-rw-r--r--dw/textblock.hh52
-rw-r--r--dw/textblock_linebreaking.cc17
-rw-r--r--dw/widget.cc136
-rw-r--r--dw/widget.hh24
-rw-r--r--src/css.hh10
-rw-r--r--src/cssparser.cc135
-rw-r--r--src/cssparser.hh3
-rw-r--r--src/dicache.c12
-rw-r--r--src/form.cc32
-rw-r--r--src/html.cc153
-rw-r--r--src/html_common.hh16
-rw-r--r--src/image.cc23
-rw-r--r--src/image.hh5
-rw-r--r--src/imgbuf.cc6
-rw-r--r--src/imgbuf.hh2
-rw-r--r--src/plain.cc7
-rw-r--r--src/styleengine.cc148
-rw-r--r--src/styleengine.hh30
-rw-r--r--src/table.cc20
-rw-r--r--src/uicmd.cc3
-rw-r--r--src/web.cc8
-rw-r--r--test/Makefile.am9
-rw-r--r--test/dw_image_background.cc196
36 files changed, 2054 insertions, 305 deletions
diff --git a/.hgignore b/.hgignore
index 2a169cd4..9ab76864 100644
--- a/.hgignore
+++ b/.hgignore
@@ -27,6 +27,7 @@
^test/dw-border-test$
^test/dw-example$
^test/dw-find-test$
+^test/dw-image-background$
^test/dw-images-scaled$
^test/dw-images-scaled2$
^test/dw-images-simple$
diff --git a/doc/dw-images-and-backgrounds.doc b/doc/dw-images-and-backgrounds.doc
index 356652a6..caa08e8f 100644
--- a/doc/dw-images-and-backgrounds.doc
+++ b/doc/dw-images-and-backgrounds.doc
@@ -1,10 +1,11 @@
/** \page dw-images-and-backgrounds Images and Backgrounds in Dw
-<h2>General</h2>
+Image Buffers
+=============
-Representation of the image data is delegated to dw::core::Imgbuf, see
-there for details. Drawing is delegated to dw::core::View
-(dw::core::View::drawImgbuf).
+Representation of the image data is done by dw::core::Imgbuf, see
+there for details. Drawing is done by dw::core::View
+(dw::core::View::drawImage).
Since dw::core::Imgbuf provides memory management based on reference
counting, there may be an 1-to-n relation from image renderers (image
@@ -15,61 +16,73 @@ dw::core::Imgbuf::copyRow) notify all renderers connected to the
buffer.
-<h2>Images</h2>
+Image Renderer
+==============
+
+Generally, there are no restrictions on how to manage
+dw::core::Imgbuf; but to handle image data from web resources, the
+interface dw::core::ImgRenderer should be implemented. It is again
+wrapped by DilloImago (to make access from the C part possible, since
+dw::core::ImgRenderer is written in C++), which is referenced by
+DilloWeb. There are two positions where retrieving image data is
+initiated:
+
+- Html_load_image: for embedded images (implemented by dw::Image,
+ which implements dw::core::ImgRenderer);
+- StyleEngine::apply (search for "case
+ CSS_PROPERTY_BACKGROUND_IMAGE"): for backgrond images; there are
+ some implementations of dw::core::ImgRenderer within the context of
+ dw::core::style::StyleImage.
+
+Both are described in detail below. Notice that the code is quite
+similar; only the implementation of dw::core::ImgRenderer differs.
+
+At this time, dw::core::ImgRenderer has got two methods (see more
+documentation there):
+
+- dw::core::ImgRenderer::setBuffer,
+- dw::core::ImgRenderer::drawRow,
+- dw::core::ImgRenderer::finish, and
+- dw::core::ImgRenderer::fatal.
+
+
+Images
+======
This is the simplest renderer, displaying an image. For each row to be
drawn,
-<ol>
-<li> first dw::core::Imgbuf::copyRow, and then
-<li> for each dw::Image, dw::Image::drawRow must be called, with the same
- argument (no scaling is necessary).
-</ol>
+- first dw::core::Imgbuf::copyRow, and then
+- for each dw::Image, dw::Image::drawRow must be called, with the same
+ argument (no scaling is necessary).
dw::Image automatically scales the dw::core::Imgbuf, the root buffer
should be passed to dw::Image::setBuffer.
\see dw::Image for more details.
-<h2>Future Extensions</h2>
-(This is not implemented yet.) Rendering itself (image widgets and
-background) will be abstracted, by a new interface
-dw::core::ImageRenderer. In the current code for image decoding, this
-interface will replace references to dw::Image, which implements
-dw::core::ImageRenderer, in most cases.
+Background Images
+=================
-<h2>Backgrounds</h2>
+Since background images are style resources, they are associated with
+dw::core::style::Style, as dw::core::style::StyleImage, which is
+handled in a similar way (reference counting etc.) as
+dw::core::style::Color and dw::core::style::Font, although it is
+concrete and not platform-dependant.
-(This is based on future extensions described above.) Since background
-are style resources, they are associated with
-dw::core::style::Style. For backgrounds, another level is needed,
-because of the 1-to-n relation from dw::core::style::Style to
-dw::core::Widget:
+The actual renderer (passed to Web) is an instance of
+dw::core::ImgRendererDist (distributes all calls to a set of other
+instances of dw::core::ImgRenderer), which contains two kinds of
+renderers:
-\dot
-digraph G {
- node [shape=record, fontname=Helvetica, fontsize=10];
- edge [arrowhead="open", dir="both", arrowtail="none",
- labelfontname=Helvetica, labelfontsize=10, color="#404040",
- labelfontcolor="#000080"];
- fontname=Helvetica; fontsize=10;
-
- "background renderer (as a part of style)" -> "image buffer" [headlabel="*",
- taillabel="1"];
- "widget (or a part of it)" -> "background renderer (as a part of style)"
- [headlabel="*", taillabel="1"];
-}
-\enddot
-
-Unlike dw::Image, dw::core::style::BgRenderer is not associated with a
-certain rectangle on the canvas. Instead, widgets, or parts of widgets
-take this role. This is generally represented by an implementation of
-the interface dw::core::style::BgAllocation, which is implemented by
-dw::core::Widget, but also by all parts of widget implementation,
-which may have an own background image.
+- one instance of dw::core::style::StyleImage::StyleImgRenderer, which
+ does everything needed for dw::core::style::StyleImage, and
+- other renderes, used externally (widgets etc.), which are added by
+ dw::core::style::StyleImage::putExternalImgRenderer (and removed by
+ dw::core::style::StyleImage::removeExternalImgRenderer).
-The following diagram gives a total overview:
+This diagram gives an comprehensive overview:
\dot
digraph G {
@@ -79,70 +92,151 @@ digraph G {
labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
- "DICache Entry";
-
- subgraph cluster_dw_images {
+ subgraph cluster_dw_style {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
- label="Dw Images";
- ImageRenderer [URL="\ref dw::core::ImageRenderer", color="#ff8080"];
- Imgbuf [URL="\ref dw::core::Imgbuf", color="#ff8080"];
+ Style [URL="\ref dw::core::style::Style"];
+ StyleImage [URL="\ref dw::core::style::StyleImage"];
+ Imgbuf [URL="\ref dw::core::Imgbuf", color="#a0a0a0"];
+ StyleImgRenderer
+ [URL="\ref dw::core::style::StyleImage::StyleImgRenderer"];
+ ImgRenderer [URL="\ref dw::core::ImgRenderer", color="#ff8080"];
+ ImgRendererDist [URL="\ref dw::core::ImgRendererDist"];
+ ExternalImgRenderer
+ [URL="\ref dw::core::style::StyleImage::ExternalImgRenderer",
+ color="#a0a0a0"];
+ ExternalWidgetImgRenderer
+ [URL="\ref dw::core::style::StyleImage::ExternalWidgetImgRenderer",
+ color="#a0a0a0"];
}
- subgraph cluster_widgets {
+ subgraph cluster_dw_layout {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
- label="Widgets";
- Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
- Textblock [URL="\ref dw::Textblock"];
- "Textblock::Word" [URL="\ref dw::Textblock::Word"];
- Table [URL="\ref dw::Table"];
- "Table::Row" [URL="\ref dw::Table::Row"];
- Image [URL="\ref dw::Image"];
+ Layout [URL="\ref dw::core::Layout"];
+ LayoutImgRenderer [URL="\ref dw::core::Layout::LayoutImgRenderer"];
}
- subgraph cluster_style {
+ subgraph cluster_dw_widget {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
- label="dw::core::style";
- Style [URL="\ref dw::core::style::Style"];
- BgRenderer [URL="\ref dw::core::style::BgRenderer"];
- BgAllocation [URL="\ref dw::core::style::BgAllocation", color="#ff8080"];
+ Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
+ WidgetImgRenderer [URL="\ref dw::core::Widget::WidgetImgRenderer"];
}
- "DICache Entry" -> ImageRenderer [headlabel="*", taillabel="1"];
- "DICache Entry" -> Imgbuf [headlabel="1", taillabel="1"];
-
- BgRenderer -> Imgbuf [headlabel="1", taillabel="*"];
- BgRenderer -> BgAllocation [headlabel="*", taillabel="1"];
- ImageRenderer -> BgRenderer [arrowhead="none", arrowtail="empty", dir="both",
- style="dashed"];
- ImageRenderer -> Image [arrowhead="none", arrowtail="empty", dir="both",
- style="dashed"];
-
- Style -> BgRenderer [headlabel="0..1", taillabel="1"];
+ subgraph cluster_dw_textblock {
+ style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
- Widget -> Textblock [arrowhead="none", arrowtail="empty", dir="both"];
- Textblock -> "Textblock::Word" [headlabel="*", taillabel="1"];
- Widget -> Table [arrowhead="none", arrowtail="empty", dir="both"];
- Table -> "Table::Row" [headlabel="*", taillabel="1"];
- Widget -> Image [arrowhead="none", arrowtail="empty", dir="both"];
+ Textblock [URL="\ref dw::Textblock"];
+ Word [URL="\ref dw::Textblock::Word"];
+ WordImgRenderer [URL="\ref dw::Textblock::WordImgRenderer"];
+ SpaceImgRenderer [URL="\ref dw::Textblock::SpaceImgRenderer"];
+ }
- BgAllocation -> Widget [arrowhead="none", arrowtail="empty", dir="both",
- style="dashed"];
- BgAllocation -> "Textblock::Word" [arrowhead="none", arrowtail="empty",
+ Style -> StyleImage [headlabel="*", taillabel="1"];
+ StyleImage -> Imgbuf [headlabel="*", taillabel="1"];
+ StyleImage -> StyleImgRenderer [headlabel="1", taillabel="1"];
+ StyleImage -> ImgRendererDist [headlabel="1", taillabel="1"];
+ ImgRendererDist -> StyleImgRenderer [headlabel="1", taillabel="1"];
+ ImgRendererDist -> ImgRenderer [headlabel="1", taillabel="*"];
+
+ ImgRenderer -> ImgRendererDist [arrowhead="none", arrowtail="empty",
+ dir="both", style="dashed"];
+ ImgRenderer -> StyleImgRenderer [arrowhead="none", arrowtail="empty",
+ dir="both", style="dashed"];
+ ImgRenderer -> ExternalImgRenderer [arrowhead="none", arrowtail="empty",
dir="both", style="dashed"];
- BgAllocation -> "Table::Row" [arrowhead="none", arrowtail="empty",
- dir="both", style="dashed"];
+ ExternalImgRenderer -> ExternalWidgetImgRenderer [arrowhead="none",
+ arrowtail="empty", dir="both", style="dashed"];
+
+ Layout -> LayoutImgRenderer [headlabel="1", taillabel="0..1"];
+ ExternalImgRenderer -> LayoutImgRenderer [arrowhead="none",
+ arrowtail="empty", dir="both", style="dashed"];
+
+ Widget -> WidgetImgRenderer [headlabel="1", taillabel="0..1"];
+ ExternalWidgetImgRenderer -> WidgetImgRenderer [arrowhead="none",
+ arrowtail="empty", dir="both", style="dashed"];
+
+ Textblock -> Word [headlabel="1", taillabel="*"];
+ Word -> WordImgRenderer [headlabel="1", taillabel="0..1"];
+ Word -> SpaceImgRenderer [headlabel="1", taillabel="0..1"];
+ ExternalWidgetImgRenderer -> WordImgRenderer [arrowhead="none",
+ arrowtail="empty", dir="both", style="dashed"];
+ WordImgRenderer -> SpaceImgRenderer [arrowhead="none", arrowtail="empty",
+ dir="both", style="dashed"];
}
\enddot
<center>[\ref uml-legend "legend"]</center>
-<h2>Integration into dillo</h2>
+Memory management
+-----------------
+
+dw::core::style::StyleImage extends lout::signal::ObservedObject, so
+that deleting this instance can be connected to code dealing with
+cache clients etc. See StyleImageDeletionReceiver and how it is
+attached in StyleEngine::apply ("case CSS_PROPERTY_BACKGROUND_IMAGE").
+
+
+Bugs and Things Needing Improvement
+===================================
+
+(Mostly related to image backgrounds, when not otherwise mentioned.)
+
+High Priority
+-------------
+
+**Images as toplevel resource:** There seem to be problems when the
+toplevel resource is an image. How to reproduce: Type <code>src/dillo
+http://www.dillo.org/db1.png</code> with the version from the
+background images branch; this will print the message "Content-Type
+'image/png' not viewable." and show the "Save link as file"
+dialog. The same works for the version from the main repository, with
+which the background images branch has been synchronized.
+
+**Configurability, security/privacy aspects, etc.,** which are
+currently available for image widgets, should be adopted. Perhaps some
+more configuration options specially for background images.
+
+
+Medium Priority
+---------------
+
+**Background-attachment** is not yet implemented, and will be postponed.
+
+**Small background images** result in a large number of calls to
+dw::core::View::drawImage, which may become slow. Solution: Create a
+new image buffer, which contains several copies of the original image.
+
+**Drawing background images row by row** may become slow. As an
+alternative, dw::core::ImgRenderer::finish could be used. However,
+drawing row by row could become an option. **Update:** There is now
+dw::core::style::drawBackgroundLineByLine, which can be changed in the
+code.
+
+(For image widgets, this could also become an option: in contexts,
+when image data is retrieved in a very fast way.)
+
+**Calls to dw::core::ImgRenderer::fatal** are incomplete. As an
+example, it is not called, when connecting to a server fails. (And so,
+as far as I see, no cache client is started.)
+
+
+Low Priority
+------------
+
+**Alpha support:** (not related to image backgrounds) currently alpha
+support (and also colormap management) is done in dicache, while
+dw::Image is only created with type RGB. This leads to several problems:
-\todo Add some references.
+- One dicache entry (representing an image related to the same URL),
+ which has only one background color, may refer to different images
+ with different background colors.
+- The dicache only handles background colors, not background images.
+The solution is basicly simple: keep out alpha support out of dicache;
+instead implement RGBA in dw::Image. As it seems, the main problem is
+alpha support in FLTK/X11.
*/
diff --git a/dw/Makefile.am b/dw/Makefile.am
index e108da60..d0d56d2a 100644
--- a/dw/Makefile.am
+++ b/dw/Makefile.am
@@ -14,6 +14,8 @@ libDw_core_a_SOURCES = \
findtext.cc \
findtext.hh \
imgbuf.hh \
+ imgrenderer.hh \
+ imgrenderer.cc \
iterator.cc \
iterator.hh \
layout.cc \
diff --git a/dw/core.hh b/dw/core.hh
index f39c38cc..022574b1 100644
--- a/dw/core.hh
+++ b/dw/core.hh
@@ -44,6 +44,7 @@ class ResourceFactory;
#include "types.hh"
#include "events.hh"
#include "imgbuf.hh"
+#include "imgrenderer.hh"
#include "style.hh"
#include "view.hh"
#include "platform.hh"
diff --git a/dw/image.cc b/dw/image.cc
index 1cb9ce39..acb2779e 100644
--- a/dw/image.cc
+++ b/dw/image.cc
@@ -450,6 +450,16 @@ void Image::drawRow (int row)
area.width, area.height);
}
+void Image::finish ()
+{
+ // Nothing to do; images are always drawn line by line.
+}
+
+void Image::fatal ()
+{
+ // Could display an error.
+}
+
/**
* \brief Sets image as server side image map.
diff --git a/dw/image.hh b/dw/image.hh
index a8314d4f..a712936e 100644
--- a/dw/image.hh
+++ b/dw/image.hh
@@ -116,7 +116,7 @@ public:
*
* \sa\ref dw-images-and-backgrounds
*/
-class Image: public core::Widget
+class Image: public core::Widget, public core::ImgRenderer
{
private:
char *altText;
@@ -157,6 +157,9 @@ public:
void drawRow (int row);
+ void finish ();
+ void fatal ();
+
void setIsMap ();
void setUseMap (ImageMapsList *list, Object *key);
diff --git a/dw/imgrenderer.cc b/dw/imgrenderer.cc
new file mode 100644
index 00000000..285a8dcd
--- /dev/null
+++ b/dw/imgrenderer.cc
@@ -0,0 +1,48 @@
+#include "core.hh"
+
+namespace dw {
+namespace core {
+
+using namespace lout::container;
+using namespace lout::object;
+
+void ImgRendererDist::setBuffer (core::Imgbuf *buffer, bool resize)
+{
+ for (typed::Iterator <TypedPointer <ImgRenderer> > it =
+ children->iterator (); it.hasNext (); ) {
+ TypedPointer <ImgRenderer> *tp = it.getNext ();
+ tp->getTypedValue()->setBuffer (buffer, resize);
+ }
+}
+
+void ImgRendererDist::drawRow (int row)
+{
+ for (typed::Iterator <TypedPointer <ImgRenderer> > it =
+ children->iterator (); it.hasNext (); ) {
+ TypedPointer <ImgRenderer> *tp = it.getNext ();
+ tp->getTypedValue()->drawRow (row);
+ }
+}
+
+
+void ImgRendererDist::finish ()
+{
+ for (typed::Iterator <TypedPointer <ImgRenderer> > it =
+ children->iterator (); it.hasNext (); ) {
+ TypedPointer <ImgRenderer> *tp = it.getNext ();
+ tp->getTypedValue()->finish ();
+ }
+}
+
+void ImgRendererDist::fatal ()
+{
+ for (typed::Iterator <TypedPointer <ImgRenderer> > it =
+ children->iterator (); it.hasNext (); ) {
+ TypedPointer <ImgRenderer> *tp = it.getNext ();
+ tp->getTypedValue()->fatal ();
+ }
+}
+
+
+} // namespace core
+} // namespace dw
diff --git a/dw/imgrenderer.hh b/dw/imgrenderer.hh
new file mode 100644
index 00000000..325a1998
--- /dev/null
+++ b/dw/imgrenderer.hh
@@ -0,0 +1,87 @@
+#ifndef __DW_IMGRENDERER_HH__
+#define __DW_IMGRENDERER_HH__
+
+#ifndef __INCLUDED_FROM_DW_CORE_HH__
+# error Do not include this file directly, use "core.hh" instead.
+#endif
+
+namespace dw {
+namespace core {
+
+/**
+ * \brief ...
+ *
+ * \sa \ref dw-images-and-backgrounds
+ */
+class ImgRenderer
+{
+public:
+ virtual ~ImgRenderer () { }
+
+ /**
+ * \brief Called, when an image buffer is attached.
+ *
+ * This is typically the case when all meta data (size, depth) has been read.
+ */
+ virtual void setBuffer (core::Imgbuf *buffer, bool resize = false) = 0;
+
+ /**
+ * \brief Called, when data from a row is available and has been copied into
+ * the image buffer.
+ *
+ * The implementation will typically queue the respective area for drawing.
+ */
+ virtual void drawRow (int row) = 0;
+
+ /**
+ * \brief Called, when all image data has been retrieved.
+ *
+ * The implementation may use this instead of "drawRow" for drawing, to
+ * limit the number of draws.
+ */
+ virtual void finish () = 0;
+
+ /**
+ * \brief Called, when there are problems with the retrieval of image data.
+ *
+ * The implementation may use this to indicate an error.
+ */
+ virtual void fatal () = 0;
+};
+
+/**
+ * \brief Implementation of ImgRenderer, which distributes all calls
+ * to a set of other implementations of ImgRenderer.
+ *
+ * The order of the call children is not defined, especially not
+ * identical to the order in which they have been added.
+ */
+class ImgRendererDist: public ImgRenderer
+{
+ lout::container::typed::HashSet <lout::object::TypedPointer <ImgRenderer> >
+ *children;
+
+public:
+ inline ImgRendererDist ()
+ { children = new lout::container::typed::HashSet
+ <lout::object::TypedPointer <ImgRenderer> > (true); }
+ ~ImgRendererDist () { delete children; }
+
+ void setBuffer (core::Imgbuf *buffer, bool resize);
+ void drawRow (int row);
+ void finish ();
+ void fatal ();
+
+ void put (ImgRenderer *child)
+ { children->put (new lout::object::TypedPointer <ImgRenderer> (child)); }
+ void remove (ImgRenderer *child)
+ { lout::object::TypedPointer <ImgRenderer> tp (child);
+ children->remove (&tp); }
+};
+
+} // namespace core
+} // namespace dw
+
+#endif // __DW_IMGRENDERER_HH__
+
+
diff --git a/dw/layout.cc b/dw/layout.cc
index 3f493d8a..8e1f065c 100644
--- a/dw/layout.cc
+++ b/dw/layout.cc
@@ -32,6 +32,65 @@ using namespace lout::object;
namespace dw {
namespace core {
+bool Layout::LayoutImgRenderer::readyToDraw ()
+{
+ return true;
+}
+
+void Layout::LayoutImgRenderer::getBgArea (int *x, int *y, int *width,
+ int *height)
+{
+ // TODO Actually not padding area, but visible area?
+ getRefArea (x, y, width, height);
+}
+
+void Layout::LayoutImgRenderer::getRefArea (int *xRef, int *yRef, int *widthRef,
+ int *heightRef)
+{
+ *xRef = 0;
+ *yRef = 0;
+ *widthRef = misc::max (layout->viewportWidth
+ - (layout->canvasHeightGreater ?
+ layout->vScrollbarThickness : 0),
+ layout->canvasWidth);
+ *heightRef = misc::max (layout->viewportHeight
+ - layout->hScrollbarThickness,
+ layout->canvasAscent + layout->canvasDescent);
+}
+
+style::StyleImage *Layout::LayoutImgRenderer::getBackgroundImage ()
+{
+ return layout->bgImage;
+}
+
+style::BackgroundRepeat Layout::LayoutImgRenderer::getBackgroundRepeat ()
+{
+ return layout->bgRepeat;
+}
+
+style::BackgroundAttachment
+ Layout::LayoutImgRenderer::getBackgroundAttachment ()
+{
+ return layout->bgAttachment;
+}
+
+style::Length Layout::LayoutImgRenderer::getBackgroundPositionX ()
+{
+ return layout->bgPositionX;
+}
+
+style::Length Layout::LayoutImgRenderer::getBackgroundPositionY ()
+{
+ return layout->bgPositionY;
+}
+
+void Layout::LayoutImgRenderer::draw (int x, int y, int width, int height)
+{
+ layout->queueDraw (x, y, width, height);
+}
+
+// ----------------------------------------------------------------------
+
void Layout::Receiver::canvasSizeChanged (int width, int ascent, int descent)
{
}
@@ -189,6 +248,7 @@ Layout::Layout (Platform *platform)
DBG_OBJ_CREATE (this, "DwRenderLayout");
bgColor = NULL;
+ bgImage = NULL;
cursor = style::CURSOR_DEFAULT;
canvasWidth = canvasAscent = canvasDescent = 0;
@@ -216,18 +276,28 @@ Layout::Layout (Platform *platform)
platform->setLayout (this);
selectionState.setLayout(this);
+
+ layoutImgRenderer = NULL;
}
Layout::~Layout ()
{
widgetAtPoint = NULL;
+ if (layoutImgRenderer) {
+ if (bgImage)
+ bgImage->removeExternalImgRenderer (layoutImgRenderer);
+ delete layoutImgRenderer;
+ }
+
if (scrollIdleId != -1)
platform->removeIdle (scrollIdleId);
if (resizeIdleId != -1)
platform->removeIdle (resizeIdleId);
if (bgColor)
bgColor->unref ();
+ if (bgImage)
+ bgImage->unref ();
if (topLevel) {
Widget *w = topLevel;
topLevel = NULL;
@@ -508,6 +578,23 @@ void Layout::draw (View *view, Rectangle *area)
{
Rectangle widgetArea, intersection, widgetDrawArea;
+ // First of all, draw background image. (Unlike background *color*,
+ // this is not a feature of the views.)
+ if (bgImage != NULL && bgImage->getImgbuf() != NULL)
+ style::drawBackgroundImage (view, bgImage, bgRepeat, bgAttachment,
+ bgPositionX, bgPositionY,
+ area->x, area->y, area->width,
+ area->height, 0, 0,
+ // Reference area: maximum of canvas size and
+ // viewport size.
+ misc::max (viewportWidth
+ - (canvasHeightGreater ?
+ vScrollbarThickness : 0),
+ canvasWidth),
+ misc::max (viewportHeight
+ - hScrollbarThickness,
+ canvasAscent + canvasDescent));
+
if (scrollIdleId != -1) {
/* scroll is pending, defer draw until after scrollIdle() */
drawAfterScrollReq = true;
@@ -650,6 +737,37 @@ void Layout::setBgColor (style::Color *color)
view->setBgColor (bgColor);
}
+void Layout::setBgImage (style::StyleImage *bgImage,
+ style::BackgroundRepeat bgRepeat,
+ style::BackgroundAttachment bgAttachment,
+ style::Length bgPositionX, style::Length bgPositionY)
+{
+ if (layoutImgRenderer && this->bgImage)
+ this->bgImage->removeExternalImgRenderer (layoutImgRenderer);
+
+ if (bgImage)
+ bgImage->ref ();
+
+ if (this->bgImage)
+ this->bgImage->unref ();
+
+ this->bgImage = bgImage;
+ this->bgRepeat = bgRepeat;
+ this->bgAttachment = bgAttachment;
+ this->bgPositionX = bgPositionX;
+ this->bgPositionY = bgPositionY;
+
+ if (bgImage) {
+ // Create instance of LayoutImgRenderer when needed. Until this
+ // layout is deleted, "layoutImgRenderer" will be kept, since it
+ // is not specific to the style, but only to this layout.
+ if (layoutImgRenderer == NULL)
+ layoutImgRenderer = new LayoutImgRenderer (this);
+ bgImage->putExternalImgRenderer (layoutImgRenderer);
+ }
+}
+
+
void Layout::resizeIdle ()
{
//static int calls = 0;
diff --git a/dw/layout.hh b/dw/layout.hh
index 51d764a4..47554b42 100644
--- a/dw/layout.hh
+++ b/dw/layout.hh
@@ -17,6 +17,27 @@ class Layout: public lout::object::Object
{
friend class Widget;
+private:
+ class LayoutImgRenderer: public style::StyleImage::ExternalImgRenderer
+ {
+ Layout *layout;
+
+ public:
+ LayoutImgRenderer (Layout *layout) { this->layout = layout; }
+
+ bool readyToDraw ();
+ void getBgArea (int *x, int *y, int *width, int *height);
+ void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef);
+ style::StyleImage *getBackgroundImage ();
+ style::BackgroundRepeat getBackgroundRepeat ();
+ style::BackgroundAttachment getBackgroundAttachment ();
+ style::Length getBackgroundPositionX ();
+ style::Length getBackgroundPositionY ();
+ void draw (int x, int y, int width, int height);
+ };
+
+ LayoutImgRenderer *layoutImgRenderer;
+
public:
/**
* \brief Receiver interface different signals.
@@ -135,6 +156,11 @@ private:
/* The state, which must be projected into the view. */
style::Color *bgColor;
+ style::StyleImage *bgImage;
+ style::BackgroundRepeat bgRepeat;
+ style::BackgroundAttachment bgAttachment;
+ style::Length bgPositionX, bgPositionY;
+
style::Cursor cursor;
int canvasWidth, canvasAscent, canvasDescent;
@@ -386,8 +412,13 @@ public:
inline void resetSearch () { findtextState.resetSearch (); }
void setBgColor (style::Color *color);
+ void setBgImage (style::StyleImage *bgImage,
+ style::BackgroundRepeat bgRepeat,
+ style::BackgroundAttachment bgAttachment,
+ style::Length bgPositionX, style::Length bgPositionY);
inline style::Color* getBgColor () { return bgColor; }
+ inline style::StyleImage* getBgImage () { return bgImage; }
};
} // namespace core
diff --git a/dw/style.cc b/dw/style.cc
index 6c0abda2..a9ce7544 100644
--- a/dw/style.cc
+++ b/dw/style.cc
@@ -34,6 +34,22 @@ namespace dw {
namespace core {
namespace style {
+const bool drawBackgroundLineByLine = true;
+
+static void calcBackgroundRelatedValues (StyleImage *backgroundImage,
+ BackgroundRepeat backgroundRepeat,
+ BackgroundAttachment
+ backgroundAttachment,
+ Length backgroundPositionX,
+ Length backgroundPositionY,
+ int xDraw, int yDraw, int widthDraw,
+ int heightDraw, int xRef, int yRef,
+ int widthRef, int heightRef,
+ bool *repeatX, bool *repeatY,
+ int *origX, int *origY,
+ int *tileX1, int *tileX2, int *tileY1,
+ int *tileY2, bool *doDraw);
+
void StyleAttrs::initValues ()
{
x_link = -1;
@@ -48,6 +64,11 @@ void StyleAttrs::initValues ()
listStyleType = LIST_STYLE_TYPE_DISC;
valign = VALIGN_BASELINE;
backgroundColor = NULL;
+ backgroundImage = NULL;
+ backgroundRepeat = BACKGROUND_REPEAT;
+ backgroundAttachment = BACKGROUND_ATTACHMENT_SCROLL;
+ backgroundPositionX = createPerLength (0);
+ backgroundPositionY = createPerLength (0);
width = height = lineHeight = LENGTH_AUTO;
textIndent = 0;
margin.setVal (0);
@@ -76,6 +97,11 @@ void StyleAttrs::resetValues ()
valign = VALIGN_BASELINE;
textAlignChar = '.';
backgroundColor = NULL;
+ backgroundImage = NULL;
+ backgroundRepeat = BACKGROUND_REPEAT;
+ backgroundAttachment = BACKGROUND_ATTACHMENT_SCROLL;
+ backgroundPositionX = createPerLength (0);
+ backgroundPositionY = createPerLength (0);
width = LENGTH_AUTO;
height = LENGTH_AUTO;
@@ -116,6 +142,11 @@ bool StyleAttrs::equals (object::Object *other) {
textDecoration == otherAttrs->textDecoration &&
color == otherAttrs->color &&
backgroundColor == otherAttrs->backgroundColor &&
+ backgroundImage == otherAttrs->backgroundImage &&
+ backgroundRepeat == otherAttrs->backgroundRepeat &&
+ backgroundAttachment == otherAttrs->backgroundAttachment &&
+ backgroundPositionX == otherAttrs->backgroundPositionX &&
+ backgroundPositionY == otherAttrs->backgroundPositionY &&
textAlign == otherAttrs->textAlign &&
valign == otherAttrs->valign &&
textAlignChar == otherAttrs->textAlignChar &&
@@ -156,6 +187,11 @@ int StyleAttrs::hashValue () {
textDecoration +
(intptr_t) color +
(intptr_t) backgroundColor +
+ (intptr_t) backgroundImage +
+ backgroundRepeat +
+ backgroundAttachment +
+ backgroundPositionX +
+ backgroundPositionY +
textAlign +
valign +
textAlignChar +
@@ -205,6 +241,8 @@ Style::Style (StyleAttrs *attrs)
color->ref ();
if (backgroundColor)
backgroundColor->ref ();
+ if (backgroundImage)
+ backgroundImage->ref ();
if (borderColor.top)
borderColor.top->ref();
if (borderColor.bottom)
@@ -227,6 +265,8 @@ Style::~Style ()
color->unref ();
if (backgroundColor)
backgroundColor->unref ();
+ if (backgroundImage)
+ backgroundImage->unref ();
if (borderColor.top)
borderColor.top->unref();
if (borderColor.bottom)
@@ -248,6 +288,11 @@ void Style::copyAttrs (StyleAttrs *attrs)
textDecoration = attrs->textDecoration;
color = attrs->color;
backgroundColor = attrs->backgroundColor;
+ backgroundImage = attrs->backgroundImage;
+ backgroundRepeat = attrs->backgroundRepeat;
+ backgroundAttachment = attrs->backgroundAttachment;
+ backgroundPositionX = attrs->backgroundPositionX;
+ backgroundPositionY = attrs->backgroundPositionY;
textAlign = attrs->textAlign;
valign = attrs->valign;
textAlignChar = attrs->textAlignChar;
@@ -422,6 +467,160 @@ Tooltip *Tooltip::create (Layout *layout, const char *text)
// ----------------------------------------------------------------------
+void StyleImage::StyleImgRenderer::setBuffer (core::Imgbuf *buffer, bool resize)
+{
+ if (image->imgbuf)
+ image->imgbuf->unref ();
+
+ image->imgbuf = buffer;
+ if (image->imgbuf)
+ image->imgbuf->ref ();
+}
+
+void StyleImage::StyleImgRenderer::drawRow (int row)
+{
+ // Nothing to do.
+}
+
+void StyleImage::StyleImgRenderer::finish ()
+{
+ // Nothing to do.
+}
+
+void StyleImage::StyleImgRenderer::fatal ()
+{
+ // Nothing to do.
+}
+
+StyleImage::StyleImage ()
+{
+ //printf ("new StyleImage %p\n", this);
+
+ refCount = 0;
+ imgbuf = NULL;
+
+ imgRendererDist = new ImgRendererDist ();
+ styleImgRenderer = new StyleImgRenderer (this);
+ imgRendererDist->put (styleImgRenderer);
+}
+
+StyleImage::~StyleImage ()
+{
+ //printf ("delete StyleImage %p\n", this);
+
+ if (imgbuf)
+ imgbuf->unref ();
+
+ delete imgRendererDist;
+ delete styleImgRenderer;
+}
+
+void StyleImage::ExternalImgRenderer::setBuffer (core::Imgbuf *buffer,
+ bool resize)
+{
+ // Nothing to do?
+}
+
+void StyleImage::ExternalImgRenderer::drawRow (int row)
+{
+ if (drawBackgroundLineByLine) {
+ StyleImage *backgroundImage;
+ if (readyToDraw () && (backgroundImage = getBackgroundImage ())) {
+ // All single rows are drawn.
+
+ Imgbuf *imgbuf = backgroundImage->getImgbuf();
+ int imgWidth = imgbuf->getRootWidth ();
+ int imgHeight = imgbuf->getRootHeight ();
+
+ int x, y, width, height;
+ getBgArea (&x, &y, &width, &height);
+
+ int xRef, yRef, widthRef, heightRef;
+ getRefArea (&xRef, &yRef, &widthRef, &heightRef);
+
+ bool repeatX, repeatY, doDraw;
+ int origX, origY, tileX1, tileX2, tileY1, tileY2;
+
+ calcBackgroundRelatedValues (backgroundImage,
+ getBackgroundRepeat (),
+ getBackgroundAttachment (),
+ getBackgroundPositionX (),
+ getBackgroundPositionY (),
+ x, y, width, height, xRef, yRef, widthRef,
+ heightRef, &repeatX, &repeatY, &origX,
+ &origY, &tileX1, &tileX2, &tileY1,
+ &tileY2, &doDraw);
+
+ //printf ("tileX1 = %d, tileX2 = %d, tileY1 = %d, tileY2 = %d\n",
+ // tileX1, tileX2, tileY1, tileY2);
+
+ if (doDraw)
+ // Only iterate over y, because the rows can be combined
+ // horizontically.
+ for (int tileY = tileY1; tileY <= tileY2; tileY++) {
+ int x1 = misc::max (origX + tileX1 * imgWidth, x);
+ int x2 = misc::min (origX + (tileX2 + 1) * imgWidth, x + width);
+
+ int yt = origY + tileY * imgHeight + row;
+ if (yt >= y && yt < y + height)
+ draw (x1, yt, x2 - x1, 1);
+ }
+ }
+ }
+}
+
+void StyleImage::ExternalImgRenderer::finish ()
+{
+ if (!drawBackgroundLineByLine) {
+ if (readyToDraw ()) {
+ // Draw total area, as a whole.
+ int x, y, width, height;
+ getBgArea (&x, &y, &width, &height);
+ draw (x, y, width, height);
+ }
+ }
+}
+
+void StyleImage::ExternalImgRenderer::fatal ()
+{
+ // Nothing to do.
+}
+
+// ----------------------------------------------------------------------
+
+StyleImage *StyleImage::ExternalWidgetImgRenderer::getBackgroundImage ()
+{
+ Style *style = getStyle ();
+ return style ? style->backgroundImage : NULL;
+}
+
+BackgroundRepeat StyleImage::ExternalWidgetImgRenderer::getBackgroundRepeat ()
+{
+ Style *style = getStyle ();
+ return style ? style->backgroundRepeat : BACKGROUND_REPEAT;
+}
+
+BackgroundAttachment
+ StyleImage::ExternalWidgetImgRenderer::getBackgroundAttachment ()
+{
+ Style *style = getStyle ();
+ return style ? style->backgroundAttachment : BACKGROUND_ATTACHMENT_SCROLL;
+}
+
+Length StyleImage::ExternalWidgetImgRenderer::getBackgroundPositionX ()
+{
+ Style *style = getStyle ();
+ return style ? style->backgroundPositionX : createPerLength (0);
+}
+
+Length StyleImage::ExternalWidgetImgRenderer::getBackgroundPositionY ()
+{
+ Style *style = getStyle ();
+ return style ? style->backgroundPositionY : createPerLength (0);
+}
+
+// ----------------------------------------------------------------------
+
/*
* The drawBorder{Top,Bottom,Left,Right} functions are similar. They
* use a trapezium as draw polygon, or drawTypedLine() for dots and dashes.
@@ -823,8 +1022,11 @@ static void drawBorderRight(View *view, Style *style,
* \brief Draw the border of a region in window, according to style.
*
* Used by dw::core::Widget::drawBox and dw::core::Widget::drawWidgetBox.
+ *
+ * "area" is the area to be drawn, "x", "y", "width" and "height"
+ * define the box itself. All are given in canvas coordinates.
*/
-void drawBorder (View *view, Rectangle *area,
+void drawBorder (View *view, Layout *layout, Rectangle *area,
int x, int y, int width, int height,
Style *style, bool inverse)
{
@@ -861,29 +1063,183 @@ void drawBorder (View *view, Rectangle *area,
* according to style.
*
* Used by dw::core::Widget::drawBox and dw::core::Widget::drawWidgetBox.
+ *
+ * "area" is the area to be drawn, "x", "y", "width" and "height"
+ * define the box itself (padding box). "xRef", "yRef", "widthRef" and
+ * "heightRef" define the reference area, which is important for the
+ * tiling of background images (for position 0%/0%, a tile is set at
+ * xRef/yRef; for position 100%/100%, a tile is set at xRef +
+ * widthRef/yRef + widthRef). See calls for more informations; in most
+ * cases, these boxes are identical (padding box). All these
+ * coordinates are given in canvas coordinates.
+ *
+ * "atTop" should be true, only if the area is drawn directly on the
+ * canvas, not on top of other areas; this is only true for the
+ * toplevel widget itself (not parts of its contents). Toplevel widget
+ * background colors are already set as viewport background color, so
+ * that drawing again is is not neccessary, but some time can be
+ * saved.
+ *
+ * Otherwise, the caller should not try to increase the performance by
+ * doing some tests before; this is all done in this method.
*/
-void drawBackground (View *view, Rectangle *area,
+void drawBackground (View *view, Layout *layout, Rectangle *area,
int x, int y, int width, int height,
- Style *style, bool inverse)
-{
- Rectangle bgArea, intersection;
-
- if (style->backgroundColor) {
- bgArea.x = x + style->margin.left + style->borderWidth.left;
- bgArea.y = y + style->margin.top + style->borderWidth.top;
- bgArea.width =
- width - style->margin.left - style->borderWidth.left -
- style->margin.right - style->borderWidth.right;
- bgArea.height =
- height - style->margin.top - style->borderWidth.top -
- style->margin.bottom - style->borderWidth.bottom;
-
- if (area->intersectsWith (&bgArea, &intersection))
- view->drawRectangle (style->backgroundColor,
- inverse ?
- Color::SHADING_INVERSE : Color::SHADING_NORMAL,
- true, intersection.x, intersection.y,
- intersection.width, intersection.height);
+ int xRef, int yRef, int widthRef, int heightRef,
+ Style *style, bool inverse, bool atTop)
+{
+ bool bgColor = style->backgroundColor != 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->getImgbuf() != 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
+ // background image of the toplevel widget is set), only the background
+ // images are compared. A full test, which also deals with all other
+ // attributes related to backgrond images (repeat, position etc.) would
+ // be complicated and useless, so not worth the work.
+ (!atTop || layout->getBgImage () != style->backgroundImage);
+
+ // Since widgets are always drawn from top to bottom, it is *not*
+ // necessary to draw the background if background color and image
+ // are not set (NULL), i. e. shining through.
+
+ if (bgColor || bgImage) {
+ Rectangle bgArea, intersection;
+ bgArea.x = x;
+ bgArea.y = y;
+ bgArea.width = width;
+ bgArea.height = height;
+
+ if (area->intersectsWith (&bgArea, &intersection)) {
+ if (bgColor)
+ view->drawRectangle (style->backgroundColor,
+ inverse ?
+ Color::SHADING_INVERSE : Color::SHADING_NORMAL,
+ true, intersection.x, intersection.y,
+ intersection.width, intersection.height);
+
+ if (bgImage)
+ drawBackgroundImage (view, style->backgroundImage,
+ style->backgroundRepeat,
+ style->backgroundAttachment,
+ style->backgroundPositionX,
+ style->backgroundPositionY,
+ intersection.x, intersection.y,
+ intersection.width, intersection.height,
+ xRef, yRef, widthRef, heightRef);
+
+ }
+ }
+}
+
+void drawBackgroundImage (View *view, StyleImage *backgroundImage,
+ BackgroundRepeat backgroundRepeat,
+ BackgroundAttachment backgroundAttachment,
+ Length backgroundPositionX,
+ Length backgroundPositionY,
+ int x, int y, int width, int height,
+ int xRef, int yRef, int widthRef, int heightRef)
+{
+ Imgbuf *imgbuf = backgroundImage->getImgbuf();
+ int imgWidth = imgbuf->getRootWidth ();
+ int imgHeight = imgbuf->getRootHeight ();
+
+ //printf ("drawBackgroundImage (..., [img: %d, %d], ..., (%d, %d), %d x %d, "
+ // "(%d, %d), %d x %d)\n", imgWidth, imgHeight, x, y, width, height,
+ // xRef, yRef, widthRef, heightRef);
+
+ bool repeatX, repeatY, doDraw;
+ int origX, origY, tileX1, tileX2, tileY1, tileY2;
+
+ calcBackgroundRelatedValues (backgroundImage, backgroundRepeat,
+ backgroundAttachment, backgroundPositionX,
+ backgroundPositionY, x, y, width, height,
+ xRef, yRef, widthRef, heightRef,
+ &repeatX, &repeatY, &origX, &origY,
+ &tileX1, &tileX2, &tileY1, &tileY2, &doDraw);
+
+ //printf ("tileX1 = %d, tileX2 = %d, tileY1 = %d, tileY2 = %d\n",
+ // tileX1, tileX2, tileY1, tileY2);
+
+ if (doDraw)
+ for (int tileX = tileX1; tileX <= tileX2; tileX++)
+ for (int tileY = tileY1; tileY <= tileY2; tileY++) {
+ int xt = origX + tileX * imgWidth;
+ int x1 = misc::max (xt, x);
+ int x2 = misc::min (xt + imgWidth, x + width);
+ int yt = origY + tileY * imgHeight;
+ int y1 = misc::max (yt, y);
+ int y2 = misc::min (yt + imgHeight, y + height);
+
+ view->drawImage (imgbuf, xt, yt, x1 - xt, y1 - yt,
+ x2 - x1, y2 - y1);
+ }
+}
+
+void calcBackgroundRelatedValues (StyleImage *backgroundImage,
+ BackgroundRepeat backgroundRepeat,
+ BackgroundAttachment backgroundAttachment,
+ Length backgroundPositionX,
+ Length backgroundPositionY,
+ int xDraw, int yDraw, int widthDraw,
+ int heightDraw, int xRef, int yRef,
+ int widthRef, int heightRef, bool *repeatX,
+ bool *repeatY, int *origX, int *origY,
+ int *tileX1, int *tileX2, int *tileY1,
+ int *tileY2, bool *doDraw)
+{
+ Imgbuf *imgbuf = backgroundImage->getImgbuf();
+ int imgWidth = imgbuf->getRootWidth ();
+ int imgHeight = imgbuf->getRootHeight ();
+
+ *repeatX = backgroundRepeat == BACKGROUND_REPEAT ||
+ backgroundRepeat == BACKGROUND_REPEAT_X;
+ *repeatY = backgroundRepeat == BACKGROUND_REPEAT ||
+ backgroundRepeat == BACKGROUND_REPEAT_Y;
+
+ *origX = xRef +
+ (isPerLength (backgroundPositionX) ?
+ perLengthVal (backgroundPositionX) * (widthRef - imgWidth) :
+ absLengthVal (backgroundPositionX));
+ *origY = yRef +
+ (isPerLength (backgroundPositionY) ?
+ perLengthVal (backgroundPositionY) * (heightRef - imgHeight) :
+ absLengthVal (backgroundPositionY));
+
+ *tileX1 = xDraw < *origX ?
+ - (*origX - xDraw + imgWidth - 1) / imgWidth :
+ (xDraw - *origX) / imgWidth;
+ *tileX2 = *origX < xDraw + widthDraw ?
+ (xDraw + widthDraw - *origX - 1) / imgWidth :
+ - (*origX - (xDraw + widthDraw) + imgWidth - 1) / imgWidth;
+ *tileY1 = yDraw < *origY ?
+ - (*origY - yDraw + imgHeight - 1) / imgHeight :
+ (yDraw - *origY) / imgHeight;
+ *tileY2 = *origY < yDraw + heightDraw ?
+ (yDraw + heightDraw - *origY - 1) / imgHeight :
+ - (*origY - (yDraw + heightDraw) + imgHeight - 1) / imgHeight;
+
+ *doDraw = true;
+ if (!*repeatX) {
+ // Only center tile (tileX = 0) is drawn, ...
+ if (*tileX1 <= 0 && *tileX2 >= 0)
+ // ... and is visible.
+ *tileX1 = *tileX2 = 0;
+ else
+ // ... but is not visible.
+ *doDraw = false;
+ }
+
+ if (!*repeatY) {
+ // Analogue.
+ if (*tileY1 <= 0 && *tileY2 >= 0)
+ *tileY1 = *tileY2 = 0;
+ else
+ *doDraw = false;
}
}
diff --git a/dw/style.hh b/dw/style.hh
index c86446a3..752f6a64 100644
--- a/dw/style.hh
+++ b/dw/style.hh
@@ -7,6 +7,8 @@
# error Do not include this file directly, use "core.hh" instead.
#endif
+#include "../lout/signal.hh"
+
namespace dw {
namespace core {
@@ -228,6 +230,18 @@ enum BorderStyle {
BORDER_OUTSET
};
+enum BackgroundRepeat {
+ BACKGROUND_REPEAT,
+ BACKGROUND_REPEAT_X,
+ BACKGROUND_REPEAT_Y,
+ BACKGROUND_NO_REPEAT
+};
+
+enum BackgroundAttachment {
+ BACKGROUND_ATTACHMENT_SCROLL,
+ BACKGROUND_ATTACHMENT_FIXED
+};
+
enum TextAlignType {
TEXT_ALIGN_LEFT,
TEXT_ALIGN_RIGHT,
@@ -463,9 +477,10 @@ public:
}
};
+class Tooltip;
class Font;
class Color;
-class Tooltip;
+class StyleImage;
/**
* \sa dw::core::style
@@ -477,6 +492,11 @@ public:
int textDecoration; /* No TextDecoration because of problems converting
* TextDecoration <-> int */
Color *color, *backgroundColor;
+ StyleImage *backgroundImage;
+ BackgroundRepeat backgroundRepeat;
+ BackgroundAttachment backgroundAttachment;
+ Length backgroundPositionX; // "left" defined by "0%" etc. (see CSS spec)
+ Length backgroundPositionY; // "top" defined by "0%" etc. (see CSS spec)
TextAlignType textAlign;
VAlignType valign;
@@ -536,7 +556,8 @@ public:
}
inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); }
- inline bool hasBackground () { return backgroundColor != NULL; }
+ inline bool hasBackground ()
+ { return backgroundColor != NULL || backgroundImage != NULL; }
bool equals (lout::object::Object *other);
int hashValue ();
@@ -709,12 +730,133 @@ public:
{ if (--refCount == 0) delete this; }
};
-void drawBorder (View *view, Rectangle *area,
+
+class StyleImage: public lout::signal::ObservedObject
+{
+private:
+ class StyleImgRenderer: public ImgRenderer
+ {
+ private:
+ StyleImage *image;
+
+ public:
+ inline StyleImgRenderer (StyleImage *image) { this->image = image; }
+
+ void setBuffer (core::Imgbuf *buffer, bool resize);
+ void drawRow (int row);
+ void finish ();
+ void fatal ();
+ };
+
+ int refCount;
+ Imgbuf *imgbuf;
+ ImgRendererDist *imgRendererDist;
+ StyleImgRenderer *styleImgRenderer;
+
+ StyleImage ();
+ ~StyleImage ();
+
+public:
+ /**
+ * \brief Useful (but not mandatory) base class for updates of
+ * areas with background images.
+ */
+ class ExternalImgRenderer: public ImgRenderer
+ {
+ public:
+ void setBuffer (core::Imgbuf *buffer, bool resize);
+ void drawRow (int row);
+ void finish ();
+ void fatal ();
+
+ /**
+ * \brief If this method returns false, nothing is done at all.
+ */
+ virtual bool readyToDraw () = 0;
+
+ /**
+ * \brief Return the area covered by the background image.
+ */
+ virtual void getBgArea (int *x, int *y, int *width, int *height) = 0;
+
+ /**
+ * \brief Return the "reference area".
+ *
+ * See comment of "drawBackground".
+ */
+ virtual void getRefArea (int *xRef, int *yRef, int *widthRef,
+ int *heightRef) = 0;
+
+ virtual StyleImage *getBackgroundImage () = 0;
+ virtual BackgroundRepeat getBackgroundRepeat () = 0;
+ virtual BackgroundAttachment getBackgroundAttachment () = 0;
+ virtual Length getBackgroundPositionX () = 0;
+ virtual Length getBackgroundPositionY () = 0;
+
+ /**
+ * \brief Draw (or queue for drawing) an area, which is given in
+ * canvas coordinates.
+ */
+ virtual void draw (int x, int y, int width, int height) = 0;
+ };
+
+ /**
+ * \brief Suitable for widgets and parts of widgets.
+ */
+ class ExternalWidgetImgRenderer: public ExternalImgRenderer
+ {
+ public:
+ void getPaddingArea (int *x, int *y, int *width, int *height);
+
+ StyleImage *getBackgroundImage ();
+ BackgroundRepeat getBackgroundRepeat ();
+ BackgroundAttachment getBackgroundAttachment ();
+ Length getBackgroundPositionX ();
+ Length getBackgroundPositionY ();
+
+ /**
+ * \brief Return the style this background image is part of.
+ */
+ virtual Style *getStyle () = 0;
+ };
+
+ static StyleImage *create () { return new StyleImage (); }
+
+ inline void ref () { refCount++; }
+ inline void unref ()
+ { if (--refCount == 0) delete this; }
+
+ inline Imgbuf *getImgbuf () { return imgbuf; }
+ inline ImgRenderer *getMainImgRenderer () { return imgRendererDist; }
+
+ /**
+ * \brief Add an additional ImgRenderer, especially used for
+ * drawing.
+ */
+ inline void putExternalImgRenderer (ImgRenderer *ir)
+ { imgRendererDist->put (ir); }
+
+ /**
+ * \brief Remove a previously added additional ImgRenderer.
+ */
+ inline void removeExternalImgRenderer (ImgRenderer *ir)
+ { imgRendererDist->remove (ir); }
+};
+
+void drawBorder (View *view, Layout *layout, Rectangle *area,
int x, int y, int width, int height,
Style *style, bool inverse);
-void drawBackground (View *view, Rectangle *area,
+void drawBackground (View *view, Layout *layout, Rectangle *area,
int x, int y, int width, int height,
- Style *style, bool inverse);
+ int xRef, int yRef, int widthRef, int heightRef,
+ Style *style, bool inverse, bool atTop);
+void drawBackgroundImage (View *view, StyleImage *backgroundImage,
+ BackgroundRepeat backgroundRepeat,
+ BackgroundAttachment backgroundAttachment,
+ Length backgroundPositionX,
+ Length backgroundPositionY,
+ int x, int y, int width, int height,
+ int xRef, int yRef, int widthRef, int heightRef);
void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType);
} // namespace style
diff --git a/dw/textblock.cc b/dw/textblock.cc
index 3bedcdba..a45b3da5 100644
--- a/dw/textblock.cc
+++ b/dw/textblock.cc
@@ -41,6 +41,103 @@ namespace dw {
int Textblock::CLASS_ID = -1;
+Textblock::WordImgRenderer::WordImgRenderer (Textblock *textblock,
+ int wordNo)
+{
+ //printf ("new WordImgRenderer %p\n", this);
+
+ this->textblock = textblock;
+ this->wordNo = wordNo;
+ dataSet = false;
+}
+
+Textblock::WordImgRenderer::~WordImgRenderer ()
+{
+ //printf ("delete WordImgRenderer %p\n", this);
+}
+
+void Textblock::WordImgRenderer::setData (int xWordWidget, int lineNo)
+{
+ dataSet = true;
+ this->xWordWidget = xWordWidget;
+ this->lineNo = lineNo;
+}
+
+bool Textblock::WordImgRenderer::readyToDraw ()
+{
+ //print ();
+ //printf ("\n");
+
+ return dataSet && textblock->wasAllocated ()
+ && wordNo < textblock->words->size()
+ && lineNo < textblock->lines->size();
+}
+
+void Textblock::WordImgRenderer::getBgArea (int *x, int *y, int *width,
+ int *height)
+{
+ // TODO Subtract margin and border (padding box)?
+ Line *line = textblock->lines->getRef (lineNo);
+ *x = textblock->allocation.x + this->xWordWidget;
+ *y = textblock->lineYOffsetCanvas (line);
+ *width = textblock->words->getRef(wordNo)->size.width;
+ *height = line->boxAscent + line->boxDescent;
+}
+
+void Textblock::WordImgRenderer::getRefArea (int *xRef, int *yRef,
+ int *widthRef, int *heightRef)
+{
+ // See comment in Widget::drawBox about the reference area.q
+ textblock->getPaddingArea (xRef, yRef, widthRef, heightRef);
+}
+
+core::style::Style *Textblock::WordImgRenderer::getStyle ()
+{
+ return textblock->words->getRef(wordNo)->style;
+}
+
+void Textblock::WordImgRenderer::draw (int x, int y, int width, int height)
+{
+ textblock->queueDrawArea (x - textblock->allocation.x,
+ y - textblock->allocation.y, width, height);
+
+}
+
+void Textblock::WordImgRenderer::print ()
+{
+ printf ("%p: word #%d, ", this, wordNo);
+ if (wordNo < textblock->words->size())
+ textblock->printWordShort (textblock->words->getRef(wordNo));
+ else
+ printf ("<word %d does not exist>", wordNo);
+ printf (", data set: %s", dataSet ? "yes" : "no");
+}
+
+void Textblock::SpaceImgRenderer::getBgArea (int *x, int *y, int *width,
+ int *height)
+{
+ WordImgRenderer::getBgArea (x, y, width, height);
+ *x += *width;
+ *width = textblock->words->getRef(wordNo)->effSpace;
+}
+
+core::style::Style *Textblock::SpaceImgRenderer::getStyle ()
+{
+ return textblock->words->getRef(wordNo)->spaceStyle;
+}
+
+void Textblock::SpaceImgRenderer::print ()
+{
+ printf ("%p: word FOR SPACE #%d, ", this, wordNo);
+ if (wordNo < textblock->words->size())
+ textblock->printWordShort (textblock->words->getRef(wordNo));
+ else
+ printf ("<word %d does not exist>", wordNo);
+ printf (", data set: %s", dataSet ? "yes" : "no");
+}
+
+// ----------------------------------------------------------------------
+
Textblock::DivChar Textblock::divChars[NUM_DIV_CHARS] = {
// soft hyphen (U+00AD)
{ "\xc2\xad", true, false, true, PENALTY_HYPHEN, -1 },
@@ -188,8 +285,13 @@ Textblock::~Textblock ()
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 ();
}
@@ -1354,14 +1456,77 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent,
short flags, core::style::Style *style)
{
words->increase ();
- Word *word = words->getLastRef ();
- fillWord (word, width, ascent, descent, flags, style);
- return word;
+ int wordNo = words->size () - 1;
+ initWord (wordNo);
+ fillWord (wordNo, width, ascent, descent, flags, style);
+ return words->getRef (wordNo);;
+}
+
+/**
+ * Basic initialization, which is neccessary before fillWord.
+ */
+void Textblock::initWord (int wordNo)
+{
+ Word *word = words->getRef (wordNo);
+
+ word->style = word->spaceStyle = NULL;
+ word->wordImgRenderer = NULL;
+ word->spaceImgRenderer = NULL;
+}
+
+void Textblock::removeWordImgRenderer (int wordNo)
+{
+ Word *word = words->getRef (wordNo);
+
+ if (word->style && word->wordImgRenderer) {
+ word->style->backgroundImage->removeExternalImgRenderer
+ (word->wordImgRenderer);
+ delete word->wordImgRenderer;
+ word->wordImgRenderer = NULL;
+ }
}
-void Textblock::fillWord (Word *word, int width, int ascent, int descent,
+void Textblock::setWordImgRenderer (int wordNo)
+{
+ Word *word = words->getRef (wordNo);
+
+ if (word->style->backgroundImage) {
+ word->wordImgRenderer = new WordImgRenderer (this, wordNo);
+ word->style->backgroundImage->putExternalImgRenderer
+ (word->wordImgRenderer);
+ } else
+ word->wordImgRenderer = NULL;
+}
+
+void Textblock::removeSpaceImgRenderer (int wordNo)
+{
+ Word *word = words->getRef (wordNo);
+
+ if (word->spaceStyle && word->spaceImgRenderer) {
+ word->spaceStyle->backgroundImage->removeExternalImgRenderer
+ (word->spaceImgRenderer);
+ delete word->spaceImgRenderer;
+ word->spaceImgRenderer = NULL;
+ }
+}
+
+void Textblock::setSpaceImgRenderer (int wordNo)
+{
+ Word *word = words->getRef (wordNo);
+
+ if (word->spaceStyle->backgroundImage) {
+ word->spaceImgRenderer = new SpaceImgRenderer (this, wordNo);
+ word->spaceStyle->backgroundImage->putExternalImgRenderer
+ (word->spaceImgRenderer);
+ } else
+ word->spaceImgRenderer = NULL;
+}
+
+void Textblock::fillWord (int wordNo, int width, int ascent, int descent,
short flags, core::style::Style *style)
{
+ Word *word = words->getRef (wordNo);
+
word->size.width = width;
word->size.ascent = ascent;
word->size.descent = descent;
@@ -1371,8 +1536,15 @@ void Textblock::fillWord (Word *word, int width, int ascent, int descent,
word->content.space = false;
word->flags = flags;
+ removeWordImgRenderer (wordNo);
+ removeSpaceImgRenderer (wordNo);
+
word->style = style;
word->spaceStyle = style;
+
+ setWordImgRenderer (wordNo);
+ setSpaceImgRenderer (wordNo);
+
style->ref ();
style->ref ();
}
@@ -1852,7 +2024,7 @@ void Textblock::addSpace (core::style::Style *style)
{
int wordIndex = words->size () - 1;
if (wordIndex >= 0) {
- fillSpace (words->getRef(wordIndex), style);
+ fillSpace (wordIndex, style);
accumulateWordData (wordIndex);
correctLastWordExtremes ();
}
@@ -1874,7 +2046,7 @@ void Textblock::addBreakOption (core::style::Style *style, bool forceBreak)
}
}
-void Textblock::fillSpace (Word *word, core::style::Style *style)
+void Textblock::fillSpace (int wordNo, core::style::Style *style)
{
// Old comment:
//
@@ -1890,6 +2062,8 @@ void Textblock::fillSpace (Word *word, core::style::Style *style)
//
// TODO: Re-evaluate again.
+ Word *word = words->getRef (wordNo);
+
// TODO: This line does not work: addBreakOption (word, style);
// Do not override a previously set break penalty.
@@ -1907,9 +2081,14 @@ void Textblock::fillSpace (Word *word, core::style::Style *style)
//DBG_OBJ_ARRSET_NUM (this, "words.%d.content.space", wordIndex,
// word->content.space);
+
+ removeSpaceImgRenderer (wordNo);
+
word->spaceStyle->unref ();
word->spaceStyle = style;
style->ref ();
+
+ setSpaceImgRenderer (wordNo);
}
}
diff --git a/dw/textblock.hh b/dw/textblock.hh
index 7cf62f5c..b85937ba 100644
--- a/dw/textblock.hh
+++ b/dw/textblock.hh
@@ -237,6 +237,44 @@ private:
static const char *hyphenDrawChar;
protected:
+ /**
+ * \brief Implementation used for words.
+ */
+ class WordImgRenderer:
+ public core::style::StyleImage::ExternalWidgetImgRenderer
+ {
+ protected:
+ Textblock *textblock;
+ int wordNo, xWordWidget, lineNo;
+ bool dataSet;
+
+ public:
+ WordImgRenderer (Textblock *textblock, int wordNo);
+ ~WordImgRenderer ();
+
+ void setData (int xWordWidget, int lineNo);
+
+ bool readyToDraw ();
+ void getBgArea (int *x, int *y, int *width, int *height);
+ void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef);
+ core::style::Style *getStyle ();
+ void draw (int x, int y, int width, int height);
+
+ virtual void print ();
+ };
+
+ class SpaceImgRenderer: public WordImgRenderer
+ {
+ public:
+ inline SpaceImgRenderer (Textblock *textblock, int wordNo) :
+ WordImgRenderer (textblock, wordNo) { }
+
+ void getBgArea (int *x, int *y, int *width, int *height);
+ core::style::Style *getStyle ();
+
+ void print ();
+ };
+
struct Paragraph
{
int firstWord; /* first word's index in word vector */
@@ -350,6 +388,11 @@ protected:
core::style::Style *style;
core::style::Style *spaceStyle; /* initially the same as of the word,
later set by a_Dw_page_add_space */
+
+ // These two are used rarely, so there is perhaps a way to store
+ // them which is consuming less memory.
+ WordImgRenderer *wordImgRenderer;
+ SpaceImgRenderer *spaceImgRenderer;
};
void printWordShort (Word *word);
@@ -483,9 +526,14 @@ protected:
Word *addWord (int width, int ascent, int descent, short flags,
core::style::Style *style);
- void fillWord (Word *word, int width, int ascent, int descent,
+ void initWord (int wordNo);
+ void removeWordImgRenderer (int wordNo);
+ void setWordImgRenderer (int wordNo);
+ void removeSpaceImgRenderer (int wordNo);
+ void setSpaceImgRenderer (int wordNo);
+ void fillWord (int wordNo, int width, int ascent, int descent,
short flags, core::style::Style *style);
- void fillSpace (Word *word, core::style::Style *style);
+ 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);
diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc
index 226dc3c7..c07dc602 100644
--- a/dw/textblock_linebreaking.cc
+++ b/dw/textblock_linebreaking.cc
@@ -420,6 +420,16 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
//printf (": ");
//words->getRef(line->lastWord)->badnessAndPenalty.print ();
//printf ("\n");
+
+ int xWidget = lineXOffsetWidget(line);
+ for (int i = firstWord; i <= lastWord; i++) {
+ Word *word = words->getRef (i);
+ if (word->wordImgRenderer)
+ word->wordImgRenderer->setData (xWidget, lines->size () - 1);
+ if (word->spaceImgRenderer)
+ word->spaceImgRenderer->setData (xWidget, lines->size () - 1);
+ xWidget += word->size.width + word->effSpace;
+ }
return line;
}
@@ -872,6 +882,8 @@ int Textblock::hyphenateWord (int wordIndex)
PRINTF ("[%p] %d words ...\n", this, words->size ());
words->insert (wordIndex, numBreaks);
+ for (int i = 0; i < numBreaks; i++)
+ initWord (wordIndex + i);
PRINTF ("[%p] ... => %d words\n", this, words->size ());
// Adjust anchor indexes.
@@ -883,8 +895,7 @@ int Textblock::hyphenateWord (int wordIndex)
for (int i = 0; i < numBreaks + 1; i++) {
Word *w = words->getRef (wordIndex + i);
-
- fillWord (w, wordSize[i].width, wordSize[i].ascent,
+ fillWord (wordIndex + i, wordSize[i].width, wordSize[i].ascent,
wordSize[i].descent, false, origWord.style);
// TODO There should be a method fillText0.
@@ -923,7 +934,7 @@ int Textblock::hyphenateWord (int wordIndex)
PRINTF (" [%d] + hyphen\n", wordIndex + i);
} else {
if (origWord.content.space) {
- fillSpace (w, origWord.spaceStyle);
+ fillSpace (wordIndex + i, origWord.spaceStyle);
PRINTF (" [%d] + space\n", wordIndex + i);
} else {
PRINTF (" [%d] + nothing\n", wordIndex + i);
diff --git a/dw/widget.cc b/dw/widget.cc
index 9c6fe380..415da6bc 100644
--- a/dw/widget.cc
+++ b/dw/widget.cc
@@ -32,6 +32,36 @@ namespace core {
// ----------------------------------------------------------------------
+bool Widget::WidgetImgRenderer::readyToDraw ()
+{
+ return widget->wasAllocated ();
+}
+
+void Widget::WidgetImgRenderer::getBgArea (int *x, int *y, int *width,
+ int *height)
+{
+ widget->getPaddingArea (x, y, width, height);
+}
+
+void Widget::WidgetImgRenderer::getRefArea (int *xRef, int *yRef, int *widthRef,
+ int *heightRef)
+{
+ widget->getPaddingArea (xRef, yRef, widthRef, heightRef);
+}
+
+style::Style *Widget::WidgetImgRenderer::getStyle ()
+{
+ return widget->getStyle ();
+}
+
+void Widget::WidgetImgRenderer::draw (int x, int y, int width, int height)
+{
+ widget->queueDrawArea (x - widget->allocation.x, y - widget->allocation.y,
+ width, height);
+}
+
+// ----------------------------------------------------------------------
+
int Widget::CLASS_ID = -1;
Widget::Widget ()
@@ -55,6 +85,8 @@ Widget::Widget ()
deleteCallbackData = NULL;
deleteCallbackFunc = NULL;
+
+ widgetImgRenderer = NULL;
}
Widget::~Widget ()
@@ -62,6 +94,12 @@ Widget::~Widget ()
if (deleteCallbackFunc)
deleteCallbackFunc (deleteCallbackData);
+ if (widgetImgRenderer) {
+ if (style && style->backgroundImage)
+ style->backgroundImage->removeExternalImgRenderer (widgetImgRenderer);
+ delete widgetImgRenderer;
+ }
+
if (style)
style->unref ();
@@ -281,6 +319,10 @@ void Widget::setStyle (style::Style *style)
{
bool sizeChanged;
+ if (widgetImgRenderer && this->style && this->style->backgroundImage)
+ this->style->backgroundImage->removeExternalImgRenderer
+ (widgetImgRenderer);
+
style->ref ();
if (this->style) {
@@ -291,6 +333,15 @@ void Widget::setStyle (style::Style *style)
this->style = style;
+ if (style && style->backgroundImage) {
+ // Create instance of WidgetImgRenderer when needed. Until this
+ // widget is deleted, "widgetImgRenderer" will be kept, since it
+ // is not specific to the style, but only to this widget.
+ if (widgetImgRenderer == NULL)
+ widgetImgRenderer = new WidgetImgRenderer (this);
+ style->backgroundImage->putExternalImgRenderer (widgetImgRenderer);
+ }
+
if (layout != NULL) {
layout->updateCursor ();
}
@@ -340,20 +391,31 @@ style::Color *Widget::getBgColor ()
void Widget::drawBox (View *view, style::Style *style, Rectangle *area,
int x, int y, int width, int height, bool inverse)
{
- Rectangle viewArea;
- viewArea.x = area->x + allocation.x;
- viewArea.y = area->y + allocation.y;
- viewArea.width = area->width;
- viewArea.height = area->height;
+ Rectangle canvasArea;
+ canvasArea.x = area->x + allocation.x;
+ canvasArea.y = area->y + allocation.y;
+ canvasArea.width = area->width;
+ canvasArea.height = area->height;
- style::drawBorder (view, &viewArea, allocation.x + x, allocation.y + y,
+ style::drawBorder (view, layout, &canvasArea,
+ allocation.x + x, allocation.y + y,
width, height, style, inverse);
- /** \todo Background images? */
- if (style->backgroundColor)
- style::drawBackground (view, &viewArea,
- allocation.x + x, allocation.y + y, width, height,
- style, inverse);
+ // This method is used for inline elements, where the CSS 2 specification
+ // does not define what here is called "reference area". To make it look
+ // smoothly, the widget padding box is used.
+
+ int xPad, yPad, widthPad, heightPad;
+ getPaddingArea (&xPad, &yPad, &widthPad, &heightPad);
+ style::drawBackground
+ (view, layout, &canvasArea,
+ allocation.x + x + style->margin.left + style->borderWidth.left,
+ allocation.y + y + style->margin.top + style->borderWidth.top,
+ width - style->margin.left - style->borderWidth.left
+ - 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);
}
/**
@@ -364,32 +426,21 @@ void Widget::drawBox (View *view, style::Style *style, Rectangle *area,
*/
void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse)
{
- Rectangle viewArea;
- viewArea.x = area->x + allocation.x;
- viewArea.y = area->y + allocation.y;
- viewArea.width = area->width;
- viewArea.height = area->height;
+ Rectangle canvasArea;
+ canvasArea.x = area->x + allocation.x;
+ canvasArea.y = area->y + allocation.y;
+ canvasArea.width = area->width;
+ canvasArea.height = area->height;
- style::drawBorder (view, &viewArea, allocation.x, allocation.y,
+ style::drawBorder (view, layout, &canvasArea, allocation.x, allocation.y,
allocation.width, getHeight (), style, inverse);
- /** \todo Adjust following comment from the old dw sources. */
- /*
- * - Toplevel widget background colors are set as viewport
- * background color. This is not crucial for the rendering, but
- * looks a bit nicer when scrolling. Furthermore, the viewport
- * does anything else in this case.
- *
- * - Since widgets are always drawn from top to bottom, it is
- * *not* necessary to draw the background if
- * widget->style->background_color is NULL (shining through).
- */
- /** \todo Background images? */
-
- if (style->backgroundColor &&
- (parent || layout->getBgColor () != style->backgroundColor))
- style::drawBackground (view, &viewArea, allocation.x, allocation.y,
- allocation.width, getHeight (), style, inverse);
+ int xPad, yPad, widthPad, heightPad;
+ getPaddingArea (&xPad, &yPad, &widthPad, &heightPad);
+ style::drawBackground (view, layout, &canvasArea,
+ xPad, yPad, widthPad, heightPad,
+ xPad, yPad, widthPad, heightPad,
+ style, inverse, parent == NULL);
}
/*
@@ -533,6 +584,23 @@ void Widget::scrollTo (HPosition hpos, VPosition vpos,
x + allocation.x, y + allocation.y, width, height);
}
+/**
+ * \brief Return the padding area (content plus padding).
+ *
+ * Used as "reference area" (ee comment of "style::drawBackground")
+ * for backgrounds.
+ */
+void Widget::getPaddingArea (int *xPad, int *yPad, int *widthPad,
+ int *heightPad)
+{
+ *xPad = allocation.x + style->margin.left + style->borderWidth.left;
+ *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
+ - style->margin.bottom - style->borderWidth.bottom;
+}
+
void Widget::getExtremesImpl (Extremes *extremes)
{
/* Simply return the requisition width */
diff --git a/dw/widget.hh b/dw/widget.hh
index 6942221b..34b35efa 100644
--- a/dw/widget.hh
+++ b/dw/widget.hh
@@ -74,6 +74,28 @@ protected:
BLOCK_LEVEL = 1 << 6,
};
+ /**
+ * \brief Implementation which represents the whole widget.
+ *
+ * The only instance is set created needed.
+ */
+ class WidgetImgRenderer: public style::StyleImage::ExternalWidgetImgRenderer
+ {
+ private:
+ Widget *widget;
+
+ public:
+ inline WidgetImgRenderer (Widget *widget) { this->widget = widget; }
+
+ bool readyToDraw ();
+ void getBgArea (int *x, int *y, int *width, int *height);
+ void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef);
+ style::Style *getStyle ();
+ void draw (int x, int y, int width, int height);
+ };
+
+ WidgetImgRenderer *widgetImgRenderer;
+
private:
/**
* \brief The parent widget, NULL for top-level widgets.
@@ -289,6 +311,8 @@ public:
void scrollTo (HPosition hpos, VPosition vpos,
int x, int y, int width, int height);
+ void getPaddingArea (int *xPad, int *yPad, int *widthPad, int *heightPad);
+
/**
* \brief Return an iterator for this widget.
*
diff --git a/src/css.hh b/src/css.hh
index 6b142827..29f0b24a 100644
--- a/src/css.hh
+++ b/src/css.hh
@@ -45,6 +45,8 @@ typedef enum {
opposed to CSS_TYPE_ENUM and
CSS_TYPE_MULTI_ENUM). Used for
'font-family'. */
+ CSS_TYPE_URI, /* <uri> */
+ CSS_TYPE_BACKGROUND_POSITION,
CSS_TYPE_UNUSED /* Not yet used. Will itself get unused some
day. */
} CssValueType;
@@ -229,9 +231,15 @@ typedef enum {
CSS_PROPERTY_LAST
} CssPropertyName;
+typedef struct {
+ int32_t posX;
+ int32_t posY;
+} CssBackgroundPosition;
+
typedef union {
int32_t intVal;
char *strVal;
+ CssBackgroundPosition *posVal;
} CssPropertyValue;
typedef enum {
@@ -285,6 +293,8 @@ class CssProperty {
case CSS_TYPE_SYMBOL:
dFree (value.strVal);
break;
+ case CSS_TYPE_BACKGROUND_POSITION:
+ dFree (value.posVal);
default:
break;
}
diff --git a/src/cssparser.cc b/src/cssparser.cc
index eda45472..0d0ce5a1 100644
--- a/src/cssparser.cc
+++ b/src/cssparser.cc
@@ -47,6 +47,14 @@ typedef struct {
const char *const *enum_symbols;
} CssPropertyInfo;
+static const char *const Css_background_attachment_enum_vals[] = {
+ "scroll", "fixed", NULL
+};
+
+static const char *const Css_background_repeat_enum_vals[] = {
+ "repeat", "repeat-x", "repeat-y", "no-repeat", NULL
+};
+
static const char *const Css_border_collapse_enum_vals[] = {
"separate", "collapse", NULL
};
@@ -139,11 +147,14 @@ static const char *const Css_word_spacing_enum_vals[] = {
};
const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
- {"background-attachment", {CSS_TYPE_UNUSED}, NULL},
+ {"background-attachment", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_background_attachment_enum_vals},
{"background-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
- {"background-image", {CSS_TYPE_UNUSED}, NULL},
- {"background-position", {CSS_TYPE_UNUSED}, NULL},
- {"background-repeat", {CSS_TYPE_UNUSED}, NULL},
+ {"background-image", {CSS_TYPE_URI, CSS_TYPE_UNUSED}, NULL},
+ {"background-position", {CSS_TYPE_BACKGROUND_POSITION, CSS_TYPE_UNUSED},
+ NULL},
+ {"background-repeat", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_background_repeat_enum_vals},
{"border-bottom-color", {CSS_TYPE_ENUM, CSS_TYPE_COLOR, CSS_TYPE_UNUSED},
Css_border_color_enum_vals},
{"border-bottom-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
@@ -694,6 +705,15 @@ bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type)
}
break;
+ case CSS_TYPE_BACKGROUND_POSITION:
+ if (ttype == CSS_TK_SYMBOL &&
+ (dStrAsciiCasecmp(tval, "center") == 0 ||
+ dStrAsciiCasecmp(tval, "left") == 0 ||
+ dStrAsciiCasecmp(tval, "right") == 0 ||
+ dStrAsciiCasecmp(tval, "top") == 0 ||
+ dStrAsciiCasecmp(tval, "bottom") == 0))
+ return true;
+ // Fall Through (lenght and percentage)
case CSS_TYPE_LENGTH_PERCENTAGE:
case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER:
case CSS_TYPE_LENGTH:
@@ -734,6 +754,12 @@ bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type)
}
break;
+ case CSS_TYPE_URI:
+ if (ttype == CSS_TK_SYMBOL &&
+ dStrAsciiCasecmp(tval, "url") == 0)
+ return true;
+ break;
+
case CSS_TYPE_UNUSED:
case CSS_TYPE_INTEGER:
/* Not used for parser values. */
@@ -1004,6 +1030,107 @@ 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)
+ ret = true;
+ }
+ break;
+
+ case CSS_TYPE_BACKGROUND_POSITION:
+ // 'background-position' consists of one or two values: vertical and
+ // horizontal position; in most cases in this order. However, as long it
+ // is unambigous, the order can be switched: "10px left" and "left 10px"
+ // are both possible and have the same effect. For this reason, all
+ // possibilities are tested parrallel.
+
+ bool h[2], v[2];
+ int pos[2];
+ h[0] = v[0] = h[1] = v[1] = false;
+
+ // First: collect values in pos[0] and pos[1], and determine whether
+ // they can be used for a horizontal (h[i]) or vertical (v[i]) position
+ // (or both). When neither h[i] or v[i] is set, pos[i] is undefined.
+ for (i = 0; i < 2; i++) {
+ CssValueType typeTmp;
+ // tokenMatchesProperty will, for CSS_PROPERTY_BACKGROUND_POSITION,
+ // work on both parts, since they are exchangable.
+ if (tokenMatchesProperty (CSS_PROPERTY_BACKGROUND_POSITION,
+ &typeTmp)) {
+ h[i] = ttype != CSS_TK_SYMBOL ||
+ (dStrAsciiCasecmp(tval, "top") != 0 &&
+ dStrAsciiCasecmp(tval, "bottom") != 0);
+ v[i] = ttype != CSS_TK_SYMBOL ||
+ (dStrAsciiCasecmp(tval, "left") != 0 &&
+ dStrAsciiCasecmp(tval, "right") != 0);
+ } else
+ // No match.
+ h[i] = v[i] = false;
+
+ if (h[i] || v[i]) {
+ // Calculate values.
+ if (ttype == CSS_TK_SYMBOL) {
+ if (dStrAsciiCasecmp(tval, "top") == 0 ||
+ dStrAsciiCasecmp(tval, "left") == 0) {
+ pos[i] = CSS_CREATE_LENGTH (0.0, CSS_LENGTH_TYPE_PERCENTAGE);
+ nextToken();
+ } else if (dStrAsciiCasecmp(tval, "center") == 0) {
+ pos[i] = CSS_CREATE_LENGTH (0.5, CSS_LENGTH_TYPE_PERCENTAGE);
+ nextToken();
+ } else if (dStrAsciiCasecmp(tval, "bottom") == 0 ||
+ dStrAsciiCasecmp(tval, "right") == 0) {
+ pos[i] = CSS_CREATE_LENGTH (1.0, CSS_LENGTH_TYPE_PERCENTAGE);
+ nextToken();
+ } else
+ // tokenMatchesProperty should have returned "false" already.
+ lout::misc::assertNotReached ();
+ } else {
+ // We can assume <length> or <percentage> here ...
+ CssPropertyValue valTmp;
+ if (parseValue(prop, CSS_TYPE_LENGTH_PERCENTAGE, &valTmp)) {
+ pos[i] = valTmp.intVal;
+ ret = true;
+ } else
+ // ... but something may still fail.
+ h[i] = v[i] = false;
+ }
+ }
+
+ // If the first value cannot be read, do not read the second.
+ if (!h[i] && !v[i])
+ break;
+ }
+
+ // Second: Create the final value. Order will be determined here.
+ if (v[0] || h[0]) {
+ // If second value is not set, it is set to "center", i. e. 50%, (see
+ // CSS specification), which is suitable for both dimensions.
+ if (!h[1] && !v[1]) {
+ pos[1] = CSS_CREATE_LENGTH (0.5, CSS_LENGTH_TYPE_PERCENTAGE);
+ h[1] = v[1] = true;
+ }
+
+ // Only valid, when a combination h/v or v/h is possible.
+ if ((h[0] && v[1]) || (v[0] && h[1])) {
+ ret = true;
+ val->posVal = dNew(CssBackgroundPosition, 1);
+
+ // Prefer combination h/v:
+ if (h[0] && v[1]) {
+ val->posVal->posX = pos[0];
+ val->posVal->posY = pos[1];
+ } else {
+ // This should be v/h:
+ val->posVal->posX = pos[1];
+ val->posVal->posY = pos[0];
+ }
+ }
+ }
+ break;
+
case CSS_TYPE_UNUSED:
/* nothing */
break;
diff --git a/src/cssparser.hh b/src/cssparser.hh
index 8609877b..30d02eee 100644
--- a/src/cssparser.hh
+++ b/src/cssparser.hh
@@ -2,7 +2,8 @@
#define __CSSPARSER_HH__
#include "css.hh"
-#include "html_common.hh"
+
+class DilloHtml;
class CssParser {
private:
diff --git a/src/dicache.c b/src/dicache.c
index 55232846..ec63df1f 100644
--- a/src/dicache.c
+++ b/src/dicache.c
@@ -276,7 +276,8 @@ void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
/* BUG: there's just one image-type now */
#define I_RGB 0
- DicEntry->v_imgbuf = a_Imgbuf_new(Image->dw, I_RGB, width, height, gamma);
+ DicEntry->v_imgbuf =
+ a_Imgbuf_new(Image->layout, I_RGB, width, height, gamma);
DicEntry->TotalSize = width * height * 3;
DicEntry->width = width;
@@ -402,7 +403,7 @@ static void *Dicache_image(int ImgType, const char *MimeType, void *Ptr,
dReturn_val_if_fail(MimeType && Ptr, NULL);
if (!web->Image) {
- web->Image = a_Image_new(NULL, web->bgColor);
+ web->Image = a_Image_new(NULL, NULL, web->bgColor);
a_Image_ref(web->Image);
}
@@ -429,7 +430,7 @@ static void *Dicache_image(int ImgType, const char *MimeType, void *Ptr,
*Data = DicEntry->DecoderData;
*Call = (CA_Callback_t) a_Dicache_callback;
- return (web->Image->dw);
+ return (web->Image->img_rnd);
}
/*
@@ -516,9 +517,12 @@ void a_Dicache_callback(int Op, CacheClient_t *Client)
Image->ScanNumber = DicEntry->ScanNumber;
}
}
- } else if (Op == CA_Close || Op == CA_Abort) {
+ } else if (Op == CA_Close) {
a_Image_close(Image);
a_Bw_close_client(Web->bw, Client->Key);
+ } else if (Op == CA_Abort) {
+ a_Image_abort(Image);
+ a_Bw_close_client(Web->bw, Client->Key);
}
}
diff --git a/src/form.cc b/src/form.cc
index 8476b73b..b6df56f5 100644
--- a/src/form.cc
+++ b/src/form.cc
@@ -340,7 +340,7 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize)
char *charset, *first;
const char *attrbuf;
- HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (9, html->wordStyle ());
if (html->InFlags & IN_FORM) {
BUG_MSG("nested forms\n");
@@ -567,7 +567,7 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
html->styleEngine->setNonCssHint (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING,
attrbuf);
}
- HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle());
+ HT2TB(html)->addWidget (embed, html->backgroundStyle());
}
dFree(type);
dFree(name);
@@ -600,17 +600,17 @@ void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize)
html->charset);
html->InFlags |= IN_FORM;
- HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (9, html->wordStyle ());
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "prompt")))
- HT2TB(html)->addText(attrbuf, html->styleEngine->wordStyle ());
+ HT2TB(html)->addText(attrbuf, html->wordStyle ());
ResourceFactory *factory = HT2LT(html)->getResourceFactory();
EntryResource *entryResource = factory->createEntryResource (20,false,NULL);
embed = new Embed (entryResource);
Html_add_input(html, DILLO_HTML_INPUT_INDEX, embed, NULL, NULL, FALSE);
- HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
+ HT2TB(html)->addWidget (embed, html->backgroundStyle ());
a_Url_free(action);
html->InFlags &= ~IN_FORM;
@@ -675,7 +675,7 @@ void Html_tag_content_textarea(DilloHtml *html, const char *tag, int tagsize)
textres->setEditable(false);
Html_add_input(html, DILLO_HTML_INPUT_TEXTAREA, embed, name, NULL, false);
- HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
+ HT2TB(html)->addWidget (embed, html->backgroundStyle ());
dFree(name);
}
@@ -766,7 +766,7 @@ void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize)
html->styleEngine->setNonCssHint (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING,
attrbuf);
}
- HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
+ HT2TB(html)->addWidget (embed, html->backgroundStyle ());
Html_add_input(html, type, embed, name, NULL, false);
a_Html_stash_init(html);
@@ -938,16 +938,16 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize)
* but it caused 100% CPU usage.
*/
page = new Textblock (false);
- page->setStyle (html->styleEngine->backgroundStyle ());
+ page->setStyle (html->backgroundStyle ());
ResourceFactory *factory = HT2LT(html)->getResourceFactory();
Resource *resource = factory->createComplexButtonResource(page, true);
embed = new Embed(resource);
// a_Dw_button_set_sensitive (DW_BUTTON (button), FALSE);
- HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
- HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
- HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
+ 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;
@@ -2005,12 +2005,16 @@ static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize)
/* create new image and add it to the button */
a_Html_image_attrs(html, tag, tagsize);
if ((Image = a_Html_image_new(html, tag, tagsize))) {
- IM2DW(Image)->setStyle (html->styleEngine->backgroundStyle ());
+ // At this point, we know that Image->ir represents an image
+ // widget. Notice that the order of the casts matters, because
+ // of multiple inheritance.
+ dw::Image *dwi = (dw::Image*)(dw::core::ImgRenderer*)Image->img_rnd;
+ dwi->setStyle (html->backgroundStyle ());
ResourceFactory *factory = HT2LT(html)->getResourceFactory();
ComplexButtonResource *complex_b_r =
- factory->createComplexButtonResource(IM2DW(Image), false);
+ factory->createComplexButtonResource(dwi, false);
button = new Embed(complex_b_r);
- HT2TB(html)->addWidget (button, html->styleEngine->style ());
+ HT2TB(html)->addWidget (button, html->style ());
}
if (!button)
MSG("Html_input_image: unable to create image submit.\n");
diff --git a/src/html.cc b/src/html.cc
index 03bcb736..00882ccc 100644
--- a/src/html.cc
+++ b/src/html.cc
@@ -360,9 +360,9 @@ static void Html_add_textblock(DilloHtml *html, int space)
{
Textblock *textblock = new Textblock (prefs.limit_text_width);
- HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ());
- HT2TB(html)->addWidget (textblock, html->styleEngine->style ());
- HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (space, html->wordStyle ());
+ HT2TB(html)->addWidget (textblock, html->style ());
+ HT2TB(html)->addParbreak (space, html->wordStyle ());
S_TOP(html)->textblock = html->dw = textblock;
S_TOP(html)->hand_over_break = true;
}
@@ -1040,11 +1040,11 @@ static void Html_process_space_pre_line(DilloHtml *html, const char *space,
breakCnt++;
html->PrevWasCR = (space[i] == '\r');
- HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
+ HT2TB(html)->addLinebreak (html->wordStyle ());
}
}
if (breakCnt == 0) {
- HT2TB(html)->addSpace(html->styleEngine->wordStyle ());
+ HT2TB(html)->addSpace(html->wordStyle ());
}
}
@@ -1077,12 +1077,11 @@ static void Html_process_space(DilloHtml *html, const char *space,
if (spaceCnt) {
spc = dStrnfill(spaceCnt, ' ');
- HT2TB(html)->addText (spc, spaceCnt,
- html->styleEngine->wordStyle ());
+ HT2TB(html)->addText (spc, spaceCnt, html->wordStyle ());
dFree(spc);
spaceCnt = 0;
}
- HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
+ HT2TB(html)->addLinebreak (html->wordStyle ());
html->pre_column = 0;
}
html->PreFirstChar = false;
@@ -1110,20 +1109,19 @@ static void Html_process_space(DilloHtml *html, const char *space,
if (spaceCnt) {
// add break possibility for the white-space:pre-wrap case
- HT2TB(html)->addBreakOption (html->styleEngine->wordStyle (), false);
+ HT2TB(html)->addBreakOption (html->wordStyle (), false);
spc = dStrnfill(spaceCnt, ' ');
- HT2TB(html)->addText (spc, spaceCnt, html->styleEngine->wordStyle ());
+ HT2TB(html)->addText (spc, spaceCnt, html->wordStyle ());
dFree(spc);
}
} else {
if (SGML_SPCDEL) {
/* SGML_SPCDEL ignores white space immediately after an open tag */
- } else if (html->styleEngine->wordStyle ()->whiteSpace ==
- WHITE_SPACE_PRE_LINE) {
+ } else if (html->wordStyle ()->whiteSpace == WHITE_SPACE_PRE_LINE) {
Html_process_space_pre_line(html, space, spacesize);
} else {
- HT2TB(html)->addSpace(html->styleEngine->wordStyle ());
+ HT2TB(html)->addSpace(html->wordStyle ());
}
if (parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY)
@@ -1176,8 +1174,7 @@ static void Html_process_word(DilloHtml *html, const char *word, int size)
Html_process_space(html, Pword + start, i - start);
} else {
while (Pword[++i] && !isspace(Pword[i])) ;
- HT2TB(html)->addText(Pword + start, i - start,
- html->styleEngine->wordStyle ());
+ HT2TB(html)->addText(Pword + start, i - start, html->wordStyle ());
html->pre_column += i - start;
html->PreFirstChar = false;
}
@@ -1215,20 +1212,18 @@ static void Html_process_word(DilloHtml *html, const char *word, int size)
Html_process_space(html, word2 + start, i - start);
} else if (!strncmp(word2+i, utf8_zero_width_space, 3)) {
i += 3;
- HT2TB(html)->addBreakOption(html->styleEngine->wordStyle (), false);
+ HT2TB(html)->addBreakOption(html->wordStyle (), false);
} else if (a_Utf8_ideographic(word2+i, beyond_word2, &len)) {
i += len;
- HT2TB(html)->addText(word2 + start, i - start,
- html->styleEngine->wordStyle ());
- HT2TB(html)->addBreakOption(html->styleEngine->wordStyle (), false);
+ HT2TB(html)->addText(word2 + start, i - start, html->wordStyle ());
+ HT2TB(html)->addBreakOption(html->wordStyle (), false);
} else {
do {
i += len;
} while (word2[i] && !isspace(word2[i]) &&
strncmp(word2+i, utf8_zero_width_space, 3) &&
(!a_Utf8_ideographic(word2+i, beyond_word2, &len)));
- HT2TB(html)->addText(word2 + start, i - start,
- html->styleEngine->wordStyle ());
+ HT2TB(html)->addText(word2 + start, i - start, html->wordStyle ());
}
}
if (Pword == word2)
@@ -1262,7 +1257,7 @@ static void Html_eventually_pop_dw(DilloHtml *html, bool hand_over_break)
{
if (html->dw != S_TOP(html)->textblock) {
if (hand_over_break)
- HT2TB(html)->handOverBreak (html->styleEngine->style ());
+ HT2TB(html)->handOverBreak (html->style ());
HT2TB(html)->flush ();
html->dw = S_TOP(html)->textblock;
}
@@ -1290,7 +1285,7 @@ static void Html_push_tag(DilloHtml *html, int tag_idx)
*/
static void Html_force_push_tag(DilloHtml *html, int tag_idx)
{
- html->styleEngine->startElement (tag_idx);
+ html->startElement (tag_idx);
Html_push_tag(html, tag_idx);
}
@@ -1816,6 +1811,10 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
int32_t color;
int tag_index_a = a_Html_tag_index ("a");
style::Color *bgColor;
+ style::StyleImage *bgImage;
+ style::BackgroundRepeat bgRepeat;
+ style::BackgroundAttachment bgAttachment;
+ style::Length bgPositionX, bgPositionY;
_MSG("Html_tag_open_body Num_BODY=%d\n", html->Num_BODY);
if (!(html->InFlags & IN_BODY))
@@ -1856,7 +1855,7 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
CSS_TYPE_COLOR, color);
}
- html->styleEngine->restyle ();
+ html->restyle ();
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "link"))) {
html->non_css_link_color = a_Html_color_parse(html, attrbuf, -1);
@@ -1870,35 +1869,40 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
BUG_MSG("<body> vlink attribute is obsolete.\n");
}
- html->dw->setStyle (html->styleEngine->style ());
+ html->dw->setStyle (html->style ());
bgColor = html->styleEngine->backgroundColor ();
-
if (bgColor)
HT2LT(html)->setBgColor(bgColor);
+ bgImage = html->styleEngine->backgroundImage (&bgRepeat, &bgAttachment,
+ &bgPositionX, &bgPositionY);
+ if (bgImage)
+ HT2LT(html)->setBgImage(bgImage, bgRepeat, bgAttachment, bgPositionX,
+ bgPositionY);
+
/* Determine a color for visited links.
* This color is computed once per page and used for immediate feedback
* when clicking a link.
* On reload style including color for visited links is computed properly
* according to CSS.
*/
- html->styleEngine->startElement (tag_index_a);
+ html->startElement (tag_index_a);
html->styleEngine->setPseudoVisited ();
if (html->non_css_visited_color != -1) {
html->styleEngine->setNonCssHint (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR,
html->non_css_visited_color);
}
- html->visited_color = html->styleEngine->style ()->color->getColor ();
+ html->visited_color = html->style ()->color->getColor ();
html->styleEngine->endElement (tag_index_a);
if (prefs.contrast_visited_color) {
/* get a color that has a "safe distance" from text, link and bg */
html->visited_color =
a_Color_vc(html->visited_color,
- html->styleEngine->style ()->color->getColor(),
+ html->style ()->color->getColor(),
html->non_css_link_color,
- html->styleEngine->backgroundStyle()->backgroundColor->getColor());
+ html->backgroundStyle()->backgroundColor->getColor());
}
@@ -1970,28 +1974,28 @@ static void
src = dStrdup(attrbuf);
- textblock->addParbreak (5, html->styleEngine->wordStyle ());
+ textblock->addParbreak (5, html->wordStyle ());
bullet = new Bullet();
- textblock->addWidget(bullet, html->styleEngine->wordStyle ());
- textblock->addSpace(html->styleEngine->wordStyle ());
+ textblock->addWidget(bullet, html->wordStyle ());
+ textblock->addSpace(html->wordStyle ());
if (D_ASCII_TOLOWER(tag[1]) == 'i') {
/* IFRAME usually comes with very long advertising/spying URLS,
* to not break rendering we will force name="IFRAME" */
- textblock->addText ("IFRAME", html->styleEngine->wordStyle ());
+ textblock->addText ("IFRAME", html->wordStyle ());
} else {
/* FRAME:
* If 'name' tag is present use it, if not use 'src' value */
if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
- textblock->addText (src, html->styleEngine->wordStyle ());
+ textblock->addText (src, html->wordStyle ());
} else {
- textblock->addText (attrbuf, html->styleEngine->wordStyle ());
+ textblock->addText (attrbuf, html->wordStyle ());
}
}
- textblock->addParbreak (5, html->styleEngine->wordStyle ());
+ textblock->addParbreak (5, html->wordStyle ());
dFree(src);
}
@@ -2004,8 +2008,8 @@ static void
static void Html_tag_content_frameset (DilloHtml *html,
const char *tag, int tagsize)
{
- HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
- HT2TB(html)->addText("--FRAME--", html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (9, html->wordStyle ());
+ HT2TB(html)->addText("--FRAME--", html->wordStyle ());
Html_add_textblock(html, 5);
}
@@ -2026,7 +2030,7 @@ static void Html_tag_open_h(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_content_br(DilloHtml *html, const char *tag, int tagsize)
{
- HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
+ HT2TB(html)->addLinebreak (html->wordStyle ());
}
/*
@@ -2215,7 +2219,9 @@ DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, int tagsize)
alt_ptr = dStrdup("[IMG]"); // Place holder for img_off mode
}
- image = a_Image_new(alt_ptr, 0);
+ dw::Image *dw = new dw::Image(alt_ptr);
+ image =
+ a_Image_new(html->dw->getLayout(), (void*)(dw::core::ImgRenderer*)dw, 0);
if (HT2TB(html)->getBgColor())
image->bg_color = HT2TB(html)->getBgColor()->getColor();
@@ -2292,13 +2298,17 @@ static void Html_tag_content_img(DilloHtml *html, const char *tag, int tagsize)
/* TODO: usemap URLs outside of the document are not used. */
usemap_url = a_Html_url_new(html, attrbuf, NULL, 0);
- HT2TB(html)->addWidget((Widget*)Image->dw, html->styleEngine->style());
+ // At this point, we know that Image->ir represents an image
+ // widget. Notice that the order of the casts matters, because of
+ // multiple inheritance.
+ dw::Image *dwi = (dw::Image*)(dw::core::ImgRenderer*)Image->img_rnd;
+ HT2TB(html)->addWidget(dwi, html->style());
/* Image maps */
if (a_Html_get_attr(html, tag, tagsize, "ismap")) {
- ((::dw::Image*)Image->dw)->setIsMap();
+ dwi->setIsMap();
_MSG(" Html_tag_open_img: server-side map (ISMAP)\n");
- } else if (html->styleEngine->style ()->x_link != -1 &&
+ } else if (html->style ()->x_link != -1 &&
usemap_url == NULL) {
/* For simple links, we have to suppress the "image_pressed" signal.
* This is overridden for USEMAP images. */
@@ -2306,8 +2316,7 @@ static void Html_tag_content_img(DilloHtml *html, const char *tag, int tagsize)
}
if (usemap_url) {
- ((::dw::Image*)Image->dw)->setUseMap(&html->maps,
- new ::object::String(URL_STR(usemap_url)));
+ dwi->setUseMap(&html->maps, new ::object::String(URL_STR(usemap_url)));
a_Url_free (usemap_url);
}
}
@@ -2350,8 +2359,14 @@ static void Html_tag_close_map(DilloHtml *html)
for (int i = 0; i < html->images->size(); i++) {
DilloImage *img = html->images->get(i)->image;
- if (img)
- ((dw::Image*) img->dw)->forceMapRedraw();
+ if (img) {
+ // At this point, we know that img->ir represents an image
+ // widget. (Really? Is this assumtion safe?) Notice that the
+ // order of the casts matters, because of multiple
+ // inheritance.
+ dw::Image *dwi = (dw::Image*)(dw::core::ImgRenderer*)img->img_rnd;
+ dwi->forceMapRedraw();
+ }
}
html->InFlags &= ~IN_MAP;
}
@@ -2494,7 +2509,7 @@ static void Html_tag_open_object(DilloHtml *html, const char *tag, int tagsize)
html->styleEngine->setNonCssHint(PROPERTY_X_LINK, CSS_TYPE_INTEGER,
Html_set_new_link(html, &url));
- HT2TB(html)->addText("[OBJECT]", html->styleEngine->wordStyle ());
+ HT2TB(html)->addText("[OBJECT]", html->wordStyle ());
}
a_Url_free(base_url);
}
@@ -2528,7 +2543,7 @@ static const char* Html_get_javascript_link(DilloHtml *html)
static void Html_add_anchor(DilloHtml *html, const char *name)
{
_MSG("Registering ANCHOR: %s\n", name);
- if (!HT2TB(html)->addAnchor (name, html->styleEngine->style ()))
+ if (!HT2TB(html)->addAnchor (name, html->style ()))
BUG_MSG("Anchor names must be unique within the document ('%s')\n",name);
/*
* According to Sec. 12.2.1 of the HTML 4.01 spec, "anchor names that
@@ -2637,7 +2652,7 @@ static void Html_tag_open_q(DilloHtml *html, const char *tag, int tagsize)
const char *U201C = "\xe2\x80\x9c";
html->styleEngine->inheritBackgroundColor ();
- HT2TB(html)->addText (U201C, html->styleEngine->wordStyle ());
+ HT2TB(html)->addText (U201C, html->wordStyle ());
}
/*
@@ -2648,7 +2663,7 @@ static void Html_tag_close_q(DilloHtml *html)
/* Right Double Quotation Mark */
const char *U201D = "\xe2\x80\x9d";
- HT2TB(html)->addText (U201D, html->styleEngine->wordStyle ());
+ HT2TB(html)->addText (U201D, html->wordStyle ());
}
/*
@@ -2690,7 +2705,7 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_dir(DilloHtml *html, const char *tag, int tagsize)
{
html->styleEngine->inheritBackgroundColor ();
- HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (9, html->wordStyle ());
S_TOP(html)->list_type = HTML_LIST_UNORDERED;
S_TOP(html)->list_number = 0;
@@ -2750,7 +2765,7 @@ static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize)
{
- Style *style = html->styleEngine->style ();
+ Style *style = html->style ();
int *list_number;
const char *attrbuf;
@@ -2845,12 +2860,12 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_content_hr(DilloHtml *html, const char *tag, int tagsize)
{
Widget *hruler;
- HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (5, html->wordStyle ());
hruler = new Ruler();
- hruler->setStyle (html->styleEngine->style ());
- HT2TB(html)->addWidget (hruler, html->styleEngine->style ());
- HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
+ hruler->setStyle (html->style ());
+ HT2TB(html)->addWidget (hruler, html->style ());
+ HT2TB(html)->addParbreak (5, html->wordStyle ());
}
/*
@@ -2860,7 +2875,7 @@ static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize)
{
/* may want to actually do some stuff here. */
html->styleEngine->inheritBackgroundColor ();
- HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (9, html->wordStyle ());
}
/*
@@ -2869,7 +2884,7 @@ static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_dt(DilloHtml *html, const char *tag, int tagsize)
{
html->styleEngine->inheritBackgroundColor ();
- HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (9, html->wordStyle ());
}
/*
@@ -2886,7 +2901,7 @@ static void Html_tag_open_dd(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize)
{
html->styleEngine->inheritBackgroundColor ();
- HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (9, html->wordStyle ());
html->InFlags |= IN_PRE;
}
@@ -3218,7 +3233,7 @@ static void Html_tag_open_div(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_close_par(DilloHtml *html)
{
- HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (9, html->wordStyle ());
}
/*
@@ -3226,7 +3241,7 @@ static void Html_tag_close_par(DilloHtml *html)
*/
static void Html_tag_content_wbr(DilloHtml *html, const char *tag, int tagsize)
{
- HT2TB(html)->addBreakOption(html->styleEngine->wordStyle (), true);
+ HT2TB(html)->addBreakOption(html->wordStyle (), true);
}
@@ -3661,8 +3676,8 @@ static void Html_display_block(DilloHtml *html)
static void Html_display_listitem(DilloHtml *html)
{
- Style *style = html->styleEngine->style ();
- Style *wordStyle = html->styleEngine->wordStyle ();
+ Style *style = html->style ();
+ Style *wordStyle = html->wordStyle ();
Widget **ref_list_item;
ListItem *list_item;
int *list_number;
@@ -3746,7 +3761,7 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
/* Push the tag into the stack */
Html_push_tag(html, ni);
- html->styleEngine->startElement (ni);
+ html->startElement (ni);
_MSG("Open : %*s%s\n", html->stack->size(), " ", Tags[ni].name);
/* Parse attributes that can appear on any tag */
@@ -3757,7 +3772,7 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
Tags[ni].open (html, tag, tagsize);
if (! S_TOP(html)->display_none) {
- switch (html->styleEngine->style ()->display) {
+ switch (html->style ()->display) {
case DISPLAY_BLOCK:
Html_display_block(html);
break;
@@ -3784,8 +3799,8 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
if (S_TOP(html)->parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM) {
/* don't change anything */
} else if (S_TOP(html)->parse_mode != DILLO_HTML_PARSE_MODE_PRE &&
- (html->styleEngine->style ()->whiteSpace == WHITE_SPACE_PRE ||
- html->styleEngine->style ()->whiteSpace == WHITE_SPACE_PRE_WRAP)) {
+ (html->style ()->whiteSpace == WHITE_SPACE_PRE ||
+ html->style ()->whiteSpace == WHITE_SPACE_PRE_WRAP)) {
S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_PRE;
html->pre_column = 0;
html->PreFirstChar = true;
diff --git a/src/html_common.hh b/src/html_common.hh
index 6c5d4807..a43d91b7 100644
--- a/src/html_common.hh
+++ b/src/html_common.hh
@@ -218,6 +218,22 @@ public:
bool_t unloadedImages();
void loadImages (const DilloUrl *pattern);
void addCssUrl(const DilloUrl *url);
+
+ // useful shortcuts
+ inline void startElement (int tag)
+ { styleEngine->startElement (tag, bw, base_url); }
+ inline void startElement (const char *tagname)
+ { styleEngine->startElement (tagname, bw, base_url); }
+
+ inline dw::core::style::Style *backgroundStyle ()
+ { return styleEngine->backgroundStyle (bw, base_url); }
+ inline dw::core::style::Style *style ()
+ { return styleEngine->style (bw, base_url); }
+ inline dw::core::style::Style *wordStyle ()
+ { return styleEngine->wordStyle (bw, base_url); }
+
+ inline void restyle () { styleEngine->restyle (bw, base_url); }
+
};
/*
diff --git a/src/image.cc b/src/image.cc
index c499d977..04d89a82 100644
--- a/src/image.cc
+++ b/src/image.cc
@@ -23,19 +23,20 @@
using namespace dw::core;
-// Image to Object-Image macro
-#define I2DW(Image) ((dw::Image*)(Image->dw))
+// Image to Object-ImgRenderer macro
+#define I2IR(Image) ((dw::core::ImgRenderer*)(Image->img_rnd))
/*
* Create and initialize a new image structure.
*/
-DilloImage *a_Image_new(const char *alt_text, int32_t bg_color)
+DilloImage *a_Image_new(void *layout, void *img_rnd, int32_t bg_color)
{
DilloImage *Image;
Image = dNew(DilloImage, 1);
- Image->dw = (void*) new dw::Image(alt_text);
+ Image->layout = layout;
+ Image->img_rnd = img_rnd;
Image->width = 0;
Image->height = 0;
Image->bg_color = bg_color;
@@ -88,7 +89,7 @@ void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
_MSG("a_Image_set_parms: width=%d height=%d\n", width, height);
bool resize = (Image->width != width || Image->height != height);
- I2DW(Image)->setBuffer((Imgbuf*)v_imgbuf, resize);
+ I2IR(Image)->setBuffer((Imgbuf*)v_imgbuf, resize);
if (!Image->BitVec)
Image->BitVec = a_Bitvec_new(height);
@@ -106,7 +107,7 @@ void a_Image_write(DilloImage *Image, uint_t y)
dReturn_if_fail ( y < Image->height );
/* Update the row in DwImage */
- I2DW(Image)->drawRow(y);
+ I2IR(Image)->drawRow(y);
a_Bitvec_set_bit(Image->BitVec, y);
Image->State = IMG_Write;
}
@@ -117,5 +118,15 @@ void a_Image_write(DilloImage *Image, uint_t y)
void a_Image_close(DilloImage *Image)
{
_MSG("a_Image_close\n");
+ I2IR(Image)->finish();
+}
+
+/*
+ * Implement the abort method
+ */
+void a_Image_abort(DilloImage *Image)
+{
+ _MSG("a_Image_abort\n");
+ I2IR(Image)->fatal();
}
diff --git a/src/image.hh b/src/image.hh
index a66edaae..fd105a7e 100644
--- a/src/image.hh
+++ b/src/image.hh
@@ -44,7 +44,7 @@ typedef enum {
} ImageState;
struct _DilloImage {
- void *dw;
+ void *layout, *img_rnd;
/* Parameters as told by image data */
uint_t width;
@@ -62,7 +62,7 @@ struct _DilloImage {
/*
* Function prototypes
*/
-DilloImage *a_Image_new(const char *alt_text, int32_t bg_color);
+DilloImage *a_Image_new(void *layout, void *img_rnd, int32_t bg_color);
void a_Image_ref(DilloImage *Image);
void a_Image_unref(DilloImage *Image);
@@ -71,6 +71,7 @@ void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
DilloImgType type);
void a_Image_write(DilloImage *Image, uint_t y);
void a_Image_close(DilloImage *Image);
+void a_Image_abort(DilloImage *Image);
#ifdef __cplusplus
diff --git a/src/imgbuf.cc b/src/imgbuf.cc
index 16eb5c31..48e6bde5 100644
--- a/src/imgbuf.cc
+++ b/src/imgbuf.cc
@@ -90,10 +90,9 @@ void a_Imgbuf_unref(void *v_imgbuf)
/*
* Create a new Imgbuf
*/
-void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height,
+void *a_Imgbuf_new(void *layout, int img_type, uint_t width, uint_t height,
double gamma)
{
- Layout *layout = ((Widget*)v_dw)->getLayout();
if (!layout) {
MSG_ERR("a_Imgbuf_new: layout is NULL.\n");
exit(1);
@@ -104,7 +103,8 @@ void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height,
linebuf = (uchar_t*) dRealloc(linebuf, linebuf_size);
}
- return (void*)layout->createImgbuf(Imgbuf::RGB, width, height, gamma);
+ return (void*)((Layout*)layout)->createImgbuf(Imgbuf::RGB, width, height,
+ gamma);
}
/*
diff --git a/src/imgbuf.hh b/src/imgbuf.hh
index af0bf9a6..efa1b48c 100644
--- a/src/imgbuf.hh
+++ b/src/imgbuf.hh
@@ -16,7 +16,7 @@ extern "C" {
*/
void a_Imgbuf_ref(void *v_imgbuf);
void a_Imgbuf_unref(void *v_imgbuf);
-void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height,
+void *a_Imgbuf_new(void *v_ir, int img_type, uint_t width, uint_t height,
double gamma);
int a_Imgbuf_last_reference(void *v_imgbuf);
void a_Imgbuf_update(void *v_imgbuf, const uchar_t *buf, DilloImgType type,
diff --git a/src/plain.cc b/src/plain.cc
index d63ce6cf..d09f6d79 100644
--- a/src/plain.cc
+++ b/src/plain.cc
@@ -97,9 +97,10 @@ DilloPlain::DilloPlain(BrowserWindow *p_bw)
Layout *layout = (Layout*) bw->render_layout;
StyleEngine styleEngine (layout);
- styleEngine.startElement ("body");
- styleEngine.startElement ("pre");
- widgetStyle = styleEngine.wordStyle ();
+ // TODO (3x) No URL?
+ styleEngine.startElement ("body", bw, NULL);
+ styleEngine.startElement ("pre", bw, NULL);
+ widgetStyle = styleEngine.wordStyle (bw, NULL);
widgetStyle->ref ();
/* The context menu */
diff --git a/src/styleengine.cc b/src/styleengine.cc
index 47bf6240..5b3b34f0 100644
--- a/src/styleengine.cc
+++ b/src/styleengine.cc
@@ -15,10 +15,49 @@
#include "misc.h"
#include "html_common.hh"
#include "styleengine.hh"
+#include "web.hh"
+#include "capi.h"
using namespace lout::misc;
using namespace dw::core::style;
+/**
+ * Signal handler for "delete": This handles the case when an instance
+ * of StyleImage is deleted, possibly when the cache client is still
+ * active.
+ *
+ * \todo Not neccessary for dw::Image? (dw::Image also implements
+ * lout::signal::ObservedObject.)
+ */
+class StyleImageDeletionReceiver:
+ public lout::signal::ObservedObject::DeletionReceiver
+{
+ int clientKey;
+
+public:
+ StyleImageDeletionReceiver (int clientKey);
+ ~StyleImageDeletionReceiver ();
+
+ void deleted (lout::signal::ObservedObject *object);
+};
+
+StyleImageDeletionReceiver::StyleImageDeletionReceiver (int clientKey)
+{
+ this->clientKey = clientKey;
+}
+
+StyleImageDeletionReceiver::~StyleImageDeletionReceiver ()
+{
+}
+
+void StyleImageDeletionReceiver::deleted (lout::signal::ObservedObject *object)
+{
+ a_Capi_stop_client (clientKey, 0);
+ delete this;
+}
+
+// ----------------------------------------------------------------------
+
StyleEngine::StyleEngine (dw::core::Layout *layout) {
StyleAttrs style_attrs;
FontAttrs font_attrs;
@@ -92,8 +131,8 @@ void StyleEngine::stackPop () {
/**
* \brief tell the styleEngine that a new html element has started.
*/
-void StyleEngine::startElement (int element) {
- style (); // ensure that style of current node is computed
+void StyleEngine::startElement (int element, BrowserWindow *bw, DilloUrl *url) {
+ style (bw, url); // ensure that style of current node is computed
stackPush ();
Node *n = stack->getLastRef ();
@@ -103,8 +142,9 @@ void StyleEngine::startElement (int element) {
n->doctreeNode = dn;
}
-void StyleEngine::startElement (const char *tagname) {
- startElement (a_Html_tag_index (tagname));
+void StyleEngine::startElement (const char *tagname, BrowserWindow *bw,
+ DilloUrl *url) {
+ startElement (a_Html_tag_index (tagname), bw, url);
}
void StyleEngine::setId (const char *id) {
@@ -207,6 +247,26 @@ dw::core::style::Color *StyleEngine::backgroundColor () {
return NULL;
}
+dw::core::style::StyleImage *StyleEngine::backgroundImage
+ (dw::core::style::BackgroundRepeat *bgRepeat,
+ dw::core::style::BackgroundAttachment *bgAttachment,
+ dw::core::style::Length *bgPositionX,
+ dw::core::style::Length *bgPositionY) {
+ for (int i = 1; i < stack->size (); i++) {
+ Node *n = stack->getRef (i);
+
+ if (n->style && n->style->backgroundImage) {
+ *bgRepeat = n->style->backgroundRepeat;
+ *bgAttachment = n->style->backgroundAttachment;
+ *bgPositionX = n->style->backgroundPositionX;
+ *bgPositionY = n->style->backgroundPositionY;
+ return n->style->backgroundImage;
+ }
+ }
+
+ return NULL;
+}
+
/**
* \brief set the CSS pseudo class :link.
*/
@@ -238,6 +298,16 @@ void StyleEngine::preprocessAttrs (dw::core::style::StyleAttrs *attrs) {
if (stack->getRef (stack->size () - 2)->inheritBackgroundColor) {
attrs->backgroundColor =
stack->getRef (stack->size () - 2)->style->backgroundColor;
+ attrs->backgroundImage =
+ stack->getRef (stack->size () - 2)->style->backgroundImage;
+ attrs->backgroundRepeat =
+ stack->getRef (stack->size () - 2)->style->backgroundRepeat;
+ attrs->backgroundAttachment =
+ stack->getRef (stack->size () - 2)->style->backgroundAttachment;
+ attrs->backgroundPositionX =
+ stack->getRef (stack->size () - 2)->style->backgroundPositionX;
+ attrs->backgroundPositionY =
+ stack->getRef (stack->size () - 2)->style->backgroundPositionY;
attrs->valign = stack->getRef (stack->size () - 2)->style->valign;
}
@@ -281,7 +351,8 @@ void StyleEngine::postprocessAttrs (dw::core::style::StyleAttrs *attrs) {
/**
* \brief Make changes to StyleAttrs attrs according to CssPropertyList props.
*/
-void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) {
+void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props,
+ BrowserWindow *bw, DilloUrl *url) {
FontAttrs fontAttrs = *attrs->font;
Font *parentFont = stack->get (i - 1).style->font;
char *c, *fontName;
@@ -441,6 +512,10 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) {
switch (p->name) {
/* \todo missing cases */
+ case CSS_PROPERTY_BACKGROUND_ATTACHMENT:
+ attrs->backgroundAttachment =
+ (BackgroundAttachment) p->value.intVal;
+ break;
case CSS_PROPERTY_BACKGROUND_COLOR:
if (prefs.allow_white_bg || p->value.intVal != 0xffffff)
attrs->backgroundColor = Color::create(layout, p->value.intVal);
@@ -448,6 +523,41 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) {
attrs->backgroundColor =
Color::create(layout, prefs.white_bg_replacement);
break;
+ case CSS_PROPERTY_BACKGROUND_IMAGE:
+ {
+ DilloUrl *imgUrl =
+ a_Url_new (p->value.strVal, a_Url_str (url));
+
+ attrs->backgroundImage = StyleImage::create();
+ DilloImage *image =
+ a_Image_new(layout,
+ (void*)attrs->backgroundImage
+ ->getMainImgRenderer(),
+ 0xffffff);
+
+ DilloWeb *web = a_Web_new(bw, imgUrl, url);
+ web->Image = image;
+ a_Image_ref(image);
+ web->flags |= WEB_Image;
+
+ int clientKey;
+ if ((clientKey = a_Capi_open_url(web, NULL, NULL)) != 0) {
+ a_Bw_add_client(bw, clientKey, 0);
+ a_Bw_add_url(bw, url);
+ attrs->backgroundImage->connectDeletion
+ (new StyleImageDeletionReceiver (clientKey));
+ }
+ }
+ break;
+ case CSS_PROPERTY_BACKGROUND_POSITION:
+ computeLength (&attrs->backgroundPositionX, p->value.posVal->posX,
+ attrs->font);
+ computeLength (&attrs->backgroundPositionY, p->value.posVal->posY,
+ attrs->font);
+ break;
+ case CSS_PROPERTY_BACKGROUND_REPEAT:
+ attrs->backgroundRepeat = (BackgroundRepeat) p->value.intVal;
+ break;
case CSS_PROPERTY_BORDER_COLLAPSE:
attrs->borderCollapse = (BorderCollapse) p->value.intVal;
break;
@@ -706,9 +816,9 @@ void StyleEngine::computeBorderWidth (int *dest, CssProperty *p,
* A normal style might have backgroundColor == NULL to indicate a transparent
* background. This method ensures that backgroundColor is set.
*/
-Style * StyleEngine::backgroundStyle () {
+Style * StyleEngine::backgroundStyle (BrowserWindow *bw, DilloUrl *url) {
if (!stack->getRef (stack->size () - 1)->backgroundStyle) {
- StyleAttrs attrs = *style ();
+ StyleAttrs attrs = *style (bw, url);
for (int i = stack->size () - 1; i >= 0 && ! attrs.backgroundColor; i--)
attrs.backgroundColor = stack->getRef (i)->style->backgroundColor;
@@ -725,7 +835,7 @@ Style * StyleEngine::backgroundStyle () {
* HTML elements and the nonCssProperties that have been set.
* This method is private. Call style() to get a current style object.
*/
-Style * StyleEngine::style0 (int i) {
+Style * StyleEngine::style0 (int i, BrowserWindow *bw, DilloUrl *url) {
CssPropertyList props, *styleAttrProperties, *styleAttrPropertiesImportant;
CssPropertyList *nonCssProperties;
// get previous style from the stack
@@ -753,7 +863,7 @@ Style * StyleEngine::style0 (int i) {
nonCssProperties);
// apply style
- apply (i, &attrs, &props);
+ apply (i, &attrs, &props, bw, url);
postprocessAttrs (&attrs);
@@ -762,14 +872,20 @@ Style * StyleEngine::style0 (int i) {
return stack->getRef (i)->style;
}
-Style * StyleEngine::wordStyle0 () {
- StyleAttrs attrs = *style ();
+Style * StyleEngine::wordStyle0 (BrowserWindow *bw, DilloUrl *url) {
+ StyleAttrs attrs = *style (bw, url);
attrs.resetValues ();
- if (stack->getRef (stack->size() - 1)->inheritBackgroundColor)
- attrs.backgroundColor = style ()->backgroundColor;
+ if (stack->getRef (stack->size() - 1)->inheritBackgroundColor) {
+ attrs.backgroundColor = style (bw, url)->backgroundColor;
+ attrs.backgroundImage = style (bw, url)->backgroundImage;
+ attrs.backgroundRepeat = style (bw, url)->backgroundRepeat;
+ attrs.backgroundAttachment = style (bw, url)->backgroundAttachment;
+ attrs.backgroundPositionX = style (bw, url)->backgroundPositionX;
+ attrs.backgroundPositionY = style (bw, url)->backgroundPositionY;
+ }
- attrs.valign = style ()->valign;
+ attrs.valign = style (bw, url)->valign;
stack->getRef(stack->size() - 1)->wordStyle = Style::create(&attrs);
return stack->getRef (stack->size () - 1)->wordStyle;
@@ -782,7 +898,7 @@ Style * StyleEngine::wordStyle0 () {
* and thereby after the HTML-element has been opened.
* Note that restyle() does not change any styles in the widget tree.
*/
-void StyleEngine::restyle () {
+void StyleEngine::restyle (BrowserWindow *bw, DilloUrl *url) {
for (int i = 1; i < stack->size (); i++) {
Node *n = stack->getRef (i);
if (n->style) {
@@ -798,7 +914,7 @@ void StyleEngine::restyle () {
n->backgroundStyle = NULL;
}
- style0 (i);
+ style0 (i, bw, url);
}
}
diff --git a/src/styleengine.hh b/src/styleengine.hh
index 237008a6..714553ff 100644
--- a/src/styleengine.hh
+++ b/src/styleengine.hh
@@ -40,8 +40,8 @@ class StyleEngine {
void stackPop ();
void buildUserAgentStyle ();
void buildUserStyle ();
- dw::core::style::Style *style0 (int i);
- dw::core::style::Style *wordStyle0 ();
+ dw::core::style::Style *style0 (int i, BrowserWindow *bw, DilloUrl *url);
+ dw::core::style::Style *wordStyle0 (BrowserWindow *bw, DilloUrl *url);
inline void setNonCssHint(CssPropertyName name, CssValueType type,
CssPropertyValue value) {
Node *n = stack->getRef (stack->size () - 1);
@@ -52,7 +52,8 @@ class StyleEngine {
}
void preprocessAttrs (dw::core::style::StyleAttrs *attrs);
void postprocessAttrs (dw::core::style::StyleAttrs *attrs);
- void apply (int i, dw::core::style::StyleAttrs *attrs, CssPropertyList *props);
+ void apply (int i, dw::core::style::StyleAttrs *attrs,
+ CssPropertyList *props, BrowserWindow *bw, DilloUrl *url);
bool computeValue (int *dest, CssLength value,
dw::core::style::Font *font);
bool computeValue (int *dest, CssLength value,
@@ -68,8 +69,8 @@ class StyleEngine {
void parse (DilloHtml *html, DilloUrl *url, const char *buf, int buflen,
CssOrigin origin);
- void startElement (int tag);
- void startElement (const char *tagname);
+ void startElement (int tag, BrowserWindow *bw, DilloUrl *url);
+ void startElement (const char *tagname, BrowserWindow *bw, DilloUrl *url);
void setId (const char *id);
const char * getId () { return doctree->top ()->id; };
void setClass (const char *klass);
@@ -91,25 +92,32 @@ class StyleEngine {
}
void inheritNonCssHints ();
void clearNonCssHints ();
- void restyle ();
+ void restyle (BrowserWindow *bw, DilloUrl *url);
void inheritBackgroundColor (); /* \todo get rid of this somehow */
- dw::core::style::Style *backgroundStyle ();
+ dw::core::style::Style *backgroundStyle (BrowserWindow *bw,
+ DilloUrl *url);
dw::core::style::Color *backgroundColor ();
+ dw::core::style::StyleImage *backgroundImage
+ (dw::core::style::BackgroundRepeat *bgRepeat,
+ dw::core::style::BackgroundAttachment *bgAttachment,
+ dw::core::style::Length *bgPositionX,
+ dw::core::style::Length *bgPositionY);
- inline dw::core::style::Style *style () {
+ inline dw::core::style::Style *style (BrowserWindow *bw, DilloUrl *url) {
dw::core::style::Style *s = stack->getRef (stack->size () - 1)->style;
if (s)
return s;
else
- return style0 (stack->size () - 1);
+ return style0 (stack->size () - 1, bw, url);
};
- inline dw::core::style::Style *wordStyle () {
+ inline dw::core::style::Style *wordStyle (BrowserWindow *bw,
+ DilloUrl *url) {
dw::core::style::Style *s = stack->getRef(stack->size()-1)->wordStyle;
if (s)
return s;
else
- return wordStyle0 ();
+ return wordStyle0 (bw, url);
};
};
diff --git a/src/table.cc b/src/table.cc
index 15200516..a3002ebf 100644
--- a/src/table.cc
+++ b/src/table.cc
@@ -114,7 +114,7 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
BUG_MSG("<table> bgcolor attribute is obsolete.\n");
}
- html->styleEngine->style (); // evaluate now, so we can build non-css hints for the cells
+ html->style (); // evaluate now, so we can build non-css hints for the cells
/* The style for the cells */
html->styleEngine->clearNonCssHints ();
@@ -155,10 +155,10 @@ void Html_tag_content_table(DilloHtml *html, const char *tag, int tagsize)
{
dw::core::Widget *table;
- HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
+ HT2TB(html)->addParbreak (0, html->wordStyle ());
table = new dw::Table(prefs.limit_text_width);
- HT2TB(html)->addWidget (table, html->styleEngine->style ());
- HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
+ HT2TB(html)->addWidget (table, html->style ());
+ HT2TB(html)->addParbreak (0, html->wordStyle ());
S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TOP;
S_TOP(html)->table_border_mode = DILLO_HTML_TABLE_BORDER_SEPARATE;
@@ -221,7 +221,7 @@ void Html_tag_content_tr(DilloHtml *html, const char *tag, int tagsize)
case DILLO_HTML_TABLE_MODE_TOP:
case DILLO_HTML_TABLE_MODE_TR:
case DILLO_HTML_TABLE_MODE_TD:
- ((dw::Table*)S_TOP(html)->table)->addRow (html->styleEngine->style ());
+ ((dw::Table*)S_TOP(html)->table)->addRow (html->style ());
default:
break;
}
@@ -302,10 +302,10 @@ static void Html_set_collapsing_border_model(DilloHtml *html, Widget *col_tb)
int borderWidth, marginWidth;
tableStyle = ((dw::Table*)S_TOP(html)->table)->getStyle ();
- borderWidth = html->styleEngine->style ()->borderWidth.top;
+ borderWidth = html->style ()->borderWidth.top;
marginWidth = tableStyle->margin.top;
- collapseCellAttrs = *(html->styleEngine->style ());
+ collapseCellAttrs = *(html->style ());
collapseCellAttrs.margin.setVal (0);
collapseCellAttrs.borderWidth.left = 0;
collapseCellAttrs.borderWidth.top = 0;
@@ -344,7 +344,7 @@ static void Html_set_separate_border_model(DilloHtml *html, Widget *col_tb)
dw::core::style::Style *separateStyle;
dw::core::style::StyleAttrs separateCellAttrs;
- separateCellAttrs = *(html->styleEngine->style ());
+ separateCellAttrs = *(html->style ());
/* CSS2 17.5: Internal table elements do not have margins */
separateCellAttrs.margin.setVal (0);
separateStyle = Style::create(&separateCellAttrs);
@@ -443,7 +443,7 @@ static void Html_tag_content_table_cell(DilloHtml *html,
/* TODO: check errors? */
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rowspan")))
rowspan = MAX(1, strtol (attrbuf, NULL, 10));
- if (html->styleEngine->style ()->textAlign
+ if (html->style ()->textAlign
== TEXT_ALIGN_STRING)
col_tb = new dw::TableCell (
((dw::Table*)S_TOP(html)->table)->getCellRef (),
@@ -451,7 +451,7 @@ static void Html_tag_content_table_cell(DilloHtml *html,
else
col_tb = new Textblock (prefs.limit_text_width);
- if (html->styleEngine->style()->borderCollapse == BORDER_MODEL_COLLAPSE){
+ if (html->style()->borderCollapse == BORDER_MODEL_COLLAPSE){
Html_set_collapsing_border_model(html, col_tb);
} else {
Html_set_separate_border_model(html, col_tb);
diff --git a/src/uicmd.cc b/src/uicmd.cc
index 84e7e4cd..4f737255 100644
--- a/src/uicmd.cc
+++ b/src/uicmd.cc
@@ -583,6 +583,9 @@ static BrowserWindow *UIcmd_tab_new(CustTabs *tabs, UI *old_ui, int focus)
Layout *layout = new Layout (platform);
style::Color *bgColor = style::Color::create (layout, prefs.bg_color);
layout->setBgColor (bgColor);
+ layout->setBgImage (NULL, style::BACKGROUND_REPEAT,
+ style::BACKGROUND_ATTACHMENT_SCROLL,
+ style::createPerLength (0), style::createPerLength (0));
// set_render_layout() sets the proper viewport size
FltkViewport *viewport = new FltkViewport (0, 0, 0, 1);
diff --git a/src/web.cc b/src/web.cc
index fcc65af8..0a8ee710 100644
--- a/src/web.cc
+++ b/src/web.cc
@@ -67,16 +67,20 @@ int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web,
style::Color *bgColor = style::Color::create (layout, prefs.bg_color);
Web->bgColor = bgColor->getColor ();
layout->setBgColor (bgColor);
+ layout->setBgImage (NULL, style::BACKGROUND_REPEAT,
+ style::BACKGROUND_ATTACHMENT_SCROLL,
+ style::createPerLength (0),
+ style::createPerLength (0));
/* Set a style for the widget */
StyleEngine styleEngine (layout);
- styleEngine.startElement ("body");
+ styleEngine.startElement ("body", Web->bw, Web->url);
dw = (Widget*) viewer(Type, Web, Call, Data);
if (dw == NULL)
return -1;
- dw->setStyle (styleEngine.style ());
+ dw->setStyle (styleEngine.style (Web->bw, Web->url));
/* This method frees the old dw if any */
layout->setWidget(dw);
diff --git a/test/Makefile.am b/test/Makefile.am
index 6a834acb..156c8667 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -9,6 +9,7 @@ noinst_PROGRAMS = \
dw-find-test \
dw-links \
dw-links2 \
+ dw-image-background \
dw-images-simple \
dw-images-scaled \
dw-images-scaled2 \
@@ -67,6 +68,14 @@ dw_links2_LDADD = \
$(top_builddir)/lout/liblout.a \
@LIBFLTK_LIBS@
+dw_image_background_SOURCES = dw_image_background.cc
+dw_image_background_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_images_simple_SOURCES = dw_images_simple.cc
dw_images_simple_LDADD = \
$(top_builddir)/dw/libDw-widgets.a \
diff --git a/test/dw_image_background.cc b/test/dw_image_background.cc
new file mode 100644
index 00000000..ad50924d
--- /dev/null
+++ b/test/dw_image_background.cc
@@ -0,0 +1,196 @@
+/*
+ * 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 <stdlib.h>
+
+#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"
+#include "../dw/image.hh"
+
+using namespace lout::signal;
+using namespace lout::misc;
+using namespace dw;
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace dw::fltk;
+
+class ImageStyleDeletionReceiver: public ObservedObject::DeletionReceiver
+{
+public:
+ void deleted (ObservedObject *object);
+};
+
+static StyleImage *image1 = NULL, *image2 = NULL;
+static Layout *layout;
+static int imgRow1 = 0, imgRow2 = 0;
+static ImageStyleDeletionReceiver isdr;
+
+void ImageStyleDeletionReceiver::deleted (ObservedObject *object)
+{
+ if ((StyleImage*)object == image1)
+ image1 = NULL;
+ else if ((StyleImage*)object == image2)
+ image2 = NULL;
+ else
+ assertNotReached ();
+}
+
+static void imageInitTimeout (void *data)
+{
+ if (image1) {
+ Imgbuf *imgbuf1 = layout->createImgbuf (Imgbuf::RGB, 400, 200, 1);
+ image1->getMainImgRenderer()->setBuffer (imgbuf1, false);
+ }
+
+ if (image2) {
+ Imgbuf *imgbuf2 = layout->createImgbuf (Imgbuf::RGB, 100, 100, 1);
+ image2->getMainImgRenderer()->setBuffer (imgbuf2, false);
+ }
+}
+
+static void imageDrawTimeout (void *data)
+{
+ Imgbuf *imgbuf1 = image1 ? image1->getImgbuf () : NULL;
+ Imgbuf *imgbuf2 = image2 ? image2->getImgbuf () : NULL;
+
+ if (imgbuf1 && imgRow1 < 200) {
+ byte buf[3 * 400];
+ for(int x = 0; x < 400; x++) {
+ buf[3 * x + 0] = 128 + x * 127 / 399;
+ buf[3 * x + 1] = 128 + (399 - x) * 127 / 399;
+ buf[3 * x + 2] = 128 + imgRow1 * 127 / 199;
+ }
+
+ imgbuf1->copyRow (imgRow1, buf);
+ image1->getMainImgRenderer()->drawRow (imgRow1);
+ imgRow1++;
+ }
+
+ if (imgbuf2 && imgRow2 < 100) {
+ byte buf[3 * 100];
+ for(int x = 0; x < 100; x++) {
+ int r = 128 + rand () % 127;
+ buf[3 * x + 0] = buf[3 * x + 1] = buf[3 * x + 2] = r;
+ }
+
+ imgbuf2->copyRow (imgRow2, buf);
+ image2->getMainImgRenderer()->drawRow (imgRow2);
+ imgRow2++;
+ }
+
+ if(imgRow1 < 200 || imgRow2 < 100)
+ Fl::repeat_timeout (0.5, imageDrawTimeout, NULL);
+}
+
+int main(int argc, char **argv)
+{
+ FltkPlatform *platform = new FltkPlatform ();
+ 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);
+
+ image1 = StyleImage::create ();
+ image1->connectDeletion (&isdr);
+ layout->setBgImage (image1, BACKGROUND_REPEAT_Y,
+ BACKGROUND_ATTACHMENT_SCROLL, createPerLength (0.5),
+ createAbsLength (30));
+
+ StyleAttrs styleAttrs;
+ styleAttrs.initValues ();
+ styleAttrs.margin.setVal (5);
+ styleAttrs.x_lang[0] = 'e';
+ styleAttrs.x_lang[1] = 'n';
+
+ 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 *widgetStyle = Style::create (&styleAttrs);
+
+ Textblock *textblock = new Textblock (false);
+ textblock->setStyle (widgetStyle);
+ layout->setWidget (textblock);
+
+ widgetStyle->unref();
+
+ styleAttrs.margin.setVal (0);
+ styleAttrs.backgroundColor = NULL;
+ styleAttrs.backgroundImage = NULL;
+
+ Style *wordStyle = Style::create (&styleAttrs);
+
+ image2 = styleAttrs.backgroundImage = StyleImage::create ();
+ image2->connectDeletion (&isdr);
+ styleAttrs.backgroundRepeat = BACKGROUND_REPEAT;
+ styleAttrs.backgroundPositionX = createPerLength (0);
+ styleAttrs.backgroundPositionY = createPerLength (0);
+ Style *wordStyleBg = Style::create (&styleAttrs);
+
+ for(int i = 1; i <= 1; i++) {
+ char buf[4];
+ sprintf(buf, "%d.", i);
+
+ 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], j == 11 ? wordStyleBg : wordStyle);
+ textblock->addSpace(wordStyle);
+ }
+
+ textblock->addParbreak(10, wordStyle);
+ }
+
+ wordStyle->unref();
+ wordStyleBg->unref();
+
+ textblock->flush ();
+
+ window->resizable(viewport);
+ window->show();
+
+ Fl::add_timeout (1.0, imageInitTimeout, NULL);
+ Fl::add_timeout (0.1, imageDrawTimeout, NULL);
+
+ int errorCode = Fl::run();
+
+ delete layout;
+
+ return errorCode;
+}