aboutsummaryrefslogtreecommitdiff
path: root/dw
diff options
context:
space:
mode:
Diffstat (limited to 'dw')
-rw-r--r--dw/Makefile.am2
-rw-r--r--dw/core.hh1
-rw-r--r--dw/fltkimgbuf.cc31
-rw-r--r--dw/fltkimgbuf.hh3
-rw-r--r--dw/image.cc10
-rw-r--r--dw/image.hh5
-rw-r--r--dw/imgbuf.hh13
-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.cc460
-rw-r--r--dw/style.hh156
-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
18 files changed, 1312 insertions, 73 deletions
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/fltkimgbuf.cc b/dw/fltkimgbuf.cc
index 81d2dc2d..e526ad60 100644
--- a/dw/fltkimgbuf.cc
+++ b/dw/fltkimgbuf.cc
@@ -427,6 +427,37 @@ int FltkImgbuf::getRootHeight ()
return root ? root->height : height;
}
+core::Imgbuf *FltkImgbuf::createSimilarBuf (int width, int height)
+{
+ return new FltkImgbuf (type, width, height, gamma);
+}
+
+void FltkImgbuf::copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot,
+ int xSrc, int ySrc, int widthSrc, int heightSrc)
+{
+ FltkImgbuf *fDest = (FltkImgbuf*)dest;
+ assert (bpp == fDest->bpp);
+
+ int xSrc2 = lout::misc::min (xSrc + widthSrc, fDest->width - xDestRoot);
+ int ySrc2 = lout::misc::min (ySrc + heightSrc, fDest->height - yDestRoot);
+
+ //printf ("copying from (%d, %d), %d x %d to (%d, %d) (root) => "
+ // "xSrc2 = %d, ySrc2 = %d\n",
+ // xSrc, ySrc, widthSrc, heightSrc, xDestRoot, yDestRoot,
+ // xSrc2, ySrc2);
+
+ for (int x = xSrc; x < xSrc2; x++)
+ for (int y = ySrc; y < ySrc2; y++) {
+ int iSrc = x + width * y;
+ int iDest = xDestRoot + x + fDest->width * (yDestRoot + y);
+
+ //printf (" (%d, %d): %d -> %d\n", x, y, iSrc, iDest);
+
+ for (int b = 0; b < bpp; b++)
+ fDest->rawdata[bpp * iDest + b] = rawdata[bpp * iSrc + b];
+ }
+}
+
void FltkImgbuf::ref ()
{
refCount++;
diff --git a/dw/fltkimgbuf.hh b/dw/fltkimgbuf.hh
index ee5ae922..0b8b5549 100644
--- a/dw/fltkimgbuf.hh
+++ b/dw/fltkimgbuf.hh
@@ -73,6 +73,9 @@ public:
void getRowArea (int row, dw::core::Rectangle *area);
int getRootWidth ();
int getRootHeight ();
+ core::Imgbuf *createSimilarBuf (int width, int height);
+ void copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot,
+ int xSrc, int ySrc, int widthSrc, int heightSrc);
void ref ();
void unref ();
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/imgbuf.hh b/dw/imgbuf.hh
index 02ba9087..3ccbe3c5 100644
--- a/dw/imgbuf.hh
+++ b/dw/imgbuf.hh
@@ -178,6 +178,19 @@ public:
virtual int getRootWidth () = 0;
virtual int getRootHeight () = 0;
+
+ /**
+ * Creates an image buffer with same parameters (type, gamma etc.)
+ * except size.
+ */
+ virtual Imgbuf *createSimilarBuf (int width, int height) = 0;
+
+ /**
+ * Copies another image buffer into this image buffer.
+ */
+ virtual void copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot,
+ int xSrc, int ySrc, int widthSrc, int heightSrc) = 0;
+
/*
* Reference counting.
*/
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..89361881 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->getImgbufSrc() != 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..3b57ffcf 100644
--- a/dw/style.cc
+++ b/dw/style.cc
@@ -34,6 +34,27 @@ namespace dw {
namespace core {
namespace style {
+const bool drawBackgroundLineByLine = true;
+
+const int MIN_BG_IMG_W = 10;
+const int MIN_BG_IMG_H = 10;
+const int OPT_BG_IMG_W = 50;
+const int OPT_BG_IMG_H = 50;
+
+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 +69,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 +102,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 +147,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 +192,11 @@ int StyleAttrs::hashValue () {
textDecoration +
(intptr_t) color +
(intptr_t) backgroundColor +
+ (intptr_t) backgroundImage +
+ backgroundRepeat +
+ backgroundAttachment +
+ backgroundPositionX +
+ backgroundPositionY +
textAlign +
valign +
textAlignChar +
@@ -205,6 +246,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 +270,8 @@ Style::~Style ()
color->unref ();
if (backgroundColor)
backgroundColor->unref ();
+ if (backgroundImage)
+ backgroundImage->unref ();
if (borderColor.top)
borderColor.top->unref();
if (borderColor.bottom)
@@ -248,6 +293,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 +472,205 @@ Tooltip *Tooltip::create (Layout *layout, const char *text)
// ----------------------------------------------------------------------
+void StyleImage::StyleImgRenderer::setBuffer (core::Imgbuf *buffer, bool resize)
+{
+ if (image->imgbufSrc)
+ image->imgbufSrc->unref ();
+ if (image->imgbufTiled)
+ image->imgbufTiled->unref ();
+
+ image->imgbufTiled = NULL;
+
+ image->imgbufSrc = buffer;
+ if (image->imgbufSrc) {
+ image->imgbufSrc->ref ();
+
+ // If the image is too small, drawing a background will cause
+ // many calls of View::drawImgbuf. For this reason, we create
+ // another image buffer, the "tiled" image buffer, which is
+ // larger (the "optimal" size is defined as OPT_BG_IMG_W *
+ // OPT_BG_IMG_H) and contains the "source" buffer several times.
+
+ if (image->imgbufSrc->getRootWidth() * image->imgbufSrc->getRootHeight()
+ < MIN_BG_IMG_W * MIN_BG_IMG_H) {
+ image->tilesX =
+ misc::max (OPT_BG_IMG_W / image->imgbufSrc->getRootWidth(), 1);
+ image->tilesY =
+ misc::max (OPT_BG_IMG_H / image->imgbufSrc->getRootHeight(), 1);
+ image->imgbufTiled =
+ image->imgbufSrc->createSimilarBuf
+ (image->tilesX * image->imgbufSrc->getRootWidth(),
+ image->tilesY * image->imgbufSrc->getRootHeight());
+ }
+ }
+}
+
+void StyleImage::StyleImgRenderer::drawRow (int row)
+{
+ if (image->imgbufTiled) {
+ // A row of data has been copied to the source buffer, here it
+ // is copied into the tiled buffer.
+
+ // Unfortunately, this code may be called *after* some other
+ // implementations of ImgRenderer::drawRow, which actually
+ // *draw* the tiled buffer, which is so not up to date
+ // (ImgRendererDist does not define an order). OTOH, these
+ // drawing implementations calle Widget::queueResize, so the
+ // actual drawing (and so access to the tiled buffer) is done
+ // later.
+
+ int w = image->imgbufSrc->getRootWidth ();
+ int h = image->imgbufSrc->getRootHeight ();
+
+ for (int x = 0; x < image->tilesX; x++)
+ for (int y = 0; y < image->tilesX; y++)
+ image->imgbufSrc->copyTo (image->imgbufTiled, x * w, y * h,
+ 0, row, w, 1);
+ }
+}
+
+void StyleImage::StyleImgRenderer::finish ()
+{
+ // Nothing to do.
+}
+
+void StyleImage::StyleImgRenderer::fatal ()
+{
+ // Nothing to do.
+}
+
+StyleImage::StyleImage ()
+{
+ //printf ("new StyleImage %p\n", this);
+
+ refCount = 0;
+ imgbufSrc = NULL;
+ imgbufTiled = NULL;
+
+ imgRendererDist = new ImgRendererDist ();
+ styleImgRenderer = new StyleImgRenderer (this);
+ imgRendererDist->put (styleImgRenderer);
+}
+
+StyleImage::~StyleImage ()
+{
+ //printf ("delete StyleImage %p\n", this);
+
+ if (imgbufSrc)
+ imgbufSrc->unref ();
+ if (imgbufTiled)
+ imgbufTiled->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->getImgbufSrc();
+ 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 +1072,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 +1113,193 @@ 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->getImgbufSrc() != NULL) &&
+ // ... but for backgrounds, it would be rather complicated. To handle the
+ // two cases (normal HTML in a viewport, where the layout background
+ // image is set, and contents of <button> within a flat view, where the
+ // 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)
+{
+ //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) {
+ // Drawing is done with the "tiled" buffer, but all calculations
+ // before have been done with the "source" buffer.
+
+ Imgbuf *imgbufS = backgroundImage->getImgbufSrc();
+ int imgWidthS = imgbufS->getRootWidth ();
+ int imgHeightS = imgbufS->getRootHeight ();
+
+ Imgbuf *imgbufT = backgroundImage->getImgbufTiled();
+ int imgWidthT = imgbufT->getRootWidth ();
+ int imgHeightT = imgbufT->getRootHeight ();
+ int tilesX = backgroundImage->getTilesX ();
+ int tilesY = backgroundImage->getTilesY ();
+
+ for (int tileX = tileX1; tileX <= tileX2; tileX += tilesX)
+ for (int tileY = tileY1; tileY <= tileY2; tileY += tilesY) {
+ int xt = origX + tileX * imgWidthS;
+ int x1 = misc::max (xt, x);
+ int x2 = misc::min (xt + imgWidthT, x + width);
+ int yt = origY + tileY * imgHeightS;
+ int y1 = misc::max (yt, y);
+ int y2 = misc::min (yt + imgHeightT, y + height);
+
+ view->drawImage (imgbufT, 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->getImgbufSrc();
+ 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) ?
+ multiplyWithPerLength (widthRef - imgWidth, backgroundPositionX) :
+ absLengthVal (backgroundPositionX));
+ *origY = yRef +
+ (isPerLength (backgroundPositionY) ?
+ multiplyWithPerLength (heightRef - imgHeight, backgroundPositionY) :
+ 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..ce8e2e6f 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,137 @@ 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, tilesX, tilesY;
+ Imgbuf *imgbufSrc, *imgbufTiled;
+ 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 *getImgbufSrc () { return imgbufSrc; }
+ inline Imgbuf *getImgbufTiled ()
+ { return imgbufTiled ? imgbufTiled : imgbufSrc; }
+ inline int getTilesX () { return imgbufTiled ? tilesX : 1; }
+ inline int getTilesY () { return imgbufTiled ? tilesY : 1; }
+ 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.
*