diff options
Diffstat (limited to 'dw')
-rw-r--r-- | dw/Makefile.am | 2 | ||||
-rw-r--r-- | dw/findtext.cc | 4 | ||||
-rw-r--r-- | dw/iterator.cc | 36 | ||||
-rw-r--r-- | dw/iterator.hh | 2 | ||||
-rw-r--r-- | dw/layout.cc | 1 | ||||
-rw-r--r-- | dw/outofflowmgr.cc | 265 | ||||
-rw-r--r-- | dw/outofflowmgr.hh | 105 | ||||
-rw-r--r-- | dw/style.cc | 12 | ||||
-rw-r--r-- | dw/style.hh | 17 | ||||
-rw-r--r-- | dw/table.cc | 14 | ||||
-rw-r--r-- | dw/tablecell.cc | 2 | ||||
-rw-r--r-- | dw/textblock.cc | 226 | ||||
-rw-r--r-- | dw/textblock.hh | 141 | ||||
-rw-r--r-- | dw/textblock_iterator.cc | 272 | ||||
-rw-r--r-- | dw/textblock_linebreaking.cc | 94 | ||||
-rw-r--r-- | dw/types.hh | 22 | ||||
-rw-r--r-- | dw/widget.cc | 28 | ||||
-rw-r--r-- | dw/widget.hh | 3 |
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); |