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.cc36
-rw-r--r--dw/iterator.hh2
-rw-r--r--dw/layout.cc1
-rw-r--r--dw/outofflowmgr.cc265
-rw-r--r--dw/outofflowmgr.hh105
-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.cc226
-rw-r--r--dw/textblock.hh141
-rw-r--r--dw/textblock_iterator.cc272
-rw-r--r--dw/textblock_linebreaking.cc94
-rw-r--r--dw/types.hh22
-rw-r--r--dw/widget.cc28
-rw-r--r--dw/widget.hh3
18 files changed, 1043 insertions, 203 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 9793db91..9e9076dc 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 e9431e9b..73beca6f 100644
--- a/dw/iterator.cc
+++ b/dw/iterator.cc
@@ -343,7 +343,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 +356,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 +390,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) {
@@ -422,7 +422,7 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
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 ();
@@ -467,7 +467,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
@@ -505,7 +505,7 @@ DeepIterator::DeepIterator (Iterator *it)
bool hasNext = it->next();
assert (hasNext);
- if (it->getContent()->type == Content::WIDGET &&
+ if (it->getContent()->type & Content::ANY_WIDGET &&
it->getContent()->widget == w)
break;
}
@@ -577,7 +577,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 +610,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 +642,21 @@ CharIterator::CharIterator ()
it = NULL;
}
-CharIterator::CharIterator (Widget *widget)
-{
- Iterator *i = widget->iterator (Content::SELECTION_CONTENT, false);
+/**
+ * \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)
+{
+ Content::Type widgetMask = (Content::Type)
+ (Content::WIDGET_IN_FLOW |
+ (followReferences ? Content::WIDGET_OOF_REF : Content::WIDGET_OOF_CONT));
+ Iterator *i =
+ widget->iterator ((Content::Type)
+ (Content::SELECTION_CONTENT | widgetMask), false);
it = new DeepIterator (i);
i->unref ();
ch = START;
diff --git a/dw/iterator.hh b/dw/iterator.hh
index 838d66a1..222a05c0 100644
--- a/dw/iterator.hh
+++ b/dw/iterator.hh
@@ -230,7 +230,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 9dfd5e9b..d2610687 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..241e0d27
--- /dev/null
+++ b/dw/outofflowmgr.cc
@@ -0,0 +1,265 @@
+#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;
+
+ 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);
+
+ Allocation childAllocation;
+ childAllocation.x = containingBlockAllocation->x
+ + containingBlock->getCBStyle()->boxOffsetX();
+ childAllocation.y = containingBlockAllocation->y + vloat->y;
+ childAllocation.width =
+ vloat->width - containingBlock->getCBStyle()->boxOffsetX();
+ childAllocation.ascent = vloat->ascent;
+ childAllocation.descent = vloat->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);
+ }
+
+ for (int i = 0; i < rightFloats->size(); i++) {
+ Float *vloat = rightFloats->get(i);
+ assert (vloat->y != -1);
+
+ Allocation childAllocation;
+ childAllocation.x = containingBlockAllocation->x
+ + containingBlockAllocation->width - vloat->width;
+ childAllocation.y = containingBlockAllocation->y + vloat->y;
+ childAllocation.width =
+ vloat->width - containingBlock->getCBStyle()->boxRestWidth();
+ childAllocation.ascent = vloat->ascent;
+ childAllocation.descent = vloat->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)
+{
+ if (widget->getStyle()->vloat != FLOAT_NONE) {
+ Float *vloat = new Float ();
+ vloat->widget = widget;
+ vloat->y = -1;
+
+ Requisition requisition;
+ widget->sizeRequest (&requisition);
+ vloat->width = requisition.width;
+ vloat->ascent = requisition.ascent;
+ vloat->descent = requisition.descent;
+
+ switch (widget->getStyle()->vloat) {
+ case FLOAT_LEFT:
+ vloat->width += containingBlock->getCBStyle()->boxOffsetX();
+ leftFloats->put (vloat);
+ widget->parentRef = createRefLeftFloat (leftFloats->size() - 1);
+ break;
+
+ case FLOAT_RIGHT:
+ vloat->width += containingBlock->getCBStyle()->boxRestWidth();
+ 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 = NULL;
+
+ switch (widget->getStyle()->vloat) {
+ case FLOAT_LEFT:
+ list = leftFloats;
+ break;
+
+ case FLOAT_RIGHT:
+ list = rightFloats;
+ break;
+
+ default:
+ assertNotReached();
+ }
+
+ for(int i = 0; i < list->size(); i++) {
+ Float *vloat = list->get(i);
+ if(vloat->widget == widget)
+ return vloat;
+ }
+
+ assertNotReached();
+ return NULL;
+}
+
+
+void OutOfFlowMgr::markSizeChange (int ref)
+{
+ // TODO Much copy and paste; see addWidget.
+ if (isRefLeftFloat (ref))
+ markSizeChange (leftFloats->get (getFloatIndexFromRef (ref)),
+ containingBlock->getCBStyle()->boxOffsetX());
+ else if (isRefRightFloat (ref))
+ markSizeChange (rightFloats->get (getFloatIndexFromRef (ref)),
+ containingBlock->getCBStyle()->boxRestWidth());
+ else
+ // later: absolute positions
+ assertNotReached();
+}
+
+void OutOfFlowMgr::markSizeChange (Float *vloat, int widthDiff)
+{
+ int oldWidth = vloat->width;
+ int oldHeight = vloat->ascent + vloat->descent;
+
+ Requisition requisition;
+ vloat->widget->sizeRequest (&requisition);
+ vloat->width = requisition.width + widthDiff;
+ vloat->ascent = requisition.ascent;
+ vloat->descent = requisition.descent;
+
+ if (vloat->width != oldWidth)
+ containingBlock->borderChanged (vloat->y);
+ else if (vloat->ascent + vloat->descent != oldHeight)
+ // Width remains the same, so a small optimization is possible.
+ containingBlock->borderChanged (vloat->y +
+ min (vloat->ascent + vloat->descent,
+ oldHeight));
+}
+
+void OutOfFlowMgr::markExtremesChange (int ref)
+{
+}
+
+void OutOfFlowMgr::tellNoPosition (Widget *widget)
+{
+ Float *vloat = findFloatByWidget(widget);
+ int oldY = vloat->y;
+ vloat->y = -1;
+
+ if (oldY != -1)
+ containingBlock->borderChanged (oldY);
+}
+
+void OutOfFlowMgr::tellPosition (Widget *widget, int y)
+{
+ assert (y >= 0);
+
+ // TODO Test collisions; when floats overlap, the vloat->y must be larger
+ // than y.
+
+ Float *vloat = findFloatByWidget(widget);
+ int oldY = vloat->y;
+ vloat->y = y;
+
+ if (oldY == -1)
+ containingBlock->borderChanged (y);
+ else if (y != oldY)
+ containingBlock->borderChanged (min (oldY, y));
+}
+
+int OutOfFlowMgr::getLeftBorder (int y)
+{
+ //return 40 * sin ((double)y / 30);
+
+ for(int i = 0; i < leftFloats->size(); i++) {
+ Float *vloat = leftFloats->get(i);
+ if(vloat->y != - 1 && y >= vloat->y &&
+ y < vloat->y + vloat->ascent + vloat->descent) {
+ //printf (" LEFT: %d ==> %d (%d + %d)\n", y,
+ // vloat->width, vloat->ascent, vloat->descent);
+ return vloat->width;
+ }
+ }
+
+ //printf (" LEFT: %d ==> %d\n", y, 0);
+ return 0;
+}
+
+int OutOfFlowMgr::getRightBorder (int y)
+{
+ //return 40 * cos ((double)y / 30);
+
+ for(int i = 0; i < rightFloats->size(); i++) {
+ Float *vloat = rightFloats->get(i);
+ if(vloat->y != - 1 && y >= vloat->y &&
+ y < vloat->y + vloat->ascent + vloat->descent)
+ //printf (" RIGHT: %d ==> %d (%d + %d)\n", y,
+ // vloat->width, vloat->ascent, vloat->descent);
+ return vloat->width;
+ }
+
+ //printf (" RIGHT: %d ==> %d\n", y, 0);
+ return 0;
+}
+
+} // namespace dw
diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh
new file mode 100644
index 00000000..5e96b639
--- /dev/null
+++ b/dw/outofflowmgr.hh
@@ -0,0 +1,105 @@
+#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;
+
+ // An additional "CB" to resolve the ambiguity to the methods in Widget.
+ virtual core::style::Style *getCBStyle () = 0;
+ virtual core::Allocation *getCBAllocation () = 0;
+ };
+
+private:
+ ContainingBlock *containingBlock;
+
+ class Float: public lout::object::Object
+ {
+ public:
+ core::Widget *widget;
+ // width includes border of the containing block
+ int y, width, ascent, descent;
+ };
+
+ //lout::container::typed::HashTable<lout::object::TypedPointer
+ // <dw::core::Widget>, Float> *floatsByWidget;
+ lout::container::typed::Vector<Float> *leftFloats, *rightFloats;
+
+ Float *findFloatByWidget (core::Widget *widget);
+ void markSizeChange (Float *vloat, int widthDiff);
+
+ void draw (lout::container::typed::Vector<Float> *list,
+ core::View *view, core::Rectangle *area);
+
+ 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);
+
+ static bool isWidgetOutOfFlow (core::Widget *widget);
+ void addWidget (core::Widget *widget);
+
+ void tellNoPosition (core::Widget *widget);
+ void tellPosition (core::Widget *widget, int y);
+
+ /**
+ * Get the left border for the vertical position of y, 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 getLeftBorder (int y);
+
+ int getRightBorder (int y);
+
+ 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(leftFloats->size())->widget; }
+};
+
+} // namespace dw
+
+#endif // __DW_OUTOFFLOWMGR_HH__
diff --git a/dw/style.cc b/dw/style.cc
index 2a1d4088..b0868c1f 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 2422bfa9..6b492793 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 e3d4a8b5..ee5d4f67 100644
--- a/dw/textblock.cc
+++ b/dw/textblock.cc
@@ -23,13 +23,14 @@
#include "../lout/misc.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;
@@ -68,6 +69,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);
@@ -105,8 +107,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 ();
}
@@ -121,6 +124,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;
@@ -187,7 +193,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 {
@@ -359,7 +365,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:
@@ -431,6 +437,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;
@@ -461,16 +470,25 @@ void Textblock::resizeDrawImpl ()
void Textblock::markSizeChange (int ref)
{
- markChange (ref);
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markSizeChange (ref);
+ } else
+ markChange (ref);
}
void Textblock::markExtremesChange (int ref)
{
- markChange (ref);
+ if (OutOfFlowMgr::isRefOutOfFlow (ref)) {
+ assert (outOfFlowMgr != NULL);
+ outOfFlowMgr->markExtremesChange (ref);
+ } else
+ markChange (ref);
}
/*
- * Implementation for both mark_size_change and mark_extremes_change.
+ * Implementation for both markSizeChange and markExtremesChange.
+ * Only used for normal flow.
*/
void Textblock::markChange (int ref)
{
@@ -481,16 +499,66 @@ void Textblock::markChange (int ref)
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 (wrapRef == -1)
- wrapRef = ref;
+ wrapRef = OutOfFlowMgr::getLineNoFromRef (ref);
else
- wrapRef = misc::min (wrapRef, ref);
+ wrapRef = misc::min (wrapRef, OutOfFlowMgr::getLineNoFromRef (ref));
}
PRINTF (" ... => %d\n", wrapRef);
}
+void Textblock::notifySetAsTopLevel()
+{
+ printf ("%p becomes toplevel\n", this);
+ containingBlock = this;
+ printf ("-> %p is its own containing block\n", this);
+}
+
+bool Textblock::isContainingBlock (Widget *widget)
+{
+ return
+ // Of course, only textblocks are considered as containing
+ // blocks.
+ widget->instanceOf (Textblock::CLASS_ID) &&
+ // The second condition: that this block is "out of flow", in a
+ // wider sense.
+ (// The toplevel widget is "out of flow", since there is no
+ // parent, and so no context.
+ widget->getParent() == NULL ||
+ // A similar reasoning applies to a widget with another parent
+ // than a textblock (typical example: a table cell (this is
+ // also a text block) within a table widget).
+ !widget->getParent()->instanceOf (Textblock::CLASS_ID) ||
+ // Finally, "out of flow" in a narrower sense: floats and
+ // absolute positions.
+ OutOfFlowMgr::isWidgetOutOfFlow (widget));
+}
+
+void Textblock::notifySetParent ()
+{
+ printf ("%p becomes a child of %p\n", this, getParent());
+
+ // Search for containing Box.
+ containingBlock = NULL;
+
+ for (Widget *widget = this; widget != NULL && containingBlock == NULL;
+ widget = widget->getParent())
+ if (isContainingBlock (widget)) {
+ containingBlock = (Textblock*)widget;
+
+ if (containingBlock == this)
+ printf ("-> %p is its own containing block\n", this);
+ else
+ printf ("-> %p becomes containing block of %p\n",
+ containingBlock, this);
+ }
+
+ assert (containingBlock != NULL);
+}
+
void Textblock::setWidth (int width)
{
/* If limitTextWidth is set to YES, a queueResize() may also be
@@ -502,7 +570,7 @@ void Textblock::setWidth (int width)
// words->size());
availWidth = width;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
redrawY = 0;
}
@@ -517,7 +585,7 @@ void Textblock::setAscent (int ascent)
// words->size());
availAscent = ascent;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
}
}
@@ -531,7 +599,7 @@ void Textblock::setDescent (int descent)
// words->size());
availDescent = descent;
- queueResize (0, false);
+ queueResize (OutOfFlowMgr::createRefNormalFlow (0), false);
mustQueueResize = false;
}
}
@@ -733,7 +801,7 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
}
}
it = new TextblockIterator (this, core::Content::SELECTION_CONTENT,
- wordIndex);
+ false, wordIndex);
r = selectionHandleEvent (eventType, it, charPos, link, event);
it->unref ();
return r;
@@ -1107,10 +1175,10 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
if (xWidget + word->size.width + 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;
@@ -1272,6 +1340,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area)
drawLine (line, view, area);
}
+
+ if(outOfFlowMgr)
+ outOfFlowMgr->draw(view, area);
}
/**
@@ -1544,37 +1615,37 @@ void Textblock::addText0 (const char *text, size_t len, bool canBeHyphenated,
*/
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);
+
+ if (OutOfFlowMgr::isWidgetOutOfFlow (widget)) {
+ if (containingBlock->outOfFlowMgr == NULL)
+ containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock);
+
+ widget->setParent (containingBlock);
+ containingBlock->outOfFlowMgr->addWidget (widget);
+ Word *word = addWord (0, 0, 0, false, style);
+ word->content.type = core::Content::WIDGET_OOF_REF;
+ word->content.breakSpace = 0;
+ word->content.widget = widget;
+ word->style = style;
+ } else {
+ widget->setParent (this);
- calcWidgetSize (widget, &size);
- word = addWord (size.width, size.ascent, size.descent, false, style);
-
- word->content.type = core::Content::WIDGET;
- word->content.widget = widget;
-
- //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, false, style);
+ word->content.type = core::Content::WIDGET_IN_FLOW;
+ word->content.widget = widget;
+ }
wordWrap (words->size () - 1, false);
- //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
- // word->content.widget->parent_ref);
-
- //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
- // "Assigning parent_ref = %d to added word %d, "
- // "in page with %d word(s)\n",
- // lines->size () - 1, words->size() - 1, words->size());
}
/**
@@ -1711,7 +1782,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
@@ -1720,23 +1791,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 =
@@ -1745,7 +1816,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;
}
}
@@ -1753,6 +1825,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
}
/* Otherwise continue to examine parents. */
}
+
/* Return in any case. */
return;
}
@@ -1801,7 +1874,6 @@ void Textblock::addLinebreak (core::style::Style *style)
wordWrap (words->size () - 1, false);
}
-
/**
* \brief Search recursively through widget.
*
@@ -1831,7 +1903,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);
@@ -1913,7 +1985,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,
@@ -1966,4 +2038,54 @@ void Textblock::queueDrawRange (int index1, int index2)
}
}
+void Textblock::borderChanged (int y)
+{
+ printf ("[%p] border has changed: %d\n", this, y);
+ borderChanged (y + allocation.y, true);
+}
+
+void Textblock::borderChanged (int yCanvas, bool extremesChanges)
+{
+ // 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 (yCanvas - allocation.y);
+ // 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;
+
+ queueResize (OutOfFlowMgr::createRefNormalFlow (wrapLineIndex),
+ extremesChanges);
+
+ for (int i = wrapLineIndex; i < lines->size (); i++) {
+ Word *word = words->getRef (lines->getRef(i)->firstWord);
+ if (word->content.type == core::Content::WIDGET_IN_FLOW &&
+ word->content.widget->instanceOf (Textblock::CLASS_ID)) {
+ Textblock *childBlock = (Textblock*)word->content.widget;
+ // extremes only change for the containing block, so we pass
+ // extremesChanges = false for all other widgets.
+ childBlock->borderChanged (yCanvas, false);
+ }
+ }
+ }
+}
+
+core::style::Style *Textblock::getCBStyle ()
+{
+ return getStyle();
+}
+
+core::Allocation *Textblock::getCBAllocation ()
+{
+ return &allocation;
+}
+
} // namespace dw
diff --git a/dw/textblock.hh b/dw/textblock.hh
index 12950ec3..c386a391 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
@@ -18,10 +19,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>
*
@@ -141,7 +143,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:
/**
@@ -207,6 +209,9 @@ private:
void print ();
};
+ Textblock *containingBlock;
+ OutOfFlowMgr *outOfFlowMgr;
+
protected:
enum {
/**
@@ -310,13 +315,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);
@@ -367,7 +373,8 @@ protected:
/* These values are set by set_... */
int availWidth, availAscent, availDescent;
- int wrapRef; /* [0 based] */
+ int wrapRef; /* 0-based. Important: This is the line number, not
+ the value stored in parentRef. */
lout::misc::SimpleVector <Line> *lines;
int nonTemporaryLines;
@@ -420,25 +427,115 @@ 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. Extremely (perhaps too) simple
+ * implementation.
*/
- inline int lineXOffsetContents (Line *line)
+ inline bool mustBorderBeRegardedForWord (int firstWord)
{
- return innerPadding + line->leftOffset +
- (line == lines->getFirstRef() ? line1OffsetEff : 0);
+ assert (firstWord < words->size ());
+ Word *word = words->getRef (firstWord);
+ return !(word->content.type == core::Content::WIDGET_IN_FLOW &&
+ word->content.widget->instanceOf (Textblock::CLASS_ID));
+ }
+
+ inline bool mustBorderBeRegarded (Line *line)
+ {
+ return mustBorderBeRegardedForWord (line->firstWord);
+ }
+
+ inline bool mustBorderBeRegarded (int lineNo)
+ {
+ int firstWord;
+ if (lineNo == 0)
+ firstWord = 0;
+ else
+ firstWord = lines->getRef(lineNo - 1)->lastWord + 1;
+
+ return mustBorderBeRegardedForWord (firstWord);
}
+ void borderChanged (int yCanvas, bool extremesChanges);
+
/**
- * \brief Like lineXOffset, but relative to the allocation (i.e.
- * including border etc.).
+ * \brief Returns the x offset (the indentation plus any offset
+ * needed for centering or right justification) for the line,
+ * relative to the allocation (i.e. including border etc.).
*/
inline int lineXOffsetWidget (Line *line)
{
- return lineXOffsetContents (line) + getStyle()->boxOffsetX ();
+ int resultFromOOFM;
+ if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (line))
+ resultFromOOFM =
+ containingBlock->getAllocation()->x - allocation.x +
+ containingBlock->outOfFlowMgr->getLeftBorder
+ (allocation.y + line->top + getStyle()->boxOffsetY()
+ - containingBlock->getAllocation()->y);
+ else
+ resultFromOOFM = 0;
+
+ return innerPadding + line->leftOffset +
+ (line == lines->getFirstRef() ? line1OffsetEff : 0) +
+ lout::misc::max (getStyle()->boxOffsetX(), resultFromOOFM);
+ }
+
+ inline int lineLeftBorder (int lineNo)
+ {
+ // Note that the line must not exist yet (but unless it is not
+ // the first line, the previous line, lineNo - 1, must). But
+ // lineHeight should be known to the caller.
+ int top;
+ if (lineNo == 0)
+ top = 0;
+ else {
+ Line *prevLine = lines->getRef (lineNo - 1);
+ top = prevLine->top + prevLine->boxAscent + prevLine->boxDescent +
+ prevLine->breakSpace;
+ }
+
+ int resultFromOOFM;
+ if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (lineNo))
+ resultFromOOFM =
+ containingBlock->getAllocation()->x - allocation.x +
+ containingBlock->outOfFlowMgr->getLeftBorder
+ (allocation.y + top + getStyle()->boxOffsetY()
+ - containingBlock->getAllocation()->y);
+ 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)
+ {
+ // Similar to lineLeftBorder().
+ int top;
+ if (lineNo == 0)
+ top = 0;
+ else {
+ Line *prevLine = lines->getRef (lineNo - 1);
+ top = prevLine->top + prevLine->boxAscent + prevLine->boxDescent +
+ prevLine->breakSpace;
+ }
+
+ int resultFromOOFM;
+ if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (lineNo))
+ resultFromOOFM =
+ (containingBlock->getAllocation()->x +
+ containingBlock->getAllocation()->width) -
+ (allocation.x + allocation.width) +
+ containingBlock->outOfFlowMgr->getRightBorder
+ (allocation.y + top + getStyle()->boxOffsetY()
+ - containingBlock->getAllocation()->y);
+ else
+ resultFromOOFM = 0;
+
+ return lout::misc::max (getStyle()->boxRestWidth(), resultFromOOFM);
}
inline int lineYOffsetWidgetAllocation (Line *line,
@@ -499,6 +596,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);
@@ -518,6 +617,7 @@ protected:
core::style::Style *style,
int numBreaks, int *breakPos,
core::Requisition *wordSize);
+ static bool isContainingBlock (Widget *widget);
public:
static int CLASS_ID;
@@ -563,6 +663,11 @@ 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::style::Style *getCBStyle ();
+ core::Allocation *getCBAllocation ();
};
} // namespace dw
diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc
index b5d28885..9af98137 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,53 @@ 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.type = false;
+ content.widget = textblock->outOfFlowMgr->getWidget (index);
+ } else
+ content = textblock->words->getRef(index)->content;
- content = textblock->words->getRef(index)->content;
return true;
}
@@ -88,107 +148,155 @@ 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;
-
- 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 (!oofm) {
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
+ /* nothing is highlighted */
+ textblock->hlStart[layer].index = index;
+ textblock->hlEnd[layer].index = index;
+ }
+
+ if (textblock->hlStart[layer].index >= index) {
+ index2 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index;
+ textblock->hlStart[layer].nChar = start;
+ }
+
+ if (textblock->hlEnd[layer].index <= index) {
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index;
+ textblock->hlEnd[layer].nChar = end;
+ }
- if (textblock->hlEnd[layer].index <= index) {
- index2 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index;
- textblock->hlEnd[layer].nChar = end;
+ textblock->queueDrawRange (index1, index2);
}
- 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;
-
- 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;
+
+ 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;
+ }
+
+ textblock->queueDrawRange (index1, index2);
}
- 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 + 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);
+ }
}
+ allocation->ascent = word->size.ascent;
+ allocation->descent = word->size.descent;
}
- allocation->ascent = word->size.ascent;
- allocation->descent = word->size.descent;
}
} // namespace dw
diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc
index 09dfd63d..3835273d 100644
--- a/dw/textblock_linebreaking.cc
+++ b/dw/textblock_linebreaking.cc
@@ -212,8 +212,14 @@ void Textblock::printWord (Word *word)
case core::Content::TEXT:
printf ("\"%s\"", word->content.text);
break;
- case core::Content::WIDGET:
- printf ("<widget: %p>\n", word->content.widget);
+ case core::Content::WIDGET_IN_FLOW:
+ printf ("<widget in flow: %p>\n", word->content.widget);
+ break;
+ case core::Content::WIDGET_OOF_REF:
+ printf ("<widget oof ref: %p>\n", word->content.widget);
+ break;
+ case core::Content::WIDGET_OOF_CONT:
+ printf ("<widge oof cont: %p>\n", word->content.widget);
break;
case core::Content::BREAK:
printf ("<break>\n");
@@ -289,6 +295,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;
@@ -446,19 +458,38 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll)
PRINTF ("[%p] WORD_WRAP (%d, %s)\n",
this, wordIndex, wrapAll ? "true" : "false");
- Word *word;
- //core::Extremes wordExtremes;
-
if (!wrapAll)
removeTemporaryLines ();
initLine1Offset (wordIndex);
- word = words->getRef (wordIndex);
+ Word *word = words->getRef (wordIndex);
word->effSpace = word->origSpace;
accumulateWordData (wordIndex);
+ if (word->content.type == core::Content::WIDGET_OOF_REF) {
+ 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
+ (word->content.widget,
+ allocation.y + top + getStyle()->boxOffsetY()
+ - containingBlock->getAllocation()->y);
+
+
+ // TODO: compare old/new values of calcAvailWidth(...);
+ int firstIndex =
+ lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1;
+ for (int i = firstIndex; i <= wordIndex; i++)
+ accumulateWordData (i);
+ }
+
bool newLine;
do {
bool tempNewLine = false;
@@ -478,7 +509,7 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll)
PRINTF (" NEW LINE: forced break\n");
} else if (wordIndex > firstIndex &&
word->badnessAndPenalty.lineTooTight () &&
- words->getRef(wordIndex- 1)
+ words->getRef(wordIndex - 1)
->badnessAndPenalty.lineCanBeBroken ()) {
// TODO Comment the last condition (also below where the minimun is
// searched for)
@@ -497,6 +528,10 @@ 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;
@@ -690,8 +725,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);
@@ -706,7 +745,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 =
@@ -730,7 +769,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);
@@ -796,6 +836,11 @@ void Textblock::accumulateWordData (int wordIndex)
int Textblock::calcAvailWidth (int lineIndex)
{
+ // BUG: This method must also include Line::boxLeft and Line::boxRight
+ // (introduced by floats), but since the recent changes in line breaking
+ // (together with hyphenation), this line is often not yet created, so
+ // these values cannot be determined.
+
int availWidth =
this->availWidth - getStyle()->boxDiffWidth() - innerPadding;
if (limitTextWidth &&
@@ -805,11 +850,14 @@ int Textblock::calcAvailWidth (int lineIndex)
if (lineIndex == 0)
availWidth -= line1OffsetEff;
- //PRINTF("[%p] CALC_AVAIL_WIDTH => %d - %d - %d = %d\n",
- // this, this->availWidth, getStyle()->boxDiffWidth(), innerPadding,
- // availWidth);
+ PRINTF ("[%p] CALC_AVAIL_WIDTH (%d of %d) => %d - %d - %d = %d\n",
+ this, lineIndex, lines->size(), this->availWidth,
+ getStyle()->boxDiffWidth(), innerPadding, availWidth);
- return 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?
+ return availWidth - lineLeftBorder (lineIndex) - lineRightBorder (lineIndex);
}
void Textblock::initLine1Offset (int wordIndex)
@@ -824,7 +872,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 {
@@ -915,8 +963,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);
@@ -926,9 +980,9 @@ void Textblock::rewrap ()
// changed, so getRef() must be called again.
word = words->getRef (i);
- if (word->content.type == core::Content::WIDGET) {
- word->content.widget->parentRef = lines->size () - 1;
- }
+ if (word->content.type == core::Content::WIDGET_IN_FLOW)
+ word->content.widget->parentRef =
+ OutOfFlowMgr::createRefNormalFlow (lines->size () - 1);
}
/* Next time, the page will not have to be rewrapped. */
diff --git a/dw/types.hh b/dw/types.hh
index 65983fad..61136673 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,
+
+ /** \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 << 4,
+
+ BREAK = 1 << 5,
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
diff --git a/dw/widget.cc b/dw/widget.cc
index 8ca0681a..8e619911 100644
--- a/dw/widget.cc
+++ b/dw/widget.cc
@@ -108,6 +108,8 @@ void Widget::setParent (Widget *parent)
if (!buttonSensitiveSet)
buttonSensitive = parent->buttonSensitive;
+ notifySetParent();
+
//DBG_OBJ_ASSOC (widget, parent);
//printf ("%p becomes a child of %p\n", this, parent);
}
@@ -115,7 +117,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);
}
@@ -523,7 +526,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,
@@ -567,6 +572,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 b751a282..e18344c7 100644
--- a/dw/widget.hh
+++ b/dw/widget.hh
@@ -178,6 +178,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);