aboutsummaryrefslogtreecommitdiff
path: root/dw
diff options
context:
space:
mode:
Diffstat (limited to 'dw')
-rw-r--r--dw/Makefile.am2
-rw-r--r--dw/findtext.cc4
-rw-r--r--dw/iterator.cc84
-rw-r--r--dw/iterator.hh8
-rw-r--r--dw/layout.cc1
-rw-r--r--dw/outofflowmgr.cc495
-rw-r--r--dw/outofflowmgr.hh115
-rw-r--r--dw/style.cc12
-rw-r--r--dw/style.hh17
-rw-r--r--dw/table.cc14
-rw-r--r--dw/tablecell.cc2
-rw-r--r--dw/textblock.cc455
-rw-r--r--dw/textblock.hh118
-rw-r--r--dw/textblock_iterator.cc316
-rw-r--r--dw/textblock_linebreaking.cc417
-rw-r--r--dw/types.cc53
-rw-r--r--dw/types.hh26
-rw-r--r--dw/widget.cc61
-rw-r--r--dw/widget.hh15
19 files changed, 1833 insertions, 382 deletions
diff --git a/dw/Makefile.am b/dw/Makefile.am
index e108da60..aa5c88c3 100644
--- a/dw/Makefile.am
+++ b/dw/Makefile.am
@@ -65,6 +65,8 @@ libDw_widgets_a_SOURCES = \
image.hh \
listitem.cc \
listitem.hh \
+ outofflowmgr.cc \
+ outofflowmgr.hh \
ruler.cc \
ruler.hh \
table.cc \
diff --git a/dw/findtext.cc b/dw/findtext.cc
index 05896ebd..19485078 100644
--- a/dw/findtext.cc
+++ b/dw/findtext.cc
@@ -91,7 +91,7 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens,
if (iterator)
delete iterator;
- iterator = new CharIterator (widget);
+ iterator = new CharIterator (widget, true);
if (backwards) {
/* Go to end */
@@ -123,7 +123,7 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens,
} else {
// Nothing found anymore, reset the state for the next trial.
delete iterator;
- iterator = new CharIterator (widget);
+ iterator = new CharIterator (widget, true);
if (backwards) {
/* Go to end */
while (iterator->next ()) ;
diff --git a/dw/iterator.cc b/dw/iterator.cc
index 5f46cbdb..ccfc068b 100644
--- a/dw/iterator.cc
+++ b/dw/iterator.cc
@@ -186,6 +186,15 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end,
}
}
+
+void Iterator::print ()
+{
+ printf ("in the %s %p, mask ", widget->getClassName(), widget);
+ Content::printMask (mask);
+ printf (", pointing at ");
+ Content::print (&content);
+}
+
// -------------------
// EmptyIterator
// -------------------
@@ -343,7 +352,7 @@ Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask,
//DEBUG_MSG (1, "%*smoving down (%swards) from %s\n",
// indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
- assert (it->getContent()->type == Content::WIDGET);
+ assert (it->getContent()->type & Content::ANY_WIDGET);
it2 = it->getContent()->widget->iterator (mask, fromEnd);
if (it2 == NULL) {
@@ -356,7 +365,7 @@ Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask,
//DEBUG_MSG (1, "%*sexamining %s\n",
// indent, "", a_Dw_iterator_text (it2));
- if (it2->getContent()->type == Content::WIDGET) {
+ if (it2->getContent()->type & Content::ANY_WIDGET) {
// Another widget. Search in it downwards.
it3 = searchDownward (it2, mask, fromEnd);
if (it3 != NULL) {
@@ -390,11 +399,11 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
//DEBUG_MSG (1, "%*smoving %swards from %s\n",
// indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
- assert (it->getContent()->type == Content::WIDGET);
+ assert (it->getContent()->type & Content::ANY_WIDGET);
it2 = it->cloneIterator ();
while (fromEnd ? it2->prev () : it2->next ()) {
- if (it2->getContent()->type == Content::WIDGET) {
+ if (it2->getContent()->type & Content::ANY_WIDGET) {
// Search downwards in this widget.
it3 = searchDownward (it2, mask, fromEnd);
if (it3 != NULL) {
@@ -416,13 +425,14 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
/* Nothing found, go upwards in the tree (if possible). */
it2->unref ();
- if (it->getWidget()->getParent ()) {
- it2 = it->getWidget()->getParent()->iterator (mask, false);
+ Widget *respParent = getRespectiveParent (it->getWidget(), it->getMask());
+ if (respParent) {
+ it2 = respParent->iterator (mask, false);
while (true) {
if (!it2->next ())
misc::assertNotReached ();
- if (it2->getContent()->type == Content::WIDGET &&
+ if (it2->getContent()->type & Content::ANY_WIDGET &&
it2->getContent()->widget == it->getWidget ()) {
it3 = searchSideward (it2, mask, fromEnd);
it2->unref ();
@@ -440,6 +450,19 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
return NULL;
}
+Widget *DeepIterator::getRespectiveParent (Widget *widget, Content::Type mask)
+{
+ // Return, depending on which is requested indirectly (follow
+ // references or containments) the parent (container) or the
+ // generator. At this point, the type of the parent/generator is
+ // not known (since the parent/generator is not known), so we have
+ // to examine the mask. This is the reason why only one of
+ // WIDGET_OOF_CONT and WIDGET_OOF_REF is allowed.
+
+ return (mask & Content::WIDGET_OOF_REF) ?
+ widget->getGenerator() : widget->getParent();
+}
+
/**
* \brief Create a new deep iterator from an existing dw::core::Iterator.
*
@@ -456,6 +479,14 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
*/
DeepIterator::DeepIterator (Iterator *it)
{
+ // Widgets out of flow are either followed widtin containers, or
+ // generators. Both (and also nothing at all) is not allowed. See
+ // also comment in getRespectiveParent.
+ int oofMask =
+ it->getMask() & (Content::WIDGET_OOF_CONT | Content::WIDGET_OOF_REF);
+ assert (oofMask == Content::WIDGET_OOF_CONT ||
+ oofMask == Content::WIDGET_OOF_REF);
+
//DEBUG_MSG (1, "a_Dw_ext_iterator_new: %s\n", a_Dw_iterator_text (it));
// Clone input iterator, so the iterator passed as parameter
@@ -467,7 +498,7 @@ DeepIterator::DeepIterator (Iterator *it)
// If it points to a widget, find a near non-widget content,
// since an DeepIterator should never return widgets.
- if (it->getContent()->type == Content::WIDGET) {
+ if (it->getContent()->type & Content::ANY_WIDGET) {
Iterator *it2;
// The second argument of searchDownward is actually a matter of
@@ -494,21 +525,34 @@ DeepIterator::DeepIterator (Iterator *it)
// \todo There may be a faster way instead of iterating through the
// parent widgets.
+ //printf ("STARTING WITH: ");
+ //it->print ();
+ //printf ("\n");
+
// Construct the iterators.
int thisLevel = it->getWidget()->getLevel (), level;
Widget *w;
- for (w = it->getWidget (), level = thisLevel; w->getParent() != NULL;
- w = w->getParent (), level--) {
- Iterator *it = w->getParent()->iterator (mask, false);
+ for (w = it->getWidget (), level = thisLevel;
+ getRespectiveParent (w) != NULL;
+ w = getRespectiveParent (w), level--) {
+ Iterator *it = getRespectiveParent(w)->iterator (mask, false);
+
+ //printf (" parent: %s %p\n", w->getClassName (), w);
+
stack.put (it, level - 1);
while (true) {
+ //printf (" ");
+ //it->print ();
+ //printf ("\n");
+
bool hasNext = it->next();
assert (hasNext);
- if (it->getContent()->type == Content::WIDGET &&
+ if (it->getContent()->type & Content::ANY_WIDGET &&
it->getContent()->widget == w)
break;
}
+
}
stack.put (it, thisLevel);
@@ -577,7 +621,7 @@ bool DeepIterator::next ()
Iterator *it = stack.getTop ();
if (it->next ()) {
- if (it->getContent()->type == Content::WIDGET) {
+ if (it->getContent()->type & Content::ANY_WIDGET) {
// Widget: new iterator on stack, to search in this widget.
stack.push (it->getContent()->widget->iterator (mask, false));
return next ();
@@ -610,7 +654,7 @@ bool DeepIterator::prev ()
Iterator *it = stack.getTop ();
if (it->prev ()) {
- if (it->getContent()->type == Content::WIDGET) {
+ if (it->getContent()->type & Content::ANY_WIDGET) {
// Widget: new iterator on stack, to search in this widget.
stack.push (it->getContent()->widget->iterator (mask, true));
return prev ();
@@ -642,9 +686,17 @@ CharIterator::CharIterator ()
it = NULL;
}
-CharIterator::CharIterator (Widget *widget)
+/**
+ * \brief ...
+ *
+ * If followReferences is true, only the reference are followed, when
+ * the container and generator for a widget is different. If false,
+ * only the container is followed.
+ */
+CharIterator::CharIterator (Widget *widget, bool followReferences)
{
- Iterator *i = widget->iterator (Content::SELECTION_CONTENT, false);
+ Iterator *i =
+ widget->iterator (Content::maskForSelection (followReferences), false);
it = new DeepIterator (i);
i->unref ();
ch = START;
diff --git a/dw/iterator.hh b/dw/iterator.hh
index c5cfd72b..a48356ef 100644
--- a/dw/iterator.hh
+++ b/dw/iterator.hh
@@ -85,6 +85,8 @@ public:
static void scrollTo (Iterator *it1, Iterator *it2, int start, int end,
HPosition hpos, VPosition vpos);
+
+ virtual void print ();
};
@@ -167,6 +169,10 @@ private:
bool hasContents;
inline DeepIterator () { }
+ static Widget *getRespectiveParent (Widget *widget, Content::Type mask);
+ inline Widget *getRespectiveParent (Widget *widget) {
+ return getRespectiveParent (widget, mask);
+ }
public:
DeepIterator(Iterator *it);
@@ -230,7 +236,7 @@ private:
CharIterator ();
public:
- CharIterator (Widget *widget);
+ CharIterator (Widget *widget, bool followReferences);
~CharIterator ();
lout::object::Object *clone();
diff --git a/dw/layout.cc b/dw/layout.cc
index a974378f..209decc8 100644
--- a/dw/layout.cc
+++ b/dw/layout.cc
@@ -248,6 +248,7 @@ void Layout::addWidget (Widget *widget)
topLevel = widget;
widget->layout = this;
+ widget->notifySetAsTopLevel();
findtextState.setWidget (widget);
diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc
new file mode 100644
index 00000000..ed531c79
--- /dev/null
+++ b/dw/outofflowmgr.cc
@@ -0,0 +1,495 @@
+#include "outofflowmgr.hh"
+
+//#include <math.h> // testing
+
+using namespace lout::container::typed;
+using namespace lout::misc;
+using namespace dw::core;
+using namespace dw::core::style;
+
+namespace dw {
+
+OutOfFlowMgr::OutOfFlowMgr (ContainingBlock *containingBlock)
+{
+ //printf ("OutOfFlowMgr::OutOfFlowMgr\n");
+
+ this->containingBlock = containingBlock;
+ availWidth = availAscent = availDescent = -1;
+ leftFloats = new Vector<Float> (1, true);
+ rightFloats = new Vector<Float> (1, true);
+}
+
+OutOfFlowMgr::~OutOfFlowMgr ()
+{
+ //printf ("OutOfFlowMgr::~OutOfFlowMgr\n");
+
+ delete leftFloats;
+ delete rightFloats;
+}
+
+void OutOfFlowMgr::sizeAllocate (Allocation *containingBlockAllocation)
+{
+ // TODO Much copy and paste.
+
+ for (int i = 0; i < leftFloats->size(); i++) {
+ Float *vloat = leftFloats->get(i);
+ assert (vloat->y != -1);
+ ensureFloatSize (vloat);
+
+ Allocation childAllocation;
+ childAllocation.x = containingBlockAllocation->x + vloat->borderWidth;
+ childAllocation.y = containingBlockAllocation->y + vloat->y;
+ childAllocation.width = vloat->size.width;
+ childAllocation.ascent = vloat->size.ascent;
+ childAllocation.descent = vloat->size.descent;
+
+ vloat->widget->sizeAllocate (&childAllocation);
+
+ //printf ("allocate left #%d -> (%d, %d), %d x (%d + %d)\n",
+ // i, childAllocation.x, childAllocation.y, childAllocation.width,
+ // childAllocation.ascent, childAllocation.descent);
+ }
+
+ int width =
+ availWidth != -1 ? availWidth : containingBlockAllocation->width;
+
+ for (int i = 0; i < rightFloats->size(); i++) {
+ Float *vloat = rightFloats->get(i);
+ assert (vloat->y != -1);
+ ensureFloatSize (vloat);
+
+ Allocation childAllocation;
+ childAllocation.x = containingBlockAllocation->x + width
+ - (vloat->size.width + vloat->borderWidth);
+ childAllocation.y = containingBlockAllocation->y + vloat->y;
+ childAllocation.width = vloat->size.width;
+ childAllocation.ascent = vloat->size.ascent;
+ childAllocation.descent = vloat->size.descent;
+
+ vloat->widget->sizeAllocate (&childAllocation);
+
+ //printf ("allocate right #%d -> (%d, %d), %d x (%d + %d)\n",
+ // i, childAllocation.x, childAllocation.y, childAllocation.width,
+ // childAllocation.ascent, childAllocation.descent);
+ }
+}
+
+
+void OutOfFlowMgr::draw (View *view, Rectangle *area)
+{
+ draw (leftFloats, view, area);
+ draw (rightFloats, view, area);
+}
+
+void OutOfFlowMgr::draw (Vector<Float> *list, View *view, Rectangle *area)
+{
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ assert (vloat->y != -1);
+
+ core::Rectangle childArea;
+ if (vloat->widget->intersects (area, &childArea))
+ vloat->widget->draw (view, &childArea);
+ }
+}
+
+void OutOfFlowMgr::queueResize(int ref)
+{
+}
+
+bool OutOfFlowMgr::isWidgetOutOfFlow (core::Widget *widget)
+{
+ // Will be extended for absolute positions.
+ return widget->getStyle()->vloat != FLOAT_NONE;
+}
+
+void OutOfFlowMgr::addWidget (Widget *widget, Widget *generatingBlock)
+{
+ if (widget->getStyle()->vloat != FLOAT_NONE) {
+ Float *vloat = new Float ();
+ vloat->widget = widget;
+ vloat->generatingBlock = generatingBlock;
+ vloat->dirty = true;
+ vloat->y = -1;
+
+ switch (widget->getStyle()->vloat) {
+ case FLOAT_LEFT:
+ leftFloats->put (vloat);
+ widget->parentRef = createRefLeftFloat (leftFloats->size() - 1);
+ break;
+
+ case FLOAT_RIGHT:
+ rightFloats->put (vloat);
+ widget->parentRef = createRefRightFloat (rightFloats->size() - 1);
+ break;
+
+ default:
+ assertNotReached();
+ }
+ } else
+ // Will continue here for absolute positions.
+ assertNotReached();
+}
+
+OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget)
+{
+ Vector<Float> *list = getFloatList (widget);
+
+ for(int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ if(vloat->widget == widget)
+ return vloat;
+ }
+
+ assertNotReached();
+ return NULL;
+}
+
+Vector<OutOfFlowMgr::Float> *OutOfFlowMgr::getFloatList (Widget *widget)
+{
+ switch (widget->getStyle()->vloat) {
+ case FLOAT_LEFT:
+ return leftFloats;
+
+ case FLOAT_RIGHT:
+ return rightFloats;
+
+ default:
+ assertNotReached();
+ return NULL;
+ }
+}
+
+Vector<OutOfFlowMgr::Float> *OutOfFlowMgr::getOppositeFloatList (Widget *widget)
+{
+ switch (widget->getStyle()->vloat) {
+ case FLOAT_LEFT:
+ return rightFloats;
+
+ case FLOAT_RIGHT:
+ return leftFloats;
+
+ default:
+ assertNotReached();
+ return NULL;
+ }
+}
+
+void OutOfFlowMgr::markSizeChange (int ref)
+{
+ //printf ("[%p] MARK_SIZE_CHANGE (%d)\n", containingBlock, ref);
+
+ if (isRefFloat (ref)) {
+ Float *vloat;
+
+ if (isRefLeftFloat (ref))
+ vloat = leftFloats->get (getFloatIndexFromRef (ref));
+ else if (isRefRightFloat (ref))
+ vloat = rightFloats->get (getFloatIndexFromRef (ref));
+ else {
+ assertNotReached();
+ vloat = NULL; // compiler happiness
+ }
+
+ vloat->dirty = true;
+ // In some cases, the vertical position is not yet defined. Nothing
+ // necessary then.
+ if (vloat->y != -1)
+ containingBlock->borderChanged (vloat->y);
+ }
+ else
+ // later: absolute positions
+ assertNotReached();
+}
+
+
+void OutOfFlowMgr::markExtremesChange (int ref)
+{
+}
+
+Widget *OutOfFlowMgr::getWidgetAtPoint (int x, int y, int level)
+{
+ Widget *childAtPoint = getWidgetAtPoint (leftFloats, x, y, level);
+ if (childAtPoint == NULL)
+ childAtPoint = getWidgetAtPoint (rightFloats, x, y, level);
+ return childAtPoint;
+}
+
+Widget *OutOfFlowMgr::getWidgetAtPoint (Vector<Float> *list,
+ int x, int y, int level)
+{
+ for (int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ Widget *childAtPoint = vloat->widget->getWidgetAtPoint (x, y, level + 1);
+ if (childAtPoint)
+ return childAtPoint;
+ }
+
+ return NULL;
+}
+
+
+void OutOfFlowMgr::tellNoPosition (Widget *widget)
+{
+ Float *vloat = findFloatByWidget(widget);
+ int oldY = vloat->y;
+ vloat->y = -1;
+
+ if (oldY != -1)
+ containingBlock->borderChanged (oldY);
+}
+
+
+void OutOfFlowMgr::getSize (int cbWidth, int cbHeight,
+ int *oofWidth, int *oofHeight)
+{
+ // CbWidth and cbHeight *do* contain padding, border, and
+ // margin. See call in dw::Textblock::sizeRequest. (Notice that
+ // this has changed from an earlier version.)
+
+ // Also notice that Float::y includes margins etc.
+
+ // TODO Is it correct to add padding, border, and margin to the
+ // containing block? Check CSS spec.
+
+ *oofWidth = cbWidth; /* This (or "<=" instead of "=") should be
+ the case for floats. */
+
+ int oofHeightLeft = containingBlock->asWidget()->getStyle()->boxDiffWidth();
+ int oofHeightRight = containingBlock->asWidget()->getStyle()->boxDiffWidth();
+
+ for (int i = 0; i < leftFloats->size(); i++) {
+ Float *vloat = leftFloats->get(i);
+ assert (vloat->y != -1);
+ ensureFloatSize (vloat);
+ oofHeightLeft =
+ max (oofHeightLeft,
+ vloat->y + vloat->size.ascent + vloat->size.descent
+ + containingBlock->asWidget()->getStyle()->boxRestHeight());
+ }
+
+ for (int i = 0; i < rightFloats->size(); i++) {
+ Float *vloat = rightFloats->get(i);
+ assert (vloat->y != -1);
+ ensureFloatSize (vloat);
+ oofHeightRight =
+ max (oofHeightRight,
+ vloat->y + vloat->size.ascent + vloat->size.descent
+ + containingBlock->asWidget()->getStyle()->boxRestHeight());
+ }
+
+ *oofHeight = max (oofHeightLeft, oofHeightRight);
+}
+
+
+void OutOfFlowMgr::tellPosition (Widget *widget, int y)
+{
+ assert (y >= 0);
+
+ Float *vloat = findFloatByWidget(widget);
+ ensureFloatSize (vloat);
+
+ //printf ("[%p] tellPosition (%p, %d): %d ...\n",
+ // containingBlock, widget, y, vloat->y);
+
+ int oldY = vloat->y;
+
+ int realY = y;
+ Vector<Float> *listSame = getFloatList (widget);
+ Vector<Float> *listOpp = getOppositeFloatList (widget);
+ bool collides;
+
+ do {
+ // Test collisions on the same side.
+ collides = false;
+
+ for (int i = 0; i < listSame->size(); i++) {
+ Float *v = listSame->get(i);
+ if (v != vloat) {
+ ensureFloatSize (v);
+ if (v->y != -1 && realY >= v->y &&
+ realY < v->y + v->size.ascent + v->size.descent) {
+ collides = true;
+ realY = v->y + v->size.ascent + v->size.descent;
+ break;
+ }
+ }
+ }
+
+ // Test collisions on the other side.
+ for (int i = 0; i < listOpp->size(); i++) {
+ Float *v = listOpp->get(i);
+ // Note: Since v is on the opposite side, the condition
+ // v != vloat (used above) is always true.
+ ensureFloatSize (v);
+ if (v->y != -1 && realY >= v->y &&
+ realY < v->y + v->size.ascent + v->size.descent &&
+ // For the other size, horizontal dimensions have to be
+ // considered, too.
+ v->size.width + v->borderWidth +
+ vloat->size.width + vloat->borderWidth > availWidth) {
+ collides = true;
+ realY = v->y + v->size.ascent + v->size.descent;
+ break;
+ }
+ }
+ } while (collides);
+
+ vloat->y = realY;
+
+ //printf (" => %d\n", vloat->y);
+
+ if (oldY == -1)
+ containingBlock->borderChanged (vloat->y);
+ else if (vloat->y != oldY)
+ containingBlock->borderChanged (min (oldY, vloat->y));
+}
+
+/**
+ * Get the left border for the vertical position of *y*, for a height
+ * of *h", based on floats.
+ *
+ * The border includes marging/border/padding of the containging
+ * block, but is 0 if there is no float, so a caller should also
+ * consider other borders.
+ */
+int OutOfFlowMgr::getLeftBorder (int y, int h)
+{
+ return getBorder (leftFloats, y, h);
+}
+
+/**
+ * Get the right border for the vertical position of *y*, for a height
+ * of *h", based on floats.
+ *
+ * See also getLeftBorder(int, int);
+ */
+int OutOfFlowMgr::getRightBorder (int y, int h)
+{
+ return getBorder (rightFloats, y, h);
+}
+
+int OutOfFlowMgr::getBorder (Vector<Float> *list, int y, int h)
+{
+ int border = 0;
+
+ // To be a bit more efficient, one could use linear search to find
+ // the first affected float.
+ for(int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ ensureFloatSize (vloat);
+
+ if(vloat->y != -1 && y + h >= vloat->y &&
+ y < vloat->y + vloat->size.ascent + vloat->size.descent) {
+ // It is not sufficient to find the first float, since a line
+ // (with height h) may cover the region of multiple float, of
+ // which the widest has to be choosen.
+ border = max (border, vloat->size.width + vloat->borderWidth);
+ }
+ // To be a bit more efficient, the loop could be stopped when
+ // (i) at least one float has been found, and (ii) the next float is
+ // below y + h.
+ }
+
+ return border;
+}
+
+void OutOfFlowMgr::ensureFloatSize (Float *vloat)
+{
+ if (vloat->dirty) {
+ // TODO Ugly. Soon to be replaced by cleaner code? See also
+ // comment in Textblock::calcWidgetSize.
+ if (vloat->widget->usesHints ()) {
+ if (isAbsLength (vloat->widget->getStyle()->width))
+ vloat->widget->setWidth
+ (absLengthVal (vloat->widget->getStyle()->width));
+ else if (isPerLength (vloat->widget->getStyle()->width))
+ vloat->widget->setWidth
+ (availWidth * perLengthVal (vloat->widget->getStyle()->width));
+ }
+
+ // This is a bit hackish: We first request the size, then set
+ // the available width (also considering the one of the
+ // containing block, and the extremes of the float), then
+ // request the size again, which may of course have a different
+ // result. This is a fix for the bug:
+ //
+ // Text in floats, which are wider because of an image, are
+ // broken at a too narrow width. Reproduce:
+ // test/floats2.html. After the image has been loaded, the
+ // text "Some text in a float." should not be broken
+ // anymore.
+ //
+ // If the call of setWidth not is neccessary, the second call
+ // will read the size from the cache, so no redundant
+ // calculation is necessary.
+
+ // Furthermore, extremes are considered; especially, floats are too
+ // wide, sometimes.
+ Extremes extremes;
+ vloat->widget->getExtremes (&extremes);
+
+ vloat->widget->sizeRequest (&vloat->size);
+
+ // Set width ...
+ int width = vloat->size.width;
+ // Consider the available width of the containing block (when set):
+ if (availWidth != -1 && width > availWidth)
+ width = availWidth;
+ // Finally, consider extremes (as described above).
+ if (width < extremes.minWidth)
+ width = extremes.minWidth;
+ if (width > extremes.maxWidth)
+ width = extremes.maxWidth;
+
+ vloat->widget->setWidth (width);
+ vloat->widget->sizeRequest (&vloat->size);
+
+ vloat->borderWidth = calcBorderDiff (vloat);
+
+ //printf (" Float at %d: %d x (%d + %d)\n",
+ // vloat->y, vloat->width, vloat->ascent, vloat->descent);
+
+ vloat->dirty = false;
+ }
+}
+
+/**
+ * Calculate the border diff, i. e., for left floats, the difference
+ * between the left side of the allocation of the containing block and
+ * the left side of the allocation of the float, and likewise for
+ * right floats. Both values are positive.
+ *
+ * From how I have understood the CSS specification, the generating
+ * block should be included. (In the case I am wrong: a change of this
+ * method should be sufficient.)
+ */
+int OutOfFlowMgr::calcBorderDiff (Float *vloat)
+{
+ switch (vloat->widget->getStyle()->vloat) {
+ case FLOAT_LEFT:
+ {
+ int d = containingBlock->asWidget()->getStyle()->boxOffsetX();
+ for (Widget *w = vloat->generatingBlock;
+ w != containingBlock->asWidget(); w = w->getParent())
+ d += w->getStyle()->boxOffsetX();
+ return d;
+ }
+
+ case FLOAT_RIGHT:
+ {
+ int d = containingBlock->asWidget()->getStyle()->boxRestWidth();
+ for (Widget *w = vloat->generatingBlock;
+ w != containingBlock->asWidget(); w = w->getParent())
+ d += w->getStyle()->boxRestWidth();
+ return d;
+ }
+
+ default:
+ // Only used for floats.
+ assertNotReached();
+ return 0; // compiler happiness
+ }
+}
+
+} // namespace dw
diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh
new file mode 100644
index 00000000..13fc0877
--- /dev/null
+++ b/dw/outofflowmgr.hh
@@ -0,0 +1,115 @@
+#ifndef __DW_OUTOFFLOWMGR_HH__
+#define __DW_OUTOFFLOWMGR_HH__
+
+#include "core.hh"
+
+namespace dw {
+
+/**
+ * \brief Represents additional data for containing blocks.
+ */
+class OutOfFlowMgr
+{
+public:
+ class ContainingBlock
+ {
+ public:
+ virtual void borderChanged (int y) = 0;
+ virtual core::Widget *asWidget () = 0;
+ };
+
+private:
+ ContainingBlock *containingBlock;
+ int availWidth, availAscent, availDescent;
+
+ class Float: public lout::object::Object
+ {
+ public:
+ core::Widget *widget, *generatingBlock;
+ // width includes border of the containing block
+ int y, borderWidth;
+ core::Requisition size;
+ bool dirty;
+ };
+
+ //lout::container::typed::HashTable<lout::object::TypedPointer
+ // <dw::core::Widget>, Float> *floatsByWidget;
+ lout::container::typed::Vector<Float> *leftFloats, *rightFloats;
+
+ Float *findFloatByWidget (core::Widget *widget);
+ lout::container::typed::Vector<Float> *getFloatList (core::Widget *widget);
+ lout::container::typed::Vector<Float> *getOppositeFloatList (core::Widget
+ *widget);
+ void ensureFloatSize (Float *vloat);
+ int calcBorderDiff (Float *vloat);
+
+ void draw (lout::container::typed::Vector<Float> *list,
+ core::View *view, core::Rectangle *area);
+ core::Widget *getWidgetAtPoint (lout::container::typed::Vector<Float> *list,
+ int x, int y, int level);
+ int getBorder (lout::container::typed::Vector<Float> *list, int y, int h);
+
+ inline static bool isRefFloat (int ref)
+ { return ref != -1 && (ref & 1) == 1; }
+ inline static bool isRefLeftFloat (int ref)
+ { return ref != -1 && (ref & 3) == 1; }
+ inline static bool isRefRightFloat (int ref)
+ { return ref != -1 && (ref & 3) == 3; }
+
+ inline static int createRefLeftFloat (int index)
+ { return (index << 2) | 1; }
+ inline static int createRefRightFloat (int index)
+ { return (index << 2) | 3; }
+
+ inline static int getFloatIndexFromRef (int ref)
+ { return ref == -1 ? ref : (ref >> 2); }
+
+public:
+ OutOfFlowMgr (ContainingBlock *containingBlock);
+ ~OutOfFlowMgr ();
+
+ void sizeAllocate(core::Allocation *containingBlockAllocation);
+ void draw (core::View *view, core::Rectangle *area);
+ void queueResize(int ref);
+
+ void markSizeChange (int ref);
+ void markExtremesChange (int ref);
+ core::Widget *getWidgetAtPoint (int x, int y, int level);
+
+ static bool isWidgetOutOfFlow (core::Widget *widget);
+ void addWidget (core::Widget *widget, core::Widget *generatingBlock);
+
+ void tellNoPosition (core::Widget *widget);
+ void tellPosition (core::Widget *widget, int y);
+
+ void setWidth (int width) { availWidth = width; }
+ void setAscent (int ascent) { availAscent = ascent; }
+ void setDescent (int descent) { availDescent = descent; }
+
+ void getSize (int cbWidth, int cbHeight, int *oofWidth, int *oofHeight);
+
+ int getLeftBorder (int y, int h);
+
+ int getRightBorder (int y, int h);
+
+ inline static bool isRefOutOfFlow (int ref)
+ { return ref != -1 && (ref & 1) != 0; }
+ inline static int createRefNormalFlow (int lineNo) { return lineNo << 1; }
+ inline static int getLineNoFromRef (int ref)
+ { return ref == -1 ? ref : (ref >> 1); }
+
+ //inline static bool isRefOutOfFlow (int ref) { return false; }
+ //inline static int createRefNormalFlow (int lineNo) { return lineNo; }
+ //inline static int getLineNoFromRef (int ref) { return ref; }
+
+ // for iterators
+ inline int getNumWidgets () {
+ return leftFloats->size() + rightFloats->size(); }
+ inline core::Widget *getWidget (int i) {
+ return i < leftFloats->size() ? leftFloats->get(i)->widget :
+ rightFloats->get(i - leftFloats->size())->widget; }
+};
+
+} // namespace dw
+
+#endif // __DW_OUTOFFLOWMGR_HH__
diff --git a/dw/style.cc b/dw/style.cc
index 6c0abda2..2f64e468 100644
--- a/dw/style.cc
+++ b/dw/style.cc
@@ -17,8 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-
#include <stdio.h>
#include <string.h>
#include <unistd.h>
@@ -49,6 +47,8 @@ void StyleAttrs::initValues ()
valign = VALIGN_BASELINE;
backgroundColor = NULL;
width = height = lineHeight = LENGTH_AUTO;
+ vloat = FLOAT_NONE;
+ clear = CLEAR_NONE;
textIndent = 0;
margin.setVal (0);
borderWidth.setVal (0);
@@ -75,6 +75,8 @@ void StyleAttrs::resetValues ()
valign = VALIGN_BASELINE;
textAlignChar = '.';
+ vloat = FLOAT_NONE; /** \todo Correct? Check specification. */
+ clear = CLEAR_NONE; /** \todo Correct? Check specification. */
backgroundColor = NULL;
width = LENGTH_AUTO;
height = LENGTH_AUTO;
@@ -120,6 +122,8 @@ bool StyleAttrs::equals (object::Object *other) {
valign == otherAttrs->valign &&
textAlignChar == otherAttrs->textAlignChar &&
textTransform == otherAttrs->textTransform &&
+ vloat == otherAttrs->vloat &&
+ clear == otherAttrs->clear &&
hBorderSpacing == otherAttrs->hBorderSpacing &&
vBorderSpacing == otherAttrs->vBorderSpacing &&
wordSpacing == otherAttrs->wordSpacing &&
@@ -160,6 +164,8 @@ int StyleAttrs::hashValue () {
valign +
textAlignChar +
textTransform +
+ vloat +
+ clear +
hBorderSpacing +
vBorderSpacing +
wordSpacing +
@@ -252,6 +258,8 @@ void Style::copyAttrs (StyleAttrs *attrs)
valign = attrs->valign;
textAlignChar = attrs->textAlignChar;
textTransform = attrs->textTransform;
+ vloat = attrs->vloat;
+ clear = attrs->clear;
hBorderSpacing = attrs->hBorderSpacing;
vBorderSpacing = attrs->vBorderSpacing;
wordSpacing = attrs->wordSpacing;
diff --git a/dw/style.hh b/dw/style.hh
index 7c00ac1f..41784c79 100644
--- a/dw/style.hh
+++ b/dw/style.hh
@@ -281,7 +281,6 @@ enum ListStylePosition {
LIST_STYLE_POSITION_INSIDE,
LIST_STYLE_POSITION_OUTSIDE
};
-
enum ListStyleType {
LIST_STYLE_TYPE_DISC,
LIST_STYLE_TYPE_CIRCLE,
@@ -333,6 +332,19 @@ enum WhiteSpace {
WHITE_SPACE_PRE_LINE,
};
+enum FloatType {
+ FLOAT_NONE,
+ FLOAT_LEFT,
+ FLOAT_RIGHT
+};
+
+enum ClearType {
+ CLEAR_LEFT,
+ CLEAR_RIGHT,
+ CLEAR_BOTH,
+ CLEAR_NONE
+};
+
/**
* \brief Type for representing all lengths within dw::core::style.
*
@@ -450,6 +462,9 @@ public:
VAlignType valign;
char textAlignChar; /* In future, strings will be supported. */
TextTransform textTransform;
+
+ FloatType vloat; /* "float" is a keyword. */
+ ClearType clear;
int hBorderSpacing, vBorderSpacing, wordSpacing;
Length width, height, lineHeight, textIndent;
diff --git a/dw/table.cc b/dw/table.cc
index c21b7a09..c4108e47 100644
--- a/dw/table.cc
+++ b/dw/table.cc
@@ -1103,7 +1103,7 @@ Table::TableIterator::TableIterator (Table *table,
else if (index >= table->children->size ())
content.type = core::Content::END;
else {
- content.type = core::Content::WIDGET;
+ content.type = core::Content::WIDGET_IN_FLOW;
content.widget = table->children->get(index)->cell.widget;
}
}
@@ -1125,8 +1125,8 @@ bool Table::TableIterator::next ()
if (content.type == core::Content::END)
return false;
- // tables only contain widgets:
- if ((getMask() & core::Content::WIDGET) == 0) {
+ // tables only contain widgets (in flow):
+ if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) {
content.type = core::Content::END;
return false;
}
@@ -1140,7 +1140,7 @@ bool Table::TableIterator::next ()
} while (table->children->get(index) == NULL ||
table->children->get(index)->type != Child::CELL);
- content.type = core::Content::WIDGET;
+ content.type = core::Content::WIDGET_IN_FLOW;
content.widget = table->children->get(index)->cell.widget;
return true;
}
@@ -1152,8 +1152,8 @@ bool Table::TableIterator::prev ()
if (content.type == core::Content::START)
return false;
- // tables only contain widgets:
- if ((getMask() & core::Content::WIDGET) == 0) {
+ // tables only contain widgets (in flow):
+ if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) {
content.type = core::Content::START;
return false;
}
@@ -1167,7 +1167,7 @@ bool Table::TableIterator::prev ()
} while (table->children->get(index) == NULL ||
table->children->get(index)->type != Child::CELL);
- content.type = core::Content::WIDGET;
+ content.type = core::Content::WIDGET_IN_FLOW;
content.widget = table->children->get(index)->cell.widget;
return true;
}
diff --git a/dw/tablecell.cc b/dw/tablecell.cc
index 90dc310d..d1a8e3e1 100644
--- a/dw/tablecell.cc
+++ b/dw/tablecell.cc
@@ -102,7 +102,7 @@ int TableCell::getValue ()
void TableCell::setMaxValue (int maxValue, int value)
{
line1Offset = maxValue - value;
- queueResize (0, true);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), true);
}
} // namespace dw
diff --git a/dw/textblock.cc b/dw/textblock.cc
index 068a7c0d..fc298562 100644
--- a/dw/textblock.cc
+++ b/dw/textblock.cc
@@ -24,13 +24,14 @@
#include "../lout/unicode.hh"
#include <stdio.h>
-#include <math.h>
+#include <math.h> // remove again?
+#include <limits.h>
/*
* Local variables
*/
- /* The tooltip under mouse pointer in current textblock. No ref. hold.
- * (having one per view looks not worth the extra clutter). */
+/* The tooltip under mouse pointer in current textblock. No ref. hold.
+ * (having one per view looks not worth the extra clutter). */
static dw::core::style::Tooltip *hoverTooltip = NULL;
@@ -144,6 +145,7 @@ Textblock::Textblock (bool limitTextWidth)
nonTemporaryLines = 0;
words = new misc::NotSoSimpleVector <Word> (1);
anchors = new misc::SimpleVector <Anchor> (1);
+ outOfFlowMgr = NULL;
//DBG_OBJ_SET_NUM(this, "num_lines", num_lines);
@@ -161,6 +163,9 @@ Textblock::Textblock (bool limitTextWidth)
availAscent = 100;
availDescent = 0;
+ diffXToContainingBlock = restWidthToContainingBlock =
+ diffYToContainingBlock = -1;
+
this->limitTextWidth = limitTextWidth;
for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
@@ -181,8 +186,9 @@ Textblock::~Textblock ()
for (int i = 0; i < words->size(); i++) {
Word *word = words->getRef (i);
- if (word->content.type == core::Content::WIDGET)
+ if (word->content.type == core::Content::WIDGET_IN_FLOW)
delete word->content.widget;
+ /** \todo Widget references? What about texts? */
word->style->unref ();
word->spaceStyle->unref ();
}
@@ -198,6 +204,9 @@ Textblock::~Textblock ()
delete words;
delete anchors;
+ if(outOfFlowMgr)
+ delete outOfFlowMgr;
+
/* Make sure we don't own widgets anymore. Necessary before call of
parent class destructor. (???) */
words = NULL;
@@ -252,6 +261,21 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
requisition->ascent += getStyle()->boxOffsetY ();
requisition->descent += getStyle()->boxRestHeight ();
+ // Dealing with parts out of flow, which may overlap the borders of
+ // the text block. Base lines are ignored here: they do not play a
+ // role (currently) and caring about them (for the future) would
+ // cause too much problems.
+
+ if (outOfFlowMgr) {
+ int oofWidth, oofHeight;
+ outOfFlowMgr->getSize (requisition->width,
+ requisition->ascent + requisition->descent,
+ &oofWidth, &oofHeight);
+ requisition->width = misc::max (requisition->width, oofWidth);
+ if (oofHeight > requisition->ascent + requisition->descent)
+ requisition->descent = oofHeight - requisition->ascent;
+ }
+
if (requisition->width < availWidth)
requisition->width = availWidth;
@@ -264,7 +288,7 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
*/
void Textblock::getWordExtremes (Word *word, core::Extremes *extremes)
{
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
if (word->content.widget->usesHints ())
word->content.widget->getExtremes (extremes);
else {
@@ -338,7 +362,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) {
line = lines->getRef (lineIndex);
- xCursor = lineXOffsetWidget (line);
+ xCursor = line->offsetCompleteWidget;
for (wordIndex = line->firstWord; wordIndex <= line->lastWord;
wordIndex++) {
@@ -348,7 +372,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
redrawY = misc::min (redrawY, lineYOffsetWidget (line));
}
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
/** \todo Justification within the line is done here. */
childAllocation.x = xCursor + allocation->x;
/* align=top:
@@ -420,6 +444,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
}
}
+ if(outOfFlowMgr)
+ outOfFlowMgr->sizeAllocate(allocation);
+
for (int i = 0; i < anchors->size(); i++) {
Anchor *anchor = anchors->getRef(i);
int y;
@@ -450,47 +477,118 @@ void Textblock::resizeDrawImpl ()
void Textblock::markSizeChange (int ref)
{
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markSizeChange (ref);
+ } else {
+ PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n",
+ this, ref, wrapRefLines);
+
+ /* By the way: ref == -1 may have two different causes: (i) flush()
+ calls "queueResize (-1, true)", when no rewrapping is necessary;
+ and (ii) a word may have parentRef == -1 , when it is not yet
+ added to a line. In the latter case, nothing has to be done
+ now, but addLine(...) will do everything necessary. */
+ if (ref != -1) {
+ if (wrapRefLines == -1)
+ wrapRefLines = OutOfFlowMgr::getLineNoFromRef (ref);
+ else
+ wrapRefLines = misc::min (wrapRefLines,
+ OutOfFlowMgr::getLineNoFromRef (ref));
+ }
+
+ PRINTF (" ... => %d\n", wrapRefLine);
+
+ // It seems that sometimes (even without floats) the lines
+ // structure is changed, so that wrapRefLines may refers to a
+ // line which does not exist anymore. Should be examined
+ // again. Until then, setting wrapRefLines to the same value is
+ // a workaround.
+ markExtremesChange (ref);
+ }
+}
+
+void Textblock::markExtremesChange (int ref)
+{
PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", this, ref, wrapRefLines);
- /* By the way: ref == -1 may have two different causes: (i) flush()
- calls "queueResize (-1, true)", when no rewrapping is necessary;
- and (ii) a word may have parentRef == -1 , when it is not yet
- added to a line. In the latter case, nothing has to be done
- now, but addLine(...) will do everything necessary. */
- if (ref != -1) {
- if (wrapRefLines == -1)
- wrapRefLines = ref;
- else
- wrapRefLines = misc::min (wrapRefLines, ref);
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markExtremesChange (ref);
+ } else {
+ PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n",
+ this, ref, wrapRefParagraphs);
+
+ /* By the way: ref == -1 may have two different causes: (i) flush()
+ calls "queueResize (-1, true)", when no rewrapping is necessary;
+ and (ii) a word may have parentRef == -1 , when it is not yet
+ added to a line. In the latter case, nothing has to be done
+ now, but addLine(...) will do everything necessary. */
+ if (ref != -1) {
+ if (wrapRefParagraphs == -1)
+ wrapRefParagraphs = OutOfFlowMgr::getLineNoFromRef (ref);
+ else
+ wrapRefParagraphs =
+ misc::min (wrapRefParagraphs,
+ OutOfFlowMgr::getLineNoFromRef (ref));
+ }
+
+ PRINTF (" ... => %d\n", wrapRefParagraphs);
}
+}
- PRINTF (" ... => %d\n", wrapRefLine);
+void Textblock::notifySetAsTopLevel()
+{
+ PRINTF ("%p becomes toplevel\n", this);
+ containingBlock = this;
+ diffXToContainingBlock = restWidthToContainingBlock =
+ diffYToContainingBlock = 0;
+ PRINTF ("-> %p is its own containing block\n", this);
+}
- // It seems that sometimes the lines structure is changed, so that
- // wrapRefLines may refers to a line which does not exist
- // anymore. Should be examined again. Until then, setting
- // wrapRefLines to the same value is a workaround.
- markExtremesChange (ref);
+bool Textblock::isContainingBlock (Widget *widget)
+{
+ return
+ // Of course, only textblocks are considered as containing
+ // blocks.
+ widget->instanceOf (Textblock::CLASS_ID) &&
+ // The second condition: that this block is "out of flow", in a
+ // wider sense.
+ (// The toplevel widget is "out of flow", since there is no
+ // parent, and so no context.
+ widget->getParent() == NULL ||
+ // A similar reasoning applies to a widget with another parent
+ // than a textblock (typical example: a table cell (this is
+ // also a text block) within a table widget).
+ !widget->getParent()->instanceOf (Textblock::CLASS_ID) ||
+ // Finally, "out of flow" in a narrower sense: floats and
+ // absolute positions.
+ OutOfFlowMgr::isWidgetOutOfFlow (widget));
}
-void Textblock::markExtremesChange (int ref)
+void Textblock::notifySetParent ()
{
- PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n",
- this, ref, wrapRefParagraphs);
-
- /* By the way: ref == -1 may have two different causes: (i) flush()
- calls "queueResize (-1, true)", when no rewrapping is necessary;
- and (ii) a word may have parentRef == -1 , when it is not yet
- added to a line. In the latter case, nothing has to be done
- now, but addLine(...) will do everything necessary. */
- if (ref != -1) {
- if (wrapRefParagraphs == -1)
- wrapRefParagraphs = ref;
- else
- wrapRefParagraphs = misc::min (wrapRefParagraphs, ref);
- }
+ PRINTF ("%p becomes a child of %p\n", this, getParent());
+
+ // Search for containing Box.
+ containingBlock = NULL;
- PRINTF (" ... => %d\n", wrapRefParagraphs);
+ for (Widget *widget = this; widget != NULL && containingBlock == NULL;
+ widget = widget->getParent())
+ if (isContainingBlock (widget)) {
+ containingBlock = (Textblock*)widget;
+
+ if (containingBlock == this) {
+ diffXToContainingBlock = restWidthToContainingBlock =
+ diffYToContainingBlock = 0;
+ PRINTF ("-> %p is its own containing block\n", this);
+ } else {
+ PRINTF ("-> %p becomes containing block of %p\n",
+ containingBlock, this);
+ }
+ }
+
+ assert (containingBlock != NULL);
}
void Textblock::setWidth (int width)
@@ -504,10 +602,13 @@ void Textblock::setWidth (int width)
// words->size());
availWidth = width;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
redrawY = 0;
}
+
+ if (outOfFlowMgr)
+ outOfFlowMgr->setWidth (width);
}
void Textblock::setAscent (int ascent)
@@ -519,9 +620,12 @@ void Textblock::setAscent (int ascent)
// words->size());
availAscent = ascent;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
}
+
+ if (outOfFlowMgr)
+ outOfFlowMgr->setAscent (ascent);
}
void Textblock::setDescent (int descent)
@@ -533,9 +637,12 @@ void Textblock::setDescent (int descent)
// words->size());
availDescent = descent;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
}
+
+ if (outOfFlowMgr)
+ outOfFlowMgr->setDescent (descent);
}
bool Textblock::buttonPressImpl (core::EventButton *event)
@@ -650,11 +757,11 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
// Choose this break.
wordIndex = line->lastWord;
charPos = core::SelectionState::END_OF_WORD;
- } else if (event->xWidget < lineXOffsetWidget (line)) {
+ } else if (event->xWidget < line->offsetCompleteWidget) {
// Left of the first word in the line.
wordIndex = line->firstWord;
} else {
- int nextWordStartX = lineXOffsetWidget (line);
+ int nextWordStartX = line->offsetCompleteWidget;
for (wordIndex = line->firstWord;
wordIndex <= line->lastWord;
@@ -734,8 +841,9 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
}
}
}
- it = new TextblockIterator (this, core::Content::SELECTION_CONTENT,
- wordIndex);
+
+ it = new TextblockIterator (this, core::Content::maskForSelection (true),
+ false, wordIndex);
r = selectionHandleEvent (eventType, it, charPos, link, event);
it->unref ();
return r;
@@ -767,6 +875,32 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size)
availAscent = this->availAscent - getStyle()->boxDiffHeight ();
availDescent = this->availDescent;
+ // The following should be changed, made more general, and so on.
+ //
+ // With floats, the implementation of Textblock::sizeRequestImpl
+ // becomes special: Textblock::sizeRequestImpl will rewrap lines,
+ // for which the line widths are needed, which depend on floats;
+ // however, if a text block is not its containing block, the floats
+ // positions depend on the position text block within the text
+ // container.
+ //
+ // This is something new, and should be implemented in a more
+ // general way, perhaps resembling size hints.
+
+ // Tell a child textblock its position within the containing block.
+
+ if (widget->instanceOf (Textblock::CLASS_ID)) {
+ Textblock *tb = (Textblock*)widget;
+ if (tb != tb->containingBlock) {
+ tb->diffXToContainingBlock =
+ diffXToContainingBlock + getStyle()->boxOffsetX();
+ tb->restWidthToContainingBlock =
+ restWidthToContainingBlock + getStyle()->boxRestWidth();;
+ tb->diffYToContainingBlock =
+ diffYToContainingBlock + topOfPossiblyMissingLine (lines->size ());
+ }
+ }
+
if (widget->usesHints ()) {
widget->setWidth (availWidth);
widget->setAscent (availAscent);
@@ -1101,7 +1235,7 @@ void Textblock::drawSpace(int wordIndex, core::View *view,
*/
void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
{
- int xWidget = lineXOffsetWidget(line);
+ int xWidget = line->offsetCompleteWidget;
int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
for (int wordIndex = line->firstWord;
@@ -1112,10 +1246,10 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
if (xWidget + wordSize + word->hyphenWidth + word->effSpace >= area->x) {
if (word->content.type == core::Content::TEXT ||
- word->content.type == core::Content::WIDGET) {
+ word->content.type == core::Content::WIDGET_IN_FLOW) {
if (word->size.width > 0) {
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
core::Widget *child = word->content.widget;
core::Rectangle childArea;
@@ -1259,7 +1393,7 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace)
if (yWidgetBase + line->boxDescent <= y)
return NULL;
- xCursor = lineXOffsetWidget (line);
+ xCursor = line->offsetCompleteWidget;
for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
word = words->getRef (wordIndex);
lastXCursor = xCursor;
@@ -1305,6 +1439,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area)
drawLine (line, view, area);
}
+
+ if(outOfFlowMgr)
+ outOfFlowMgr->draw(view, area);
}
/**
@@ -1704,28 +1841,48 @@ void Textblock::addText0 (const char *text, size_t len, short flags,
*/
void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
{
- Word *word;
- core::Requisition size;
-
/* We first assign -1 as parent_ref, since the call of widget->size_request
* will otherwise let this Textblock be rewrapped from the beginning.
* (parent_ref is actually undefined, but likely has the value 0.) At the,
* end of this function, the correct value is assigned. */
widget->parentRef = -1;
- PRINTF ("%p becomes child of %p\n", widget, this);
-
- widget->setParent (this);
widget->setStyle (style);
- calcWidgetSize (widget, &size);
- word = addWord (size.width, size.ascent, size.descent, 0, style);
+ PRINTF ("adding the %s %p to %p ...\n",
+ widget->getClassName(), widget, this);
+
+ if (OutOfFlowMgr::isWidgetOutOfFlow (widget)) {
+ PRINTF (" -> out of flow.\n");
+
+ if (containingBlock->outOfFlowMgr == NULL) {
+ containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock);
+ containingBlock->outOfFlowMgr->setWidth (containingBlock->availWidth);
+ containingBlock->outOfFlowMgr->setAscent
+ (containingBlock->availAscent);
+ containingBlock->outOfFlowMgr->setDescent
+ (containingBlock->availDescent);
+ }
+
+ widget->setParent (containingBlock);
+ widget->setGenerator (this);
+ containingBlock->outOfFlowMgr->addWidget (widget, this);
+ Word *word = addWord (0, 0, 0, false, style);
+ word->content.type = core::Content::WIDGET_OOF_REF;
+ word->content.widget = widget;
+ word->style = style;
+ } else {
+ PRINTF (" -> within flow.\n");
- word->content.type = core::Content::WIDGET;
- word->content.widget = widget;
+ widget->setParent (this);
- //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1,
- // word->content.widget);
+ core::Requisition size;
+ calcWidgetSize (widget, &size);
+ Word *word =
+ addWord (size.width, size.ascent, size.descent, 0, style);
+ word->content.type = core::Content::WIDGET_IN_FLOW;
+ word->content.widget = widget;
+ }
processWord (words->size () - 1);
//DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
@@ -1865,7 +2022,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
/* A break may not be the first word of a page, or directly after
the bullet/number (which is the first word) in a list item. (See
- also comment in Dw_page_size_request.) */
+ also comment in sizeRequest.) */
if (words->size () == 0 ||
(hasListitemValue && words->size () == 1)) {
/* This is a bit hackish: If a break is added as the
@@ -1874,23 +2031,23 @@ void Textblock::addParbreak (int space, core::style::Style *style)
a widget is used as a text box (lists, blockquotes, list
items etc) -- then we simply adjust the break before, in a
way that the space is in any case visible. */
- Widget *widget;
-
- /* Find the widget where to adjust the breakSpace. */
- for (widget = this;
- widget->getParent() &&
- widget->getParent()->instanceOf (Textblock::CLASS_ID);
+ /* Find the widget where to adjust the breakSpace. (Only
+ consider normal flow, no floats etc.) */
+ for (Widget *widget = this;
+ widget->getParent() != NULL &&
+ widget->getParent()->instanceOf (Textblock::CLASS_ID) &&
+ !OutOfFlowMgr::isRefOutOfFlow (widget->parentRef);
widget = widget->getParent ()) {
Textblock *textblock2 = (Textblock*)widget->getParent ();
int index = textblock2->hasListitemValue ? 1 : 0;
bool isfirst = (textblock2->words->getRef(index)->content.type
- == core::Content::WIDGET
+ == core::Content::WIDGET_IN_FLOW
&& textblock2->words->getRef(index)->content.widget
== widget);
if (!isfirst) {
- /* The page we searched for has been found. */
+ /* The text block we searched for has been found. */
Word *word2;
- int lineno = widget->parentRef;
+ int lineno = OutOfFlowMgr::getLineNoFromRef (widget->parentRef);
if (lineno > 0 &&
(word2 =
@@ -1899,7 +2056,8 @@ void Textblock::addParbreak (int space, core::style::Style *style)
word2->content.type == core::Content::BREAK) {
if (word2->content.breakSpace < space) {
word2->content.breakSpace = space;
- textblock2->queueResize (lineno, false);
+ textblock2->queueResize
+ (OutOfFlowMgr::createRefNormalFlow (lineno), false);
textblock2->mustQueueResize = false;
}
}
@@ -1907,6 +2065,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
}
/* Otherwise continue to examine parents. */
}
+
/* Return in any case. */
return;
}
@@ -1955,7 +2114,6 @@ void Textblock::addLinebreak (core::style::Style *style)
processWord (words->size () - 1);
}
-
/**
* \brief Search recursively through widget.
*
@@ -1964,6 +2122,10 @@ void Textblock::addLinebreak (core::style::Style *style)
*/
core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
{
+ //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n",
+ // 3 * level, "", getClassName (), this, allocation.x, allocation.y,
+ // allocation.width, allocation.ascent, allocation.descent);
+
int lineIndex, wordIndex;
Line *line;
@@ -1974,6 +2136,14 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
return NULL;
}
+ // First, search for widgets out of flow, notably floats, since
+ // there are cases where they overlap child textblocks. Should
+ // later be refined using z-index.
+ Widget *oofWidget =
+ outOfFlowMgr ? outOfFlowMgr->getWidgetAtPoint (x, y, level) : NULL;
+ if (oofWidget)
+ return oofWidget;
+
lineIndex = findLineIndex (y - allocation.y);
if (lineIndex < 0 || lineIndex >= lines->size ()) {
@@ -1985,7 +2155,7 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
Word *word = words->getRef (wordIndex);
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
core::Widget * childAtPoint;
childAtPoint = word->content.widget->getWidgetAtPoint (x, y,
level + 1);
@@ -2066,7 +2236,7 @@ void Textblock::changeLinkColor (int link, int newColor)
old_style->unref();
break;
}
- case core::Content::WIDGET:
+ case core::Content::WIDGET_IN_FLOW:
{ core::Widget *widget = word->content.widget;
styleAttrs = *widget->getStyle();
styleAttrs.color = core::style::Color::create (layout,
@@ -2118,4 +2288,137 @@ void Textblock::queueDrawRange (int index1, int index2)
}
}
+void Textblock::borderChanged (int y)
+{
+ PRINTF ("[%p] Border has changed: %d\n", this, y);
+ borderChanged (y, true);
+ PRINTF ("[%p] Done.\n", this);
+}
+
+void Textblock::borderChanged (int yWidget, bool extremesChanges)
+{
+ PRINTF ("[%p] Border has changed: %d (extremes: %s)\n",
+ this, yWidget, extremesChanges ? "true" : "false");
+
+ // Notice that this method is, unlike the other "borderChanged",
+ // called (i) with canvas coordinates, not widget coordinates, and
+ // (ii) for all nested textblocks, not only the containing block.
+
+ // findLineIndex expects widget coordinates
+ int lineIndex = findLineIndex (yWidget);
+ // Nothing to do at all, when lineIndex >= lines->size (),
+ // i. e. the change is below the bottom od this widget.
+ if (lineIndex < lines->size ()) {
+ int wrapLineIndex;
+ if (lineIndex < 0)
+ // Rewrap all.
+ wrapLineIndex = 0;
+ else
+ wrapLineIndex = lineIndex;
+
+ PRINTF ("[%p] Rewrapping from line %d.\n", this, wrapLineIndex);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (wrapLineIndex),
+ extremesChanges);
+
+ // lines->size () + 1 here, to get a possibly "missing" line.
+ // TODO: May there me more than one missing line? Should perhaps
+ // reworked again.
+
+ // We iterate over the lines to get the top of the line, as
+ // vertical position of the widget (provided that this is the
+ // only widget in the line). The allocation cannot be used here,
+ // since it cannot be assumed that the widget has been
+ // allocated.
+ for (int lineNo = wrapLineIndex; lineNo < lines->size () + 1; lineNo++) {
+ Textblock *childBlock = getTextblockForLine (lineNo);
+ if (childBlock)
+ // extremes only change for the containing block, so we pass
+ // extremesChanges = false for all other widgets.
+ childBlock->borderChanged (yWidget
+ - topOfPossiblyMissingLine (lineNo),
+ false);
+ }
+ }
+}
+
+Textblock *Textblock::getTextblockForLine (Line *line)
+{
+ return getTextblockForLine (line->firstWord, line->lastWord);
+}
+
+Textblock *Textblock::getTextblockForLine (int lineNo)
+{
+ int firstWord = lineNo == 0 ? 0 :lines->getRef(lineNo - 1)->lastWord + 1;
+ int lastWord = lineNo < lines->size() ?
+ lines->getRef(lineNo)->lastWord : words->size() - 1;
+ return getTextblockForLine (firstWord, lastWord);
+}
+
+Textblock *Textblock::getTextblockForLine (int firstWord, int lastWord)
+{
+ if (firstWord < words->size ()) {
+ for (int wordIndex = firstWord; wordIndex <= lastWord;
+ wordIndex++) {
+ Word *word = words->getRef (wordIndex);
+
+ if (word->content.type == core::Content::WIDGET_IN_FLOW &&
+ word->content.widget->instanceOf (Textblock::CLASS_ID)) {
+ //printf ("[%p] (line %d of %d (from %d to %d), word %d) ",
+ // this, lineNo, lines->size (), firstWord, lastWord,
+ // wordIndex);
+ //printWordShort (word);
+ //printf ("\n");
+
+ return (Textblock*)word->content.widget;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Includes margin, border, and padding.
+ */
+int Textblock::topOfPossiblyMissingLine (int lineNo)
+{
+ if (lineNo == 0)
+ return getStyle()->boxOffsetY();
+ else {
+ Line *prevLine = lines->getRef (lineNo - 1);
+ return prevLine->top + prevLine->boxAscent + prevLine->boxDescent +
+ prevLine->breakSpace + getStyle()->boxOffsetY();
+ }
+}
+
+int Textblock::heightOfPossiblyMissingLine (int lineNo)
+{
+ if (lineNo < lines->size()) {
+ // An existing line.
+ Line *line = lines->getRef (lineNo);
+ return line->boxAscent + line->boxDescent;
+ } else if (lineNo == lines->size()) {
+ // The line to be constructed: some words exist, but not the
+ // line. Accumulate the word heights. TODO Could be faster by
+ // accumulating them when words are added.
+
+ // Furthermore, this is in some cases incomplete: see
+ // doc/dw-out-of-flow.doc.
+
+ int h = 1;
+ int firstWord = lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0;
+ for (int i = firstWord; i < words->size(); i++) {
+ Word *word = words->getRef (i);
+ h = misc::max (h, word->size.ascent + word->size.descent);
+ }
+ return h;
+ } else
+ return 1;
+}
+
+core::Widget *Textblock::asWidget ()
+{
+ return this;
+}
+
} // namespace dw
diff --git a/dw/textblock.hh b/dw/textblock.hh
index d5b64e42..e5423c98 100644
--- a/dw/textblock.hh
+++ b/dw/textblock.hh
@@ -4,6 +4,7 @@
#include <limits.h>
#include "core.hh"
+#include "outofflowmgr.hh"
#include "../lout/misc.hh"
// These were used when improved line breaking and hyphenation were
@@ -11,6 +12,8 @@
#define PRINTF(fmt, ...)
#define PUTCHAR(ch)
+//#define DEBUG
+
namespace dw {
/**
@@ -18,10 +21,11 @@ namespace dw {
* of paragraphs.
*
* <div style="border: 2px solid #ffff00; margin-top: 0.5em;
- * margin-bottom: 0.5em; padding: 0.5em 1em;
- * background-color: #ffffe0"><b>Info:</b> The recent changes (line
- * breaking and hyphenation) have not yet been incorporated into this
- * documentation. See \ref dw-line-breaking.</div>
+ * margin-bottom: 0.5em; padding: 0.5em 1em; background-color:
+ * #ffffe0"><b>Info:</b> The recent changes (line breaking and
+ * hyphenation on one hand, floats on the other hand) have not yet
+ * been incorporated into this documentation. See \ref
+ * dw-line-breaking and \ref dw-out-of-flow.</div>
*
* <h3>Signals</h3>
*
@@ -146,7 +150,7 @@ namespace dw {
* necessary, or otherwise the line from which a rewrap is necessary.
*
*/
-class Textblock: public core::Widget
+class Textblock: public core::Widget, public OutOfFlowMgr::ContainingBlock
{
private:
/**
@@ -236,6 +240,9 @@ private:
static const char *hyphenDrawChar;
+ Textblock *containingBlock;
+ OutOfFlowMgr *outOfFlowMgr;
+
protected:
struct Paragraph
{
@@ -275,7 +282,7 @@ protected:
/* "top" is always relative to the top of the first line, i.e.
* page->lines[0].top is always 0. */
int top, boxAscent, boxDescent, contentAscent, contentDescent,
- breakSpace, leftOffset;
+ breakSpace, leftOffset, offsetCompleteWidget;
/* This is similar to descent, but includes the bottom margins of the
* widgets within this line. */
@@ -359,13 +366,14 @@ protected:
class TextblockIterator: public core::Iterator
{
private:
+ bool oofm;
int index;
public:
TextblockIterator (Textblock *textblock, core::Content::Type mask,
bool atEnd);
TextblockIterator (Textblock *textblock, core::Content::Type mask,
- int index);
+ bool oofm, int index);
lout::object::Object *clone();
int compareTo(lout::misc::Comparable *other);
@@ -375,6 +383,7 @@ protected:
void highlight (int start, int end, core::HighlightLayer layer);
void unhighlight (int direction, core::HighlightLayer layer);
void getAllocation (int start, int end, core::Allocation *allocation);
+ void print ();
};
friend class TextblockIterator;
@@ -424,7 +433,10 @@ protected:
/* These values are set by set_... */
int availWidth, availAscent, availDescent;
- int wrapRefLines, wrapRefParagraphs; /* [0 based] */
+ int wrapRefLines, wrapRefParagraphs; /* 0-based. Important: Both
+ are the line numbers, not
+ the value stored in
+ parentRef. */
lout::misc::SimpleVector <Line> *lines;
lout::misc::SimpleVector <Paragraph> *paragraphs;
@@ -480,25 +492,68 @@ protected:
core::Requisition *size);
/**
- * \brief Returns the x offset (the indentation plus any offset needed for
- * centering or right justification) for the line.
- *
- * The offset returned is relative to the page *content* (i.e. without
- * border etc.).
+ * Of nested text blocks, only the most inner one must regard the
+ * borders of floats.
*/
- inline int lineXOffsetContents (Line *line)
+ inline bool mustBorderBeRegarded (Line *line)
{
- return innerPadding + line->leftOffset +
- (line == lines->getFirstRef() ? line1OffsetEff : 0);
+ return getTextblockForLine (line) == NULL;
}
- /**
- * \brief Like lineXOffset, but relative to the allocation (i.e.
- * including border etc.).
- */
- inline int lineXOffsetWidget (Line *line)
+ inline bool mustBorderBeRegarded (int lineNo)
+ {
+ return getTextblockForLine (lineNo) == NULL;
+ }
+
+ void borderChanged (int yWidget, bool extremesChanges);
+
+ int diffXToContainingBlock, restWidthToContainingBlock,
+ diffYToContainingBlock;
+
+ inline int lineLeftBorder (int lineNo)
+ {
+ assert (diffXToContainingBlock != -1);
+ assert (diffYToContainingBlock != -1);
+
+ // Note that the line must not exist yet (but unless it is not
+ // the first line, the previous line, lineNo - 1, must).
+ int resultFromOOFM;
+ if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (lineNo))
+ resultFromOOFM =
+ containingBlock->outOfFlowMgr->getLeftBorder
+ (topOfPossiblyMissingLine (lineNo) + diffYToContainingBlock,
+ heightOfPossiblyMissingLine (lineNo))
+ - diffXToContainingBlock;
+ else
+ resultFromOOFM = 0;
+
+ // TODO: line->leftOffset is not regarded, which is correct, depending
+ // on where this method is called. Document; perhaps rename this method.
+ // (Update: was renamed.)
+ return innerPadding +
+ (lineNo == 0 ? line1OffsetEff : 0) +
+ lout::misc::max (getStyle()->boxOffsetX(), resultFromOOFM);
+ }
+
+ inline int lineRightBorder (int lineNo)
{
- return lineXOffsetContents (line) + getStyle()->boxOffsetX ();
+ assert (restWidthToContainingBlock != -1);
+ assert (diffYToContainingBlock != -1);
+
+ // Similar to lineLeftBorder().
+
+ int resultFromOOFM;
+ // TODO sizeRequest?
+ if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (lineNo))
+ resultFromOOFM =
+ containingBlock->outOfFlowMgr->getRightBorder
+ (topOfPossiblyMissingLine (lineNo) + diffYToContainingBlock,
+ heightOfPossiblyMissingLine (lineNo))
+ - restWidthToContainingBlock;
+ else
+ resultFromOOFM = 0;
+
+ return lout::misc::max (getStyle()->boxRestWidth(), resultFromOOFM);
}
inline int lineYOffsetWidgetAllocation (Line *line,
@@ -549,6 +604,12 @@ protected:
(Word::DIV_CHAR_AT_EOL | Word::PERM_DIV_CHAR)) ? 1 : 0;
}
+ Textblock *getTextblockForLine (Line *line);
+ Textblock *getTextblockForLine (int lineNo);
+ Textblock *getTextblockForLine (int firstWord, int lastWord);
+ int topOfPossiblyMissingLine (int lineNo);
+ int heightOfPossiblyMissingLine (int lineNo);
+
bool sendSelectionEvent (core::SelectionState::EventType eventType,
core::MousePositionEvent *event);
@@ -556,6 +617,12 @@ protected:
int *maxOfMinWidth, int *sumOfMaxWidth);
void processWord (int wordIndex);
virtual void wordWrap (int wordIndex, bool wrapAll);
+ void wrapWidgetOofRef (int wordIndex);
+ int searchMinBap (int firstWord, int lastWordm, int penaltyIndex,
+ bool correctAtEnd);
+ int considerHyphenation (int breakPos);
+ bool isHyphenationCandidate (Word *word);
+
void handleWordExtremes (int wordIndex);
void correctLastWordExtremes ();
@@ -575,6 +642,8 @@ protected:
void markSizeChange (int ref);
void markExtremesChange (int ref);
+ void notifySetAsTopLevel();
+ void notifySetParent();
void setWidth (int width);
void setAscent (int ascent);
void setDescent (int descent);
@@ -594,6 +663,7 @@ protected:
core::style::Style *style,
int numBreaks, int *breakPos,
core::Requisition *wordSize);
+ static bool isContainingBlock (Widget *widget);
public:
static int CLASS_ID;
@@ -639,6 +709,10 @@ public:
void changeLinkColor (int link, int newColor);
void changeWordStyle (int from, int to, core::style::Style *style,
bool includeFirstSpace, bool includeLastSpace);
+
+ // From OutOfFlowMgr::ContainingBlock:
+ void borderChanged (int y);
+ core::Widget *asWidget ();
};
} // namespace dw
diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc
index 245df374..285921b2 100644
--- a/dw/textblock_iterator.cc
+++ b/dw/textblock_iterator.cc
@@ -33,20 +33,34 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
bool atEnd):
core::Iterator (textblock, mask, atEnd)
{
- index = atEnd ? textblock->words->size () : -1;
+ if (atEnd) {
+ if (textblock->outOfFlowMgr) {
+ oofm = true;
+ index = textblock->outOfFlowMgr->getNumWidgets();
+ } else {
+ oofm = false;
+ index = textblock->words->size();
+ }
+ } else {
+ oofm = false;
+ index = -1;
+ }
+
content.type = atEnd ? core::Content::END : core::Content::START;
}
Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
core::Content::Type mask,
- int index):
+ bool oofm, int index):
core::Iterator (textblock, mask, false)
{
+ this->oofm = oofm;
this->index = index;
+ // TODO To be completely exact, oofm should be considered here.
if (index < 0)
content.type = core::Content::START;
- else if (index >= textblock->words->size ())
+ else if (index >= textblock->words->size())
content.type = core::Content::END;
else
content = textblock->words->getRef(index)->content;
@@ -54,12 +68,20 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
object::Object *Textblock::TextblockIterator::clone()
{
- return new TextblockIterator ((Textblock*)getWidget(), getMask(), index);
+ return
+ new TextblockIterator ((Textblock*)getWidget(), getMask(), oofm, index);
}
int Textblock::TextblockIterator::compareTo(misc::Comparable *other)
{
- return index - ((TextblockIterator*)other)->index;
+ TextblockIterator *otherTI = (TextblockIterator*)other;
+
+ if (oofm && !otherTI->oofm)
+ return +1;
+ else if (!oofm && otherTI->oofm)
+ return -1;
+ else
+ return index - otherTI->index;
}
bool Textblock::TextblockIterator::next ()
@@ -69,15 +91,52 @@ bool Textblock::TextblockIterator::next ()
if (content.type == core::Content::END)
return false;
+ short type;
+
do {
index++;
- if (index >= textblock->words->size ()) {
- content.type = core::Content::END;
- return false;
+
+ if (oofm) {
+ // Iterating over OOFM.
+ if (index >= textblock->outOfFlowMgr->getNumWidgets()) {
+ // End of OOFM list reached.
+ content.type = core::Content::END;
+ return false;
+ }
+ type = core::Content::WIDGET_OOF_CONT;
+ } else {
+ // Iterating over words list.
+ if (index < textblock->words->size ())
+ // Still words left.
+ type = textblock->words->getRef(index)->content.type;
+ else {
+ // End of words list reached.
+ if (textblock->outOfFlowMgr) {
+ oofm = true;
+ index = 0;
+ if (textblock->outOfFlowMgr->getNumWidgets() > 0)
+ // Start with OOFM widgets.
+ type = core::Content::WIDGET_OOF_CONT;
+ else {
+ // No OOFM widgets (number is 0).
+ content.type = core::Content::END;
+ return false;
+ }
+ } else {
+ // No OOFM widgets (no OOFM agt all).
+ content.type = core::Content::END;
+ return false;
+ }
+ }
}
- } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
+ } while ((type & getMask()) == 0);
+
+ if (oofm) {
+ content.type = core::Content::WIDGET_OOF_CONT;
+ content.widget = textblock->outOfFlowMgr->getWidget (index);
+ } else
+ content = textblock->words->getRef(index)->content;
- content = textblock->words->getRef(index)->content;
return true;
}
@@ -88,125 +147,180 @@ bool Textblock::TextblockIterator::prev ()
if (content.type == core::Content::START)
return false;
+ short type;
+
do {
index--;
- if (index < 0) {
- content.type = core::Content::START;
- return false;
+
+ if (oofm) {
+ // Iterating over OOFM.
+ if (index >= 0)
+ // Still widgets left.
+ type = core::Content::WIDGET_OOF_CONT;
+ else {
+ // Beginning of OOFM list reached. Continue with words.
+ oofm = false;
+ index = textblock->words->size() - 1;
+ if (index < 0) {
+ // There are no words. (Actually, this case should not
+ // happen: When there are OOF widgets, ther must be OOF
+ // references, or widgets in flow, which contain
+ // references.
+ content.type = core::Content::END;
+ return false;
+ }
+ type = textblock->words->getRef(index)->content.type;
+ }
+ } else {
+ // Iterating over words list.
+ if (index < 0) {
+ // Beginning of words list reached.
+ content.type = core::Content::START;
+ return false;
+ }
+ type = textblock->words->getRef(index)->content.type;
}
- } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
+ } while ((type & getMask()) == 0);
+
+ if (oofm) {
+ content.type = core::Content::WIDGET_OOF_CONT;
+ content.type = false;
+ content.widget = textblock->outOfFlowMgr->getWidget (index);
+ } else
+ content = textblock->words->getRef(index)->content;
- content = textblock->words->getRef(index)->content;
return true;
}
void Textblock::TextblockIterator::highlight (int start, int end,
core::HighlightLayer layer)
{
- Textblock *textblock = (Textblock*)getWidget();
- int index1 = index, index2 = index;
-
- int oldStartIndex = textblock->hlStart[layer].index;
- int oldStartChar = textblock->hlStart[layer].nChar;
- int oldEndIndex = textblock->hlEnd[layer].index;
- int oldEndChar = textblock->hlEnd[layer].nChar;
-
- if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
- /* nothing is highlighted */
- textblock->hlStart[layer].index = index;
- textblock->hlEnd[layer].index = index;
- }
-
- if (textblock->hlStart[layer].index >= index) {
- index2 = textblock->hlStart[layer].index;
- textblock->hlStart[layer].index = index;
- textblock->hlStart[layer].nChar = start;
- }
-
- if (textblock->hlEnd[layer].index <= index) {
- index2 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index;
- textblock->hlEnd[layer].nChar = end;
+ if (!oofm) {
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ int oldStartIndex = textblock->hlStart[layer].index;
+ int oldStartChar = textblock->hlStart[layer].nChar;
+ int oldEndIndex = textblock->hlEnd[layer].index;
+ int oldEndChar = textblock->hlEnd[layer].nChar;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
+ /* nothing is highlighted */
+ textblock->hlStart[layer].index = index;
+ textblock->hlEnd[layer].index = index;
+ }
+
+ if (textblock->hlStart[layer].index >= index) {
+ index2 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index;
+ textblock->hlStart[layer].nChar = start;
+ }
+
+ if (textblock->hlEnd[layer].index <= index) {
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index;
+ textblock->hlEnd[layer].nChar = end;
+ }
+
+ if (oldStartIndex != textblock->hlStart[layer].index ||
+ oldStartChar != textblock->hlStart[layer].nChar ||
+ oldEndIndex != textblock->hlEnd[layer].index ||
+ oldEndChar != textblock->hlEnd[layer].nChar)
+ textblock->queueDrawRange (index1, index2);
}
- if (oldStartIndex != textblock->hlStart[layer].index ||
- oldStartChar != textblock->hlStart[layer].nChar ||
- oldEndIndex != textblock->hlEnd[layer].index ||
- oldEndChar != textblock->hlEnd[layer].nChar)
- textblock->queueDrawRange (index1, index2);
+ // TODO What about OOF widgets?
}
void Textblock::TextblockIterator::unhighlight (int direction,
core::HighlightLayer layer)
{
- Textblock *textblock = (Textblock*)getWidget();
- int index1 = index, index2 = index;
-
- if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
- return;
-
- int oldStartIndex = textblock->hlStart[layer].index;
- int oldStartChar = textblock->hlStart[layer].nChar;
- int oldEndIndex = textblock->hlEnd[layer].index;
- int oldEndChar = textblock->hlEnd[layer].nChar;
-
- if (direction == 0) {
- index1 = textblock->hlStart[layer].index;
- index2 = textblock->hlEnd[layer].index;
- textblock->hlStart[layer].index = 1;
- textblock->hlEnd[layer].index = 0;
- } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
- index1 = textblock->hlStart[layer].index;
- textblock->hlStart[layer].index = index + 1;
- textblock->hlStart[layer].nChar = 0;
- } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
- index1 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index - 1;
- textblock->hlEnd[layer].nChar = INT_MAX;
+ if (!oofm) {
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
+ return;
+
+ int oldStartIndex = textblock->hlStart[layer].index;
+ int oldStartChar = textblock->hlStart[layer].nChar;
+ int oldEndIndex = textblock->hlEnd[layer].index;
+ int oldEndChar = textblock->hlEnd[layer].nChar;
+
+ if (direction == 0) {
+ index1 = textblock->hlStart[layer].index;
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlStart[layer].index = 1;
+ textblock->hlEnd[layer].index = 0;
+ } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
+ index1 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index + 1;
+ textblock->hlStart[layer].nChar = 0;
+ } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
+ index1 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index - 1;
+ textblock->hlEnd[layer].nChar = INT_MAX;
+ }
+
+ if (oldStartIndex != textblock->hlStart[layer].index ||
+ oldStartChar != textblock->hlStart[layer].nChar ||
+ oldEndIndex != textblock->hlEnd[layer].index ||
+ oldEndChar != textblock->hlEnd[layer].nChar)
+ textblock->queueDrawRange (index1, index2);
}
- if (oldStartIndex != textblock->hlStart[layer].index ||
- oldStartChar != textblock->hlStart[layer].nChar ||
- oldEndIndex != textblock->hlEnd[layer].index ||
- oldEndChar != textblock->hlEnd[layer].nChar)
- textblock->queueDrawRange (index1, index2);
+ // TODO What about OOF widgets?
}
void Textblock::TextblockIterator::getAllocation (int start, int end,
core::Allocation *allocation)
{
Textblock *textblock = (Textblock*)getWidget();
- int lineIndex = textblock->findLineOfWord (index);
- Line *line = textblock->lines->getRef (lineIndex);
- Word *word = textblock->words->getRef (index);
-
- allocation->x =
- textblock->allocation.x + textblock->lineXOffsetWidget (line);
- for (int i = line->firstWord; i < index; i++) {
- Word *w = textblock->words->getRef(i);
- allocation->x += w->size.width + w->effSpace;
- }
- if (start > 0 && word->content.type == core::Content::TEXT) {
- allocation->x += textblock->textWidth (word->content.text, 0, start,
- word->style);
- }
- allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
- word->size.ascent;
-
- allocation->width = word->size.width;
- if (word->content.type == core::Content::TEXT) {
- int wordEnd = strlen(word->content.text);
-
- if (start > 0 || end < wordEnd) {
- end = misc::min(end, wordEnd); /* end could be INT_MAX */
- allocation->width =
- textblock->textWidth (word->content.text, start, end - start,
- word->style);
+ if (oofm) {
+ // TODO Consider start and end?
+ *allocation =
+ *(textblock->outOfFlowMgr->getWidget(index)->getAllocation());
+ } else {
+ int lineIndex = textblock->findLineOfWord (index);
+ Line *line = textblock->lines->getRef (lineIndex);
+ Word *word = textblock->words->getRef (index);
+
+ allocation->x =
+ textblock->allocation.x + line->offsetCompleteWidget;
+
+ for (int i = line->firstWord; i < index; i++) {
+ Word *w = textblock->words->getRef(i);
+ allocation->x += w->size.width + w->effSpace;
+ }
+ if (start > 0 && word->content.type == core::Content::TEXT) {
+ allocation->x += textblock->textWidth (word->content.text, 0, start,
+ word->style);
+ }
+ allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
+ word->size.ascent;
+
+ allocation->width = word->size.width;
+ if (word->content.type == core::Content::TEXT) {
+ int wordEnd = strlen(word->content.text);
+
+ if (start > 0 || end < wordEnd) {
+ end = misc::min(end, wordEnd); /* end could be INT_MAX */
+ allocation->width =
+ textblock->textWidth (word->content.text, start, end - start,
+ word->style);
+ }
}
+ allocation->ascent = word->size.ascent;
+ allocation->descent = word->size.descent;
}
- allocation->ascent = word->size.ascent;
- allocation->descent = word->size.descent;
+}
+
+void Textblock::TextblockIterator::print ()
+{
+ Iterator::print ();
+ printf (", oofm = %s, index = %d", oofm ? "true" : "false", index);
+
}
} // namespace dw
diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc
index 35273565..e35744dd 100644
--- a/dw/textblock_linebreaking.cc
+++ b/dw/textblock_linebreaking.cc
@@ -210,21 +210,7 @@ void Textblock::BadnessAndPenalty::print ()
void Textblock::printWordShort (Word *word)
{
- switch(word->content.type) {
- case core::Content::TEXT:
- printf ("\"%s\"", word->content.text);
- break;
- case core::Content::WIDGET:
- printf ("<widget: %p (%s)>",
- word->content.widget, word->content.widget->getClassName());
- break;
- case core::Content::BREAK:
- printf ("<break>");
- break;
- default:
- printf ("<?>");
- break;
- }
+ core::Content::print (&(word->content));
}
void Textblock::printWordFlags (short flags)
@@ -315,6 +301,12 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
PRINTF ("[%p] ADD_LINE (%d, %d) => %d\n",
this, firstWord, lastWord, lines->size ());
+ //for (int i = firstWord; i <= lastWord; i++) {
+ // printf (" word %d: ", i);
+ // printWord (words->getRef (i));
+ // printf ("\n");
+ //}
+
Word *lastWordOfLine = words->getRef(lastWord);
// Word::totalWidth includes the hyphen (which is what we want here).
int lineWidth = lastWordOfLine->totalWidth;
@@ -374,6 +366,25 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
for(int i = line->firstWord; i <= line->lastWord; i++)
accumulateWordForLine (lineIndex, i);
+ // Especially empty lines (possible when there are floats) have
+ // zero height, which may cause endless loops. For this reasons,
+ // the height should be positive.
+ line->boxAscent = misc::max (line->boxAscent, 1);
+
+ // Calculate offsetCompleteWidget, which includes also floats.
+ int resultFromOOFM;
+ if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (line))
+ resultFromOOFM =
+ containingBlock->outOfFlowMgr->getLeftBorder
+ (line->top + getStyle()->boxOffsetY() + diffYToContainingBlock,
+ line->boxAscent + line->boxDescent)
+ - diffXToContainingBlock;
+ else
+ resultFromOOFM = 0;
+ line->offsetCompleteWidget =
+ innerPadding + line->leftOffset + (lineIndex == 0 ? line1OffsetEff : 0) +
+ misc::max (getStyle()->boxOffsetX(), resultFromOOFM);
+
PRINTF (" line[%d].top = %d\n", lines->size () - 1, line->top);
PRINTF (" line[%d].boxAscent = %d\n", lines->size () - 1, line->boxAscent);
PRINTF (" line[%d].boxDescent = %d\n",
@@ -452,14 +463,12 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll)
PRINTF ("[%p] WORD_WRAP (%d, %s)\n",
this, wordIndex, wrapAll ? "true" : "false");
- Word *word;
-
if (!wrapAll)
removeTemporaryLines ();
initLine1Offset (wordIndex);
- word = words->getRef (wordIndex);
+ Word *word = words->getRef (wordIndex);
word->effSpace = word->origSpace;
accumulateWordData (wordIndex);
@@ -468,16 +477,59 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll)
//printWord (word);
//printf ("\n");
+ if (word->content.type == core::Content::WIDGET_OOF_REF)
+ wrapWidgetOofRef (wordIndex);
+
int penaltyIndex = calcPenaltyIndexForNewLine ();
bool newLine;
do {
+ // This variable is set to true, if, due to floats, this line is
+ // smaller than following lines will be (and, at the end, there
+ // will be surely lines without floats). If this is the case, lines
+ // may, in an extreme case, be left empty.
+
+ // (In other cases, lines are never left empty, even if this means
+ // that the contents is wider than the available witdh. Leaving
+ // lines empty does not make sense without floats, since there will
+ // be no possibility with more space anymore.)
+
+ bool thereWillBeMoreSpace;
+ if (containingBlock->outOfFlowMgr == NULL) {
+ thereWillBeMoreSpace = false;
+ PRINTF (" thereWillBeMoreSpace = false (no OOFM)\n");
+ } else {
+ assert (diffXToContainingBlock != -1);
+ assert (restWidthToContainingBlock != -1);
+ assert (diffYToContainingBlock != -1);
+
+ int y =
+ topOfPossiblyMissingLine (lines->size ()) + diffYToContainingBlock;
+ int h = heightOfPossiblyMissingLine (lines->size ());
+ int l =
+ containingBlock->outOfFlowMgr->getLeftBorder (y, h)
+ - diffXToContainingBlock;
+ int r =
+ containingBlock->outOfFlowMgr->getRightBorder (y, h)
+ - restWidthToContainingBlock;
+
+ thereWillBeMoreSpace = l > 0 || r > 0;
+
+ PRINTF (" thereWillBeMoreSpace = %s (y = %d, l = %d, r = %d)\n",
+ thereWillBeMoreSpace ? "true" : "false", y, l, r);
+ }
+
bool tempNewLine = false;
int firstIndex =
lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1;
int searchUntil;
- if (wrapAll && wordIndex >= firstIndex && wordIndex == words->size() -1) {
+ if (wordIndex < firstIndex)
+ // Current word is already part of a line (ending with
+ // firstIndex - 1), so no new line has to be added.
+ newLine = false;
+ else if (wrapAll && wordIndex >= firstIndex &&
+ wordIndex == words->size() -1) {
newLine = true;
searchUntil = wordIndex;
tempNewLine = true;
@@ -493,13 +545,21 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll)
// Break the line when too tight, but only when there is a
// possible break point so far. (TODO: I've forgotten the
// original bug which is fixed by this.)
+
+ // Exception of the latter rule: thereWillBeMoreSpace; see
+ // above, where it is defined.
+
bool possibleLineBreak = false;
- for (int i = firstIndex; !possibleLineBreak && i <= wordIndex - 1; i++)
+ for (int i = firstIndex;
+ !(thereWillBeMoreSpace || possibleLineBreak)
+ && i <= wordIndex - 1;
+ i++)
if (words->getRef(i)->badnessAndPenalty
.lineCanBeBroken (penaltyIndex))
possibleLineBreak = true;
- if (possibleLineBreak && word->badnessAndPenalty.lineTooTight ()) {
+ if ((thereWillBeMoreSpace || possibleLineBreak)
+ && word->badnessAndPenalty.lineTooTight ()) {
newLine = true;
searchUntil = wordIndex - 1;
PRINTF (" NEW LINE: line too tight\n");
@@ -516,118 +576,68 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll)
// newLine is calculated as "true".
mustQueueResize = true;
+ PRINTF ("[%p] special case? newLine = %s, wrapAll = %s => "
+ "mustQueueResize = %s\n", this, newLine ? "true" : "false",
+ wrapAll ? "true" : "false", mustQueueResize ? "true" : "false");
+
if(newLine) {
accumulateWordData (wordIndex);
int wordIndexEnd = wordIndex;
bool lineAdded;
do {
- PRINTF (" searching from %d to %d\n", firstIndex, searchUntil);
-
- int breakPos = -1;
- for (int i = firstIndex; i <= searchUntil; i++) {
- Word *w = words->getRef(i);
-
- //printf (" %d (of %d): ", i, words->size ());
- //printWord (w);
- //printf ("\n");
-
- if (breakPos == -1 ||
- w->badnessAndPenalty.compareTo
- (penaltyIndex,
- &words->getRef(breakPos)->badnessAndPenalty) <= 0)
- // "<=" instead of "<" in the next lines tends to result in
- // more words per line -- theoretically. Practically, the
- // case "==" will never occur.
- breakPos = i;
- }
-
- PRINTF (" breakPos = %d\n", breakPos);
-
- if (wrapAll && searchUntil == words->size () - 1) {
- // Since no break and no space is added, the last word
- // will have a penalty of inf. Actually, it should be
- // less, since it is the last word. However, since more
- // words may follow, the penalty is not changesd, but
- // here, the search is corrected (maybe only
- // temporary).
-
- // (Notice that it was once (temporally) set to -inf,
- // not 0, but this will make e.g. test/table-1.html not
- // work.)
- Word *lastWord = words->getRef (searchUntil);
- BadnessAndPenalty correctedBap = lastWord->badnessAndPenalty;
- correctedBap.setPenalty (0);
- if (correctedBap.compareTo
- (penaltyIndex,
- &words->getRef(breakPos)->badnessAndPenalty) <= 0) {
- breakPos = searchUntil;
- PRINTF (" corrected: breakPos = %d\n", breakPos);
- }
- }
-
- int hyphenatedWord = -1;
- Word *word1 = words->getRef(breakPos);
- PRINTF ("[%p] line (broken at word %d): ", this, breakPos);
- //word1->badnessAndPenalty.print ();
- PRINTF ("\n");
-
- if (word1->badnessAndPenalty.lineTight () &&
- (word1->flags & Word::CAN_BE_HYPHENATED) &&
- word1->style->x_lang[0] &&
- word1->content.type == core::Content::TEXT &&
- Hyphenator::isHyphenationCandidate (word1->content.text))
- hyphenatedWord = breakPos;
-
- if (word1->badnessAndPenalty.lineLoose () &&
- breakPos + 1 < words->size ()) {
- Word *word2 = words->getRef(breakPos + 1);
- if ((word2->flags & Word::CAN_BE_HYPHENATED) &&
- word2->style->x_lang[0] &&
- word2->content.type == core::Content::TEXT &&
- Hyphenator::isHyphenationCandidate (word2->content.text))
- hyphenatedWord = breakPos + 1;
- }
-
- PRINTF ("[%p] breakPos = %d, hyphenatedWord = %d\n",
- this, breakPos, hyphenatedWord);
-
- if(hyphenatedWord == -1) {
- addLine (firstIndex, breakPos, tempNewLine);
- PRINTF ("[%p] new line %d (%s), from %d to %d\n",
- this, lines->size() - 1,
- tempNewLine ? "temporally" : "permanently",
- firstIndex, breakPos);
+ if (firstIndex > searchUntil) {
+ // empty line
+ assert (searchUntil == firstIndex - 1);
+ addLine (firstIndex, firstIndex - 1, tempNewLine);
lineAdded = true;
penaltyIndex = calcPenaltyIndexForNewLine ();
} else {
- // TODO hyphenateWord() should return whether something has
- // changed at all. So that a second run, with
- // !word->canBeHyphenated, is unnecessary.
- // TODO Update: for this, searchUntil == 0 should be checked.
- PRINTF ("[%p] old searchUntil = %d ...\n", this, searchUntil);
- int n = hyphenateWord (hyphenatedWord);
- searchUntil += n;
- if (hyphenatedWord <= wordIndex)
- wordIndexEnd += n;
- PRINTF ("[%p] -> new searchUntil = %d ...\n", this, searchUntil);
- lineAdded = false;
+ int breakPos =
+ searchMinBap (firstIndex, searchUntil, penaltyIndex, wrapAll);
+ int hyphenatedWord = considerHyphenation (breakPos);
+
+ PRINTF ("[%p] breakPos = %d, hyphenatedWord = %d\n",
+ this, breakPos, hyphenatedWord);
- // update word pointer as hyphenateWord() can trigger a
- // reorganization of the words structure
- word = words->getRef (wordIndex);
+ if(hyphenatedWord == -1) {
+ addLine (firstIndex, breakPos, tempNewLine);
+ PRINTF ("[%p] new line %d (%s), from %d to %d\n",
+ this, lines->size() - 1,
+ tempNewLine ? "temporally" : "permanently",
+ firstIndex, breakPos);
+ lineAdded = true;
+ penaltyIndex = calcPenaltyIndexForNewLine ();
+ } else {
+ // TODO hyphenateWord() should return whether
+ // something has changed at all. So that a second
+ // run, with !word->canBeHyphenated, is unnecessary.
+ // TODO Update: for this, searchUntil == 0 should be
+ // checked.
+ PRINTF ("[%p] old searchUntil = %d ...\n", this, searchUntil);
+ int n = hyphenateWord (hyphenatedWord);
+ searchUntil += n;
+ if (hyphenatedWord <= wordIndex)
+ wordIndexEnd += n;
+ PRINTF ("[%p] -> new searchUntil = %d ...\n",
+ this, searchUntil);
+ lineAdded = false;
+
+ // update word pointer as hyphenateWord() can trigger a
+ // reorganization of the words structure
+ word = words->getRef (wordIndex);
+ }
+
+ PRINTF ("[%p] accumulating again from %d to %d\n",
+ this, breakPos + 1, wordIndexEnd);
+ for(int i = breakPos + 1; i <= wordIndexEnd; i++)
+ accumulateWordData (i);
}
-
- PRINTF ("[%p] accumulating again from %d to %d\n",
- this, breakPos + 1, wordIndexEnd);
- for(int i = breakPos + 1; i <= wordIndexEnd; i++)
- accumulateWordData (i);
-
} while(!lineAdded);
}
} while (newLine);
- if(word->content.type == core::Content::WIDGET) {
+ if(word->content.type == core::Content::WIDGET_IN_FLOW) {
// Set parentRef for the child, when necessary.
//
// parentRef is set for the child already, when a line is
@@ -644,7 +654,8 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll)
firstWordWithoutLine = lines->getLastRef()->lastWord + 1;
if (wordIndex >= firstWordWithoutLine) {
- word->content.widget->parentRef = lines->size ();
+ word->content.widget->parentRef =
+ OutOfFlowMgr::createRefNormalFlow (lines->size ());
PRINTF ("The %s %p is assigned parentRef = %d.\n",
word->content.widget->getClassName(), word->content.widget,
word->content.widget->parentRef);
@@ -652,6 +663,121 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll)
}
}
+void Textblock::wrapWidgetOofRef (int wordIndex)
+{
+ int top;
+ if (lines->size() == 0)
+ top = 0;
+ else {
+ Line *prevLine = lines->getLastRef ();
+ top = prevLine->top + prevLine->boxAscent +
+ prevLine->boxDescent + prevLine->breakSpace;
+ }
+
+ containingBlock->outOfFlowMgr->tellPosition
+ (words->getRef(wordIndex)->content.widget,
+ // "diffYToContainingBlock" is already defined here; it is
+ // set by the parent, or, when this is the containing block,
+ // even earlier.
+ diffYToContainingBlock + top + getStyle()->boxOffsetY());
+
+ // TODO: compare old/new values of calcAvailWidth(...);
+ int firstIndex =
+ lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1;
+ assert (firstIndex <= wordIndex);
+ for (int i = firstIndex; i <= wordIndex; i++)
+ accumulateWordData (i);
+}
+
+int Textblock::searchMinBap (int firstWord, int lastWord, int penaltyIndex,
+ bool correctAtEnd)
+{
+ PRINTF (" searching from %d to %d\n", firstWord, lastWord);
+
+ int pos = -1;
+
+ for (int i = firstWord; i <= lastWord; i++) {
+ Word *w = words->getRef(i);
+
+ //printf (" %d (of %d): ", i, words->size ());
+ //printWord (w);
+ //printf ("\n");
+
+ if (pos == -1 ||
+ w->badnessAndPenalty.compareTo (penaltyIndex,
+ &words->getRef(pos)
+ ->badnessAndPenalty) <= 0)
+ // "<=" instead of "<" in the next lines tends to result in
+ // more words per line -- theoretically. Practically, the
+ // case "==" will never occur.
+ pos = i;
+ }
+
+ PRINTF (" found at %d\n", pos);
+
+ if (correctAtEnd && lastWord == words->size () - 1) {
+ // Since no break and no space is added, the last word will have
+ // a penalty of inf. Actually, it should be less, since it is
+ // the last word. However, since more words may follow, the
+ // penalty is not changed, but here, the search is corrected
+ // (maybe only temporary).
+
+ // (Notice that it was once (temporally) set to -inf, not 0, but
+ // this will make e.g. test/table-1.html not work.)
+ Word *w = words->getRef (lastWord);
+ BadnessAndPenalty correctedBap = w->badnessAndPenalty;
+ correctedBap.setPenalty (0);
+ if (correctedBap.compareTo(penaltyIndex,
+ &words->getRef(pos)->badnessAndPenalty) <= 0) {
+ pos = lastWord;
+ PRINTF (" corrected => %d\n", pos);
+ }
+ }
+
+ return pos;
+}
+
+
+/**
+ * Suggest a word to hyphenate, when breaking at breakPos is
+ * planned. Return a word index or -1, when hyphenation makes no
+ * sense.
+ */
+int Textblock::considerHyphenation (int breakPos)
+{
+ int hyphenatedWord = -1;
+
+ Word *word1 = words->getRef(breakPos);
+ PRINTF ("[%p] line (broken at word %d): ", this, breakPos);
+ //word1->badnessAndPenalty.print ();
+ PRINTF ("\n");
+
+ // A tight line: maybe, after hyphenation, some parts of the last
+ // word of this line can be put into the next line.
+ if (word1->badnessAndPenalty.lineTight () &&
+ isHyphenationCandidate (word1))
+ hyphenatedWord = breakPos;
+
+ // A loose line: maybe, after hyphenation, some parts of the first
+ // word of the next line can be put into this line.
+ if (word1->badnessAndPenalty.lineLoose () &&
+ breakPos + 1 < words->size ()) {
+ Word *word2 = words->getRef(breakPos + 1);
+ if (isHyphenationCandidate (word2))
+ hyphenatedWord = breakPos + 1;
+ }
+
+ return hyphenatedWord;
+}
+
+bool Textblock::isHyphenationCandidate (Word *word)
+{
+ return (word->flags & Word::CAN_BE_HYPHENATED) &&
+ word->style->x_lang[0] &&
+ word->content.type == core::Content::TEXT &&
+ Hyphenator::isHyphenationCandidate (word->content.text);
+}
+
/**
* Counter part to wordWrap(), but for extremes, not size calculation.
*/
@@ -817,9 +943,16 @@ int Textblock::hyphenateWord (int wordIndex)
PRINTF (" [%d] + nothing\n", wordIndex + i);
}
}
+ }
+
+ // AccumulateWordData() will calculate the width, which depends
+ // on the borders (possibly limited by floats), which depends on
+ // the widgeds so far. For this reason, it is important to first
+ // make all words consistent before calling
+ // accumulateWordData(); therefore the second loop.
+ for (int i = 0; i < numBreaks + 1; i++)
accumulateWordData (wordIndex + i);
- }
PRINTF (" finished\n");
@@ -839,8 +972,12 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
Line *line = lines->getRef (lineIndex);
Word *word = words->getRef (wordIndex);
- PRINTF (" %d + %d / %d + %d\n", line->boxAscent, line->boxDescent,
+ PRINTF ("[%p] ACCUMULATE_WORD_FOR_LINE (%d, %d): %d + %d / %d + %d\n",
+ this, lineIndex, wordIndex, line->boxAscent, line->boxDescent,
word->size.ascent, word->size.descent);
+ //printf (" ");
+ //printWord (word);
+ //printf ("\n");
line->boxAscent = misc::max (line->boxAscent, word->size.ascent);
line->boxDescent = misc::max (line->boxDescent, word->size.descent);
@@ -855,7 +992,7 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
len += word->style->font->ascent / 3;
line->contentDescent = misc::max (line->contentDescent, len);
- if (word->content.type == core::Content::WIDGET) {
+ if (word->content.type == core::Content::WIDGET_IN_FLOW) {
int collapseMarginTop = 0;
line->marginDescent =
@@ -879,7 +1016,8 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
+ word->content.widget->getStyle()->margin.top
- collapseMarginTop);
- word->content.widget->parentRef = lineIndex;
+ word->content.widget->parentRef =
+ OutOfFlowMgr::createRefNormalFlow (lineIndex);
} else {
line->marginDescent =
misc::max (line->marginDescent, line->boxDescent);
@@ -945,18 +1083,27 @@ void Textblock::accumulateWordData (int wordIndex)
int Textblock::calcAvailWidth (int lineIndex)
{
- int availWidth =
- this->availWidth - getStyle()->boxDiffWidth() - innerPadding;
+ int availWidth = this->availWidth - innerPadding;
if (limitTextWidth &&
layout->getUsesViewport () &&
- availWidth > layout->getWidthViewport () - 10)
+ // margin/border/padding will be subtracted later, via OOFM.
+ availWidth - getStyle()->boxDiffWidth()
+ > layout->getWidthViewport () - 10)
availWidth = layout->getWidthViewport () - 10;
if (lineIndex == 0)
availWidth -= line1OffsetEff;
- //PRINTF("[%p] CALC_AVAIL_WIDTH => %d - %d - %d = %d\n",
- // this, this->availWidth, getStyle()->boxDiffWidth(), innerPadding,
- // availWidth);
+ // TODO if the result is too small, but only in some cases
+ // (e. g. because of floats), the caller should skip a
+ // line. General distinction?
+ int leftBorder = lineLeftBorder (lineIndex);
+ int rightBorder = lineRightBorder (lineIndex);
+ availWidth -= (leftBorder + rightBorder);
+
+ PRINTF ("[%p] CALC_AVAIL_WIDTH (%d of %d) => %d - %d - (%d + %d)"
+ " = %d\n", this, lineIndex, lines->size(), this->availWidth,
+ innerPadding, leftBorder, rightBorder,
+ availWidth);
return availWidth;
}
@@ -973,7 +1120,7 @@ void Textblock::initLine1Offset (int wordIndex)
} else {
int indent = 0;
- if (word->content.type == core::Content::WIDGET &&
+ if (word->content.type == core::Content::WIDGET_IN_FLOW &&
word->content.widget->blockLevel() == true) {
/* don't use text-indent when nesting blocks */
} else {
@@ -1064,8 +1211,14 @@ void Textblock::rewrap ()
for (int i = firstWord; i < words->size (); i++) {
Word *word = words->getRef (i);
-
- if (word->content.type == core::Content::WIDGET)
+ if (word->content.type == core::Content::WIDGET_OOF_REF)
+ containingBlock->outOfFlowMgr->tellNoPosition (word->content.widget);
+ }
+
+ for (int i = firstWord; i < words->size (); i++) {
+ Word *word = words->getRef (i);
+
+ if (word->content.type == core::Content::WIDGET_IN_FLOW)
calcWidgetSize (word->content.widget, &word->size);
wordWrap (i, false);
diff --git a/dw/types.cc b/dw/types.cc
index 86836bc1..a2f0737e 100644
--- a/dw/types.cc
+++ b/dw/types.cc
@@ -268,5 +268,58 @@ void Region::addRectangle (Rectangle *rPointer)
rectangleList->append (r);
}
+Content::Type Content::maskForSelection (bool followReferences)
+{
+ Content::Type widgetMask = (Content::Type)
+ (Content::WIDGET_IN_FLOW |
+ (followReferences ? Content::WIDGET_OOF_REF : Content::WIDGET_OOF_CONT));
+ return (Content::Type)(Content::SELECTION_CONTENT | widgetMask);
+}
+
+void Content::print (Content *content)
+{
+ switch(content->type) {
+ case START:
+ printf ("<start>");
+ break;
+ case END:
+ printf ("<end>");
+ break;
+ case TEXT:
+ printf ("\"%s\"", content->text);
+ break;
+ case WIDGET_IN_FLOW:
+ printf ("<widget in flow: %p (%s)>",
+ content->widget, content->widget->getClassName());
+ break;
+ case WIDGET_OOF_REF:
+ printf ("<widget oof ref: %p (%s)>",
+ content->widget, content->widget->getClassName());
+ break;
+ case WIDGET_OOF_CONT:
+ printf ("<widge oof cont: %p (%s)>",
+ content->widget, content->widget->getClassName());
+ break;
+ case BREAK:
+ printf ("<break>");
+ break;
+ default:
+ printf ("<%d?>", content->type);
+ break;
+ }
+}
+
+void Content::printMask (Type mask)
+{
+ printf ("%s:%s:%s:%s:%s:%s:%s",
+ (mask & START) ? "st" : "--",
+ (mask & END) ? "en" : "--",
+ (mask & TEXT) ? "tx" : "--",
+ (mask & WIDGET_IN_FLOW) ? "wf" : "--",
+ (mask & WIDGET_OOF_REF) ? "Wr" : "--",
+ (mask & WIDGET_OOF_CONT) ? "Wc" : "--",
+ (mask & BREAK) ? "br" : "--");
+}
+
} // namespace core
} // namespace dw
diff --git a/dw/types.hh b/dw/types.hh
index f04fc138..0e664895 100644
--- a/dw/types.hh
+++ b/dw/types.hh
@@ -188,11 +188,27 @@ struct Content
START = 1 << 0,
END = 1 << 1,
TEXT = 1 << 2,
- WIDGET = 1 << 3,
- BREAK = 1 << 4,
+
+ /** \brief widget in normal flow, so that _this_ widget
+ (containing this content) is both container (parent) and
+ generator */
+ WIDGET_IN_FLOW = 1 << 3,
+
+ /** \brief widget out of flow (OOF); _this_ widget (containing
+ this content) is only the container (parent), but _not_
+ generator */
+ WIDGET_OOF_CONT = 1 << 4,
+
+ /** \brief reference to a widget out of flow (OOF); _this_
+ widget (containing this content) is only the generator
+ (parent), but _not_ container */
+ WIDGET_OOF_REF = 1 << 5,
+ BREAK = 1 << 6,
+
ALL = 0xff,
REAL_CONTENT = 0xff ^ (START | END),
- SELECTION_CONTENT = TEXT | WIDGET | BREAK
+ SELECTION_CONTENT = TEXT | BREAK, // WIDGET_* must be set additionally
+ ANY_WIDGET = WIDGET_IN_FLOW | WIDGET_OOF_CONT | WIDGET_OOF_REF,
};
/* Content is embedded in struct Word therefore we
@@ -205,6 +221,10 @@ struct Content
Widget *widget;
int breakSpace;
};
+
+ static Content::Type maskForSelection (bool followReferences);
+ static void print (Content *content);
+ static void printMask (Type mask);
};
} // namespace core
diff --git a/dw/widget.cc b/dw/widget.cc
index 1d9f96e4..8f5bc5d5 100644
--- a/dw/widget.cc
+++ b/dw/widget.cc
@@ -39,7 +39,7 @@ Widget::Widget ()
registerName ("dw::core::Widget", &CLASS_ID);
flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS);
- parent = NULL;
+ parent = generator = NULL;
layout = NULL;
allocation.x = -1;
@@ -108,6 +108,8 @@ void Widget::setParent (Widget *parent)
if (!buttonSensitiveSet)
buttonSensitive = parent->buttonSensitive;
+ notifySetParent();
+
//DBG_OBJ_ASSOC (widget, parent);
//printf ("The %s %p becomes a child of the %s %p\n",
// getClassName(), this, parent->getClassName(), parent);
@@ -116,7 +118,8 @@ void Widget::setParent (Widget *parent)
void Widget::queueDrawArea (int x, int y, int width, int height)
{
/** \todo Maybe only the intersection? */
- layout->queueDraw (x + allocation.x, y + allocation.y, width, height);
+ if (layout)
+ layout->queueDraw (x + allocation.x, y + allocation.y, width, height);
_MSG("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height);
}
@@ -127,10 +130,8 @@ void Widget::queueResize (int ref, bool extremesChanged)
{
Widget *widget2, *child;
- //DEBUG_MSG (DEBUG_SIZE,
- // "a %stop-level %s with parent_ref = %d has changed its size\n",
- // widget->parent ? "non-" : "",
- // gtk_type_name (GTK_OBJECT_TYPE (widget)), widget->parent_ref);
+ //printf("The %stop-level %s %p with parentRef = %d has changed its size.\n",
+ // parent ? "non-" : "", getClassName(), this, parentRef);
setFlags (NEEDS_RESIZE);
setFlags (NEEDS_ALLOCATE);
@@ -148,12 +149,10 @@ void Widget::queueResize (int ref, bool extremesChanged)
widget2->markSizeChange (child->parentRef);
widget2->setFlags (NEEDS_ALLOCATE);
- //DEBUG_MSG (DEBUG_ALLOC,
- // "setting DW_NEEDS_ALLOCATE for a %stop-level %s "
- // "with parent_ref = %d\n",
- // widget2->parent ? "non-" : "",
- // gtk_type_name (GTK_OBJECT_TYPE (widget2)),
- // widget2->parent_ref);
+ //printf (" Setting DW_NEEDS_RESIZE and NEEDS_ALLOCATE for the "
+ // "%stop-level %s %p with parentRef = %d\n",
+ // widget2->parent ? "non-" : "", widget2->getClassName(), widget2,
+ // widget2->parentRef);
if (extremesChanged) {
widget2->setFlags (EXTREMES_CHANGED);
@@ -172,6 +171,10 @@ void Widget::queueResize (int ref, bool extremesChanged)
*/
void Widget::sizeRequest (Requisition *requisition)
{
+ //printf ("The %stop-level %s %p with parentRef = %d: needsResize: %s\n",
+ // parent ? "non-" : "", getClassName(), this, parentRef,
+ // needsResize () ? "true" : "false");
+
if (needsResize ()) {
/** \todo Check requisition == &(this->requisition) and do what? */
sizeRequestImpl (requisition);
@@ -183,6 +186,9 @@ void Widget::sizeRequest (Requisition *requisition)
DBG_OBJ_SET_NUM (this, "requisition->descent", requisition->descent);
} else
*requisition = this->requisition;
+
+ //printf (" ==> Result: %d x (%d + %d)\n",
+ // requisition->width, requisition->ascent, requisition->descent);
}
/**
@@ -507,11 +513,9 @@ Widget *Widget::getWidgetAtPoint (int x, int y, int level)
Iterator *it;
Widget *childAtPoint;
- //_MSG ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n",
- // 3 * level, "", gtk_type_name (GTK_OBJECT_TYPE (widget)), widget,
- // allocation.x, allocation.y,
- // allocation.width, allocation.ascent,
- // allocation.descent);
+ //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n",
+ // 3 * level, "", getClassName (), this, allocation.x, allocation.y,
+ // allocation.width, allocation.ascent, allocation.descent);
if (x >= allocation.x &&
y >= allocation.y &&
@@ -524,7 +528,9 @@ Widget *Widget::getWidgetAtPoint (int x, int y, int level)
* is such a child, it is returned. Otherwise, this widget is returned.
*/
childAtPoint = NULL;
- it = iterator (Content::WIDGET, false);
+ it = iterator ((Content::Type)
+ (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT),
+ false);
while (childAtPoint == NULL && it->next ())
childAtPoint = it->getContent()->widget->getWidgetAtPoint (x, y,
@@ -568,6 +574,25 @@ void Widget::markExtremesChange (int ref)
{
}
+/**
+ * \brief This method is called after a widget has been set as the top of a
+ * widget tree.
+ *
+ * A widget may override this method when it is necessary to be notified.
+ */
+void Widget::notifySetAsTopLevel()
+{
+}
+
+/**
+ * \brief This method is called after a widget has been added to a parent.
+ *
+ * A widget may override this method when it is necessary to be notified.
+ */
+void Widget::notifySetParent()
+{
+}
+
void Widget::setWidth (int width)
{
}
diff --git a/dw/widget.hh b/dw/widget.hh
index 6942221b..58306c92 100644
--- a/dw/widget.hh
+++ b/dw/widget.hh
@@ -79,6 +79,14 @@ private:
* \brief The parent widget, NULL for top-level widgets.
*/
Widget *parent;
+
+ /**
+ * \brief The generating widget, NULL for top-level widgets, or if
+ * not set; in the latter case, the effective generator (see
+ * getGenerator) is the parent.
+ */
+ Widget *generator;
+
style::Style *style;
Flags flags;
@@ -178,6 +186,9 @@ protected:
*/
virtual void markExtremesChange (int ref);
+ virtual void notifySetAsTopLevel();
+ virtual void notifySetParent();
+
virtual bool buttonPressImpl (EventButton *event);
virtual bool buttonReleaseImpl (EventButton *event);
virtual bool motionNotifyImpl (EventMotion *event);
@@ -243,6 +254,8 @@ public:
void setParent (Widget *parent);
+ void setGenerator (Widget *generator) { this->generator = generator; }
+
inline style::Style *getStyle () { return style; }
/** \todo I do not like this. */
inline Allocation *getAllocation () { return &allocation; }
@@ -282,6 +295,8 @@ public:
int getLevel ();
Widget *getNearestCommonAncestor (Widget *otherWidget);
+ inline Widget *getGenerator () { return generator ? generator : parent; }
+
inline Layout *getLayout () { return layout; }
virtual Widget *getWidgetAtPoint (int x, int y, int level);