diff options
Diffstat (limited to 'dw/textblock.cc')
-rw-r--r-- | dw/textblock.cc | 842 |
1 files changed, 707 insertions, 135 deletions
diff --git a/dw/textblock.cc b/dw/textblock.cc index 2c4ca20b..91f9a82e 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -25,13 +25,14 @@ #include "../lout/debug.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; @@ -228,6 +229,7 @@ Textblock::Textblock (bool limitTextWidth) setFlags (USES_HINTS); setButtonSensitive(true); + containingBlock = NULL; hasListitemValue = false; innerPadding = 0; line1Offset = 0; @@ -250,15 +252,14 @@ Textblock::Textblock (bool limitTextWidth) nonTemporaryLines = 0; words = new misc::NotSoSimpleVector <Word> (1); anchors = new misc::SimpleVector <Anchor> (1); - - //DBG_OBJ_SET_NUM(this, "num_lines", num_lines); + outOfFlowMgr = NULL; wrapRefLines = wrapRefParagraphs = -1; - //DBG_OBJ_SET_NUM(this, "last_line_width", last_line_width); - //DBG_OBJ_SET_NUM(this, "last_line_par_min", last_line_par_min); - //DBG_OBJ_SET_NUM(this, "last_line_par_max", last_line_par_max); - //DBG_OBJ_SET_NUM(this, "wrap_ref", wrap_ref); + DBG_OBJ_SET_NUM ("lines.size", lines->size ()); + DBG_OBJ_SET_NUM ("words.size", words->size ()); + DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines); + DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs); hoverLink = -1; @@ -267,6 +268,8 @@ Textblock::Textblock (bool limitTextWidth) availAscent = 100; availDescent = 0; + verticalOffset = 0; + this->limitTextWidth = limitTextWidth; for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { @@ -276,6 +279,8 @@ Textblock::Textblock (bool limitTextWidth) hlEnd[layer].index = 0; hlEnd[layer].nChar = 0; } + + initNewLine (); } Textblock::~Textblock () @@ -288,8 +293,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? */ removeWordImgRenderer (i); removeSpaceImgRenderer (i); @@ -309,6 +315,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; @@ -323,7 +332,8 @@ Textblock::~Textblock () */ void Textblock::sizeRequestImpl (core::Requisition *requisition) { - PRINTF ("[%p] SIZE_REQUEST: ...\n", this); + DBG_OBJ_MSG ("resize", 0, "<b>sizeRequestImpl</b> ()"); + DBG_OBJ_MSG_START (); rewrap (); showMissingLines (); @@ -331,18 +341,18 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) if (lines->size () > 0) { Line *lastLine = lines->getRef (lines->size () - 1); requisition->width = lastLine->maxLineWidth; - - PRINTF ("[%p] SIZE_REQUEST: lastLine->maxLineWidth = %d\n", - this, lastLine->maxLineWidth); - - PRINTF ("[%p] SIZE_REQUEST: lines[0]->boxAscent = %d\n", - this, lines->getRef(0)->boxAscent); - PRINTF ("[%p] SIZE_REQUEST: lines[%d]->top = %d\n", - this, lines->size () - 1, lastLine->top); - PRINTF ("[%p] SIZE_REQUEST: lines[%d]->boxAscent = %d\n", - this, lines->size () - 1, lastLine->boxAscent); - PRINTF ("[%p] SIZE_REQUEST: lines[%d]->boxDescent = %d\n", - this, lines->size () - 1, lastLine->boxDescent); + + DBG_OBJ_MSGF ("resize", 1, "lines[%d]->maxLineWidth = %d", + lines->size () - 1, lastLine->maxLineWidth); + + DBG_OBJ_MSGF ("resize", 1, "lines[0]->boxAscent = %d", + lines->getRef(0)->boxAscent); + DBG_OBJ_MSGF ("resize", 1, "lines[%d]->top = %d", + lines->size () - 1, lastLine->top); + DBG_OBJ_MSGF ("resize", 1, "lines[%d]->boxAscent = %d", + lines->size () - 1, lastLine->boxAscent); + DBG_OBJ_MSGF ("resize", 1, "lines[%d]->boxDescent = %d", + lines->size () - 1, lastLine->boxDescent); /* Note: the breakSpace of the last line is ignored, so breaks at the end of a textblock are not visible. */ @@ -356,18 +366,42 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) requisition->descent = 0; } - PRINTF ("[%p] SIZE_REQUEST: inner padding = %d, boxDiffWidth = %d\n", - this, innerPadding, getStyle()->boxDiffWidth ()); + DBG_OBJ_MSGF ("resize", 1, "inner padding = %d, boxDiffWidth = %d", + innerPadding, getStyle()->boxDiffWidth ()); requisition->width += innerPadding + getStyle()->boxDiffWidth (); - requisition->ascent += getStyle()->boxOffsetY (); + requisition->ascent += verticalOffset + getStyle()->boxOffsetY (); requisition->descent += getStyle()->boxRestHeight (); - if (requisition->width < availWidth) + // Dealing with parts out of flow, which may overlap the borders of + // the text block. Base lines are ignored here: they do not play a + // role (currently) and caring about them (for the future) would + // cause too much problems. + + DBG_OBJ_MSGF ("resize", 1, "before considering OOF widgets: %d * %d + %d", + requisition->width, requisition->ascent, requisition->descent); + + if (outOfFlowMgr) { + int oofWidth, oofHeight; + outOfFlowMgr->getSize (&oofWidth, &oofHeight); + requisition->width = misc::max (requisition->width, oofWidth); + if (oofHeight > requisition->ascent + requisition->descent) + requisition->descent = oofHeight - requisition->ascent; + } + + DBG_OBJ_MSGF ("resize", 1, "before considering availWidth: %d * %d + %d", + requisition->width, requisition->ascent, requisition->descent); + + if (requisition->width < availWidth) { requisition->width = availWidth; + DBG_OBJ_MSGF ("resize", 1, "adjusting to availWidth => %d", + requisition->width); + } - PRINTF ("[%p] SIZE_REQUEST: %d x %d + %d\n", this, requisition->width, - requisition->ascent, requisition->descent); + DBG_OBJ_MSGF ("resize", 1, "=> %d * %d + %d", + requisition->width, requisition->ascent, requisition->descent); + + DBG_OBJ_MSG_END (); } /** @@ -375,7 +409,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 { @@ -405,7 +439,8 @@ void Textblock::getWordExtremes (Word *word, core::Extremes *extremes) void Textblock::getExtremesImpl (core::Extremes *extremes) { - PRINTF ("[%p] GET_EXTREMES ...\n", this); + DBG_OBJ_MSG ("resize", 0, "<b>getExtremesImpl</b>"); + DBG_OBJ_MSG_START (); fillParagraphs (); @@ -423,8 +458,22 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) extremes->minWidth += diff; extremes->maxWidth += diff; - PRINTF ("[%p] GET_EXTREMES => %d / %d\n", - this, extremes->minWidth, extremes->maxWidth); + if (outOfFlowMgr) { + int oofMinWidth, oofMaxWidth; + outOfFlowMgr->getExtremes (&oofMinWidth, &oofMaxWidth); + + DBG_OBJ_MSGF ("resize", 1, "extremes: %d / %d, corrected: %d / %d", + extremes->minWidth, extremes->maxWidth, + oofMinWidth, oofMaxWidth); + + extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth); + extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + + DBG_OBJ_MSG_END (); } @@ -436,6 +485,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) showMissingLines (); + if(outOfFlowMgr) + outOfFlowMgr->sizeAllocateStart (allocation); + int lineIndex, wordIndex; Line *line; Word *word; @@ -449,7 +501,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) { line = lines->getRef (lineIndex); - xCursor = lineXOffsetWidget (line); + xCursor = line->offsetCompleteWidget; for (wordIndex = line->firstWord; wordIndex <= line->lastWord; wordIndex++) { @@ -459,7 +511,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: @@ -473,6 +525,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) lineYOffsetCanvasAllocation (line, allocation) + (line->boxAscent - word->size.ascent) - word->content.widget->getStyle()->margin.top; + childAllocation.width = word->size.width; childAllocation.ascent = word->size.ascent + word->content.widget->getStyle()->margin.top; @@ -531,11 +584,17 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) } } + if(outOfFlowMgr) + outOfFlowMgr->sizeAllocateEnd (); + for (int i = 0; i < anchors->size(); i++) { Anchor *anchor = anchors->getRef(i); int y; - if (anchor->wordIndex >= words->size()) { + if (anchor->wordIndex >= words->size() || + // Also regard not-yet-existing lines. + lines->size () <= 0 || + anchor->wordIndex > lines->getLastRef()->lastWord) { y = allocation->y + allocation->ascent + allocation->descent; } else { Line *line = lines->getRef(findLineOfWord (anchor->wordIndex)); @@ -561,47 +620,122 @@ void Textblock::resizeDrawImpl () void Textblock::markSizeChange (int ref) { - PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", this, ref, wrapRefLines); + DBG_OBJ_MSGF ("resize", 0, "<b>markSizeChange</b> (%d)", ref); + DBG_OBJ_MSG_START (); - /* By the way: ref == -1 may have two different causes: (i) flush() - calls "queueResize (-1, true)", when no rewrapping is necessary; - and (ii) a word may have parentRef == -1 , when it is not yet - added to a line. In the latter case, nothing has to be done - now, but addLine(...) will do everything necessary. */ - if (ref != -1) { - if (wrapRefLines == -1) - wrapRefLines = ref; - else - wrapRefLines = misc::min (wrapRefLines, ref); - } + if (OutOfFlowMgr::isRefOutOfFlow (ref)) { + assert (outOfFlowMgr != NULL); + outOfFlowMgr->markSizeChange (ref); + } else { + PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", + this, ref, wrapRefLines); + + /* By the way: ref == -1 may have two different causes: (i) flush() + calls "queueResize (-1, true)", when no rewrapping is necessary; + and (ii) a word may have parentRef == -1 , when it is not yet + added to a line. In the latter case, nothing has to be done + now, but addLine(...) will do everything necessary. */ + if (ref != -1) { + if (wrapRefLines == -1) + wrapRefLines = OutOfFlowMgr::getLineNoFromRef (ref); + else + wrapRefLines = misc::min (wrapRefLines, + OutOfFlowMgr::getLineNoFromRef (ref)); + } - PRINTF (" ... => %d\n", wrapRefLine); + DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines); + + // It seems that sometimes (even without floats) the lines + // structure is changed, so that wrapRefLines may refers to a + // line which does not exist anymore. Should be examined + // again. Until then, setting wrapRefLines to the same value is + // a workaround. + markExtremesChange (ref); + } - // It seems that sometimes the lines structure is changed, so that - // wrapRefLines may refers to a line which does not exist - // anymore. Should be examined again. Until then, setting - // wrapRefLines to the same value is a workaround. - markExtremesChange (ref); + DBG_OBJ_MSG_END (); } void Textblock::markExtremesChange (int ref) { - PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n", - this, ref, wrapRefParagraphs); - - /* By the way: ref == -1 may have two different causes: (i) flush() - calls "queueResize (-1, true)", when no rewrapping is necessary; - and (ii) a word may have parentRef == -1 , when it is not yet - added to a line. In the latter case, nothing has to be done - now, but addLine(...) will do everything necessary. */ - if (ref != -1) { - if (wrapRefParagraphs == -1) - wrapRefParagraphs = ref; - else - wrapRefParagraphs = misc::min (wrapRefParagraphs, ref); + DBG_OBJ_MSGF ("resize", 1, "<b>markExtremesChange</b> (%d)", ref); + DBG_OBJ_MSG_START (); + + if (OutOfFlowMgr::isRefOutOfFlow (ref)) { + assert (outOfFlowMgr != NULL); + outOfFlowMgr->markExtremesChange (ref); + } else { + PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n", + this, ref, wrapRefParagraphs); + + /* By the way: ref == -1 may have two different causes: (i) flush() + calls "queueResize (-1, true)", when no rewrapping is necessary; + and (ii) a word may have parentRef == -1 , when it is not yet + added to a line. In the latter case, nothing has to be done + now, but addLine(...) will do everything necessary. */ + if (ref != -1) { + if (wrapRefParagraphs == -1) + wrapRefParagraphs = OutOfFlowMgr::getLineNoFromRef (ref); + else + wrapRefParagraphs = + misc::min (wrapRefParagraphs, + OutOfFlowMgr::getLineNoFromRef (ref)); + } + + DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs); } - PRINTF (" ... => %d\n", wrapRefParagraphs); + DBG_OBJ_MSG_END (); +} + +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) @@ -609,43 +743,43 @@ void Textblock::setWidth (int width) /* If limitTextWidth is set to YES, a queueResize() may also be * necessary. */ if (availWidth != width || limitTextWidth) { - //DEBUG_MSG(DEBUG_REWRAP_LEVEL, - // "setWidth: Calling queueResize, " - // "in page with %d word(s)\n", - // words->size()); + DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width); + DBG_OBJ_MSG_START (); availWidth = width; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; redrawY = 0; + + DBG_OBJ_MSG_END (); } } void Textblock::setAscent (int ascent) { if (availAscent != ascent) { - //DEBUG_MSG(DEBUG_REWRAP_LEVEL, - // "setAscent: Calling queueResize, " - // "in page with %d word(s)\n", - // words->size()); + DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent); + DBG_OBJ_MSG_START (); availAscent = ascent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; + + DBG_OBJ_MSG_END (); } } void Textblock::setDescent (int descent) { if (availDescent != descent) { - //DEBUG_MSG(DEBUG_REWRAP_LEVEL, - // "setDescent: Calling queueResize, " - // "in page with %d word(s)\n", - // words->size()); + DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent); + DBG_OBJ_MSG_START (); availDescent = descent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; + + DBG_OBJ_MSG_END (); } } @@ -753,7 +887,8 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, wordIndex = words->size () - 1; charPos = core::SelectionState::END_OF_WORD; } else { - Line *line = lines->getRef (findLineIndex (event->yWidget)); + Line *line = + lines->getRef (findLineIndexWhenAllocated (event->yWidget)); // Pointer within the break space? if (event->yWidget > @@ -761,11 +896,11 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, // Choose this break. wordIndex = line->lastWord; charPos = core::SelectionState::END_OF_WORD; - } else if (event->xWidget < lineXOffsetWidget (line)) { + } else if (event->xWidget < line->offsetCompleteWidget) { // Left of the first word in the line. wordIndex = line->firstWord; } else { - int nextWordStartX = lineXOffsetWidget (line); + int nextWordStartX = line->offsetCompleteWidget; for (wordIndex = line->firstWord; wordIndex <= line->lastWord; @@ -860,8 +995,9 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, } } } - it = new TextblockIterator (this, core::Content::SELECTION_CONTENT, - wordIndex); + + it = new TextblockIterator (this, core::Content::maskForSelection (true), + false, wordIndex); r = selectionHandleEvent (eventType, it, charPos, link, event); it->unref (); return r; @@ -894,7 +1030,25 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size) availDescent = this->availDescent; if (widget->usesHints ()) { - widget->setWidth (availWidth); + // This is a simplified version of calcAvailWidth (see there for + // more details). Until recently, the *attribute* availWidth was + // used, widthout any corrections. To limit the damage, only + // includde left and right border (by floats), until the Great + // Redesign Of Widget Sizes (GROWS). + int corrAvailWidth; + // Textblocks keep track of borders themselves, so they get the + // total available width. (Should once replaced by something + // like OOFAware.) + if (widget->instanceOf (Textblock::CLASS_ID)) + corrAvailWidth = availWidth; + else + corrAvailWidth = + misc::max (availWidth - (newLineLeftBorder + newLineRightBorder), + 0); + + PRINTF ("setWidth (%d) for the %s %p\n", + corrAvailWidth, widget->getClassName(), widget); + widget->setWidth (corrAvailWidth); widget->setAscent (availAscent); widget->setDescent (availDescent); widget->sizeRequest (size); @@ -1245,7 +1399,7 @@ void Textblock::drawSpace(int wordIndex, core::View *view, */ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) { - int xWidget = lineXOffsetWidget(line); + int xWidget = line->offsetCompleteWidget; int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; for (int wordIndex = line->firstWord; @@ -1256,10 +1410,10 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) if (xWidget + wordSize + word->hyphenWidth + word->effSpace >= area->x) { if (word->content.type == core::Content::TEXT || - word->content.type == core::Content::WIDGET) { + word->content.type == core::Content::WIDGET_IN_FLOW) { if (word->size.width > 0) { - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { core::Widget *child = word->content.widget; core::Rectangle childArea; @@ -1303,10 +1457,36 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) } /** - * Find the first line index that includes y, relative to top of widget. + * Find the first line index that includes y, which is given in widget + * coordinates. */ int Textblock::findLineIndex (int y) { + return wasAllocated () ? + findLineIndexWhenAllocated (y) : findLineIndexWhenNotAllocated (y); +} + +int Textblock::findLineIndexWhenNotAllocated (int y) +{ + if (lines->size() == 0) + return -1; + else + return findLineIndex (y, + lines->getRef(0)->boxAscent + verticalOffset + + getStyle()->boxOffsetY()); +} + +int Textblock::findLineIndexWhenAllocated (int y) +{ + assert (wasAllocated ()); + return findLineIndex (y, allocation.ascent); +} + +int Textblock::findLineIndex (int y, int ascent) +{ + core::Allocation alloc; + alloc.ascent = ascent; // More is not needed. + int maxIndex = lines->size () - 1; int step, index, low = 0; @@ -1314,12 +1494,12 @@ int Textblock::findLineIndex (int y) while ( step > 1 ) { index = low + step; if (index <= maxIndex && - lineYOffsetWidgetI (index) <= y) + lineYOffsetWidgetIAllocation (index, &alloc) <= y) low = index; step = (step + 1) >> 1; } - if (low < maxIndex && lineYOffsetWidgetI (low + 1) <= y) + if (low < maxIndex && lineYOffsetWidgetIAllocation (low + 1, &alloc) <= y) low++; /* @@ -1340,12 +1520,13 @@ int Textblock::findLineIndex (int y) */ int Textblock::findLineOfWord (int wordIndex) { - int high = lines->size () - 1, index, low = 0; - - // TODO regard also not-yet-existing lines? - if (wordIndex < 0 || wordIndex >= words->size ()) + if (wordIndex < 0 || wordIndex >= words->size () || + // Also regard not-yet-existing lines. + lines->size () <= 0 || wordIndex > lines->getLastRef()->lastWord) return -1; + int high = lines->size () - 1, index, low = 0; + while (true) { index = (low + high) / 2; if (wordIndex >= lines->getRef(index)->firstWord) { @@ -1396,14 +1577,14 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) *inSpace = false; - if ((lineIndex = findLineIndex (y)) >= lines->size ()) + if ((lineIndex = findLineIndexWhenAllocated (y)) >= lines->size ()) return NULL; line = lines->getRef (lineIndex); yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; if (yWidgetBase + line->boxDescent <= y) return NULL; - xCursor = lineXOffsetWidget (line); + xCursor = line->offsetCompleteWidget; for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { word = words->getRef (wordIndex); lastXCursor = xCursor; @@ -1438,9 +1619,17 @@ void Textblock::draw (core::View *view, core::Rectangle *area) int lineIndex; Line *line; - drawWidgetBox (view, area, false); + // Instead of drawWidgetBox, use drawBox to include verticalOffset. + if (getParent() == NULL) { + // The toplevel (parent == NULL) widget is a special case, which + // we leave to drawWidgetBox; verticalOffset will here always 0. + assert (verticalOffset == 0); + drawWidgetBox (view, area, false); + } else + drawBox (view, getStyle(), area, 0, verticalOffset, allocation.width, + getHeight() - verticalOffset, false); - lineIndex = findLineIndex (area->y); + lineIndex = findLineIndexWhenAllocated (area->y); for (; lineIndex < lines->size (); lineIndex++) { line = lines->getRef (lineIndex); @@ -1449,6 +1638,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area) drawLine (line, view, area); } + + if(outOfFlowMgr) + outOfFlowMgr->draw(view, area); } /** @@ -1458,6 +1650,7 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent, short flags, core::style::Style *style) { words->increase (); + DBG_OBJ_SET_NUM ("words.size", words->size ()); int wordNo = words->size () - 1; initWord (wordNo); fillWord (wordNo, width, ascent, descent, flags, style); @@ -1936,6 +2129,16 @@ void Textblock::addText0 (const char *text, size_t len, short flags, word->content.type = core::Content::TEXT; word->content.text = layout->textZone->strndup(text, len); + DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", "TEXT"); + DBG_OBJ_ARRATTRSET_STR ("words", words->size () - 1, + "text/widget/breakSpace", word->content.text); + + // The following debug message may be useful to identify the + // different textblocks. + + //if (words->size() == 1) + // printf ("[%p] first word: '%s'\n", this, text); + processWord (words->size () - 1); } @@ -1944,28 +2147,59 @@ void Textblock::addText0 (const char *text, size_t len, short flags, */ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) { - Word *word; - core::Requisition size; - /* We first assign -1 as parent_ref, since the call of widget->size_request * will otherwise let this Textblock be rewrapped from the beginning. * (parent_ref is actually undefined, but likely has the value 0.) At the, * end of this function, the correct value is assigned. */ widget->parentRef = -1; - PRINTF ("%p becomes child of %p\n", widget, this); - - widget->setParent (this); widget->setStyle (style); - calcWidgetSize (widget, &size); - word = addWord (size.width, size.ascent, size.descent, 0, style); + PRINTF ("adding the %s %p to %p (word %d) ...\n", + widget->getClassName(), widget, this, words->size()); - word->content.type = core::Content::WIDGET; - word->content.widget = widget; + if (containingBlock->outOfFlowMgr == NULL) { + containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock); + DBG_OBJ_ASSOC (containingBlock, containingBlock->outOfFlowMgr); + } + + if (OutOfFlowMgr::isWidgetHandledByOOFM (widget)) { + PRINTF (" -> out of flow.\n"); + + widget->setParent (containingBlock); + widget->setGenerator (this); + containingBlock->outOfFlowMgr->addWidgetOOF (widget, this, + words->size ()); + Word *word = addWord (0, 0, 0, 0, style); + word->content.type = core::Content::WIDGET_OOF_REF; + word->content.widget = widget; + word->style = style; + + DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", + "WIDGET_OOF_REF"); + DBG_OBJ_ARRATTRSET_PTR ("words", words->size () - 1, + "text/widget/breakSpace", word->content.widget); + } else { + PRINTF (" -> within flow.\n"); - //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1, - // word->content.widget); + widget->setParent (this); + + // TODO Replace (perhaps) later "textblock" by "OOF aware widget". + if (widget->instanceOf (Textblock::CLASS_ID)) + containingBlock->outOfFlowMgr->addWidgetInFlow ((Textblock*)widget, + this, words->size ()); + + core::Requisition size; + calcWidgetSize (widget, &size); + Word *word = addWord (size.width, size.ascent, size.descent, 0, style); + word->content.type = core::Content::WIDGET_IN_FLOW; + word->content.widget = widget; + + DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", + "WIDGET_IN_FLOW"); + DBG_OBJ_ARRATTRSET_PTR ("words", words->size () - 1, + "text/widget/breakSpace", word->content.widget); + } processWord (words->size () - 1); //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref", @@ -2067,8 +2301,10 @@ void Textblock::fillSpace (int wordNo, core::style::Style *style) // TODO: This line does not work: addBreakOption (word, style); - // Do not override a previously set break penalty. - if (!word->content.space) { + if (// Do not override a previously set break penalty: + !word->content.space && + // OOF references are considered specially, and must not have a space: + word->content.type != core::Content::WIDGET_OOF_REF) { setBreakOption (word, style, 0, 0, false); word->content.space = true; @@ -2140,7 +2376,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 @@ -2149,23 +2385,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 = @@ -2174,7 +2410,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; } } @@ -2182,6 +2419,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) } /* Otherwise continue to examine parents. */ } + /* Return in any case. */ return; } @@ -2205,6 +2443,11 @@ void Textblock::addParbreak (int space, core::style::Style *style) word->content.type = core::Content::BREAK; word->badnessAndPenalty.setPenalty (PENALTY_FORCE_BREAK); word->content.breakSpace = space; + + DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", "BREAK"); + DBG_OBJ_ARRATTRSET_NUM ("words", words->size () - 1, + "text/widget/breakSpace", word->content.breakSpace); + processWord (words->size () - 1); } @@ -2230,10 +2473,14 @@ void Textblock::addLinebreak (core::style::Style *style) word->content.type = core::Content::BREAK; word->badnessAndPenalty.setPenalty (PENALTY_FORCE_BREAK); word->content.breakSpace = 0; + + DBG_OBJ_ARRATTRSET_SYM ("words", words->size () - 1, "type", "BREAK"); + DBG_OBJ_ARRATTRSET_NUM ("words", words->size () - 1, + "text/widget/breakSpace", word->content.breakSpace); + processWord (words->size () - 1); } - /** * \brief Search recursively through widget. * @@ -2242,6 +2489,10 @@ void Textblock::addLinebreak (core::style::Style *style) */ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) { + //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n", + // 3 * level, "", getClassName (), this, allocation.x, allocation.y, + // allocation.width, allocation.ascent, allocation.descent); + int lineIndex, wordIndex; Line *line; @@ -2252,7 +2503,15 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) return NULL; } - lineIndex = findLineIndex (y - allocation.y); + // First, search for widgets out of flow, notably floats, since + // there are cases where they overlap child textblocks. Should + // later be refined using z-index. + Widget *oofWidget = + outOfFlowMgr ? outOfFlowMgr->getWidgetAtPoint (x, y, level) : NULL; + if (oofWidget) + return oofWidget; + + lineIndex = findLineIndexWhenAllocated (y - allocation.y); if (lineIndex < 0 || lineIndex >= lines->size ()) { return this; @@ -2263,7 +2522,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); @@ -2303,12 +2562,14 @@ void Textblock::handOverBreak (core::style::Style *style) */ void Textblock::flush () { - PRINTF ("[%p] FLUSH => %s (parentRef = %d)\n", - this, mustQueueResize ? "true" : "false", parentRef); - if (mustQueueResize) { + DBG_OBJ_MSG ("resize", 0, "<b>flush</b> (mustQueueResize set)"); + DBG_OBJ_MSG_START (); + queueResize (-1, true); mustQueueResize = false; + + DBG_OBJ_MSG_END (); } } @@ -2344,7 +2605,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, @@ -2396,4 +2657,315 @@ void Textblock::queueDrawRange (int index1, int index2) } } +void Textblock::setVerticalOffset (int verticalOffset) +{ + if (this->verticalOffset != verticalOffset) { + this->verticalOffset = verticalOffset; + mustQueueResize = true; + queueDraw (); // Could perhaps be optimized. + } +} + +/** + * Called by dw::OutOfFlowMgr when the border has changed due to a + * float (or some floats). + * + * "y", which given in widget coordinates, denotes the minimal + * position (when more than one float caused this), "vloat" the + * floating widget belonging to "y". + */ +void Textblock::borderChanged (int y, Widget *vloat) +{ + DBG_OBJ_MSGF ("resize", 0, "<b>borderChanged</b> (%d, %p)", y, vloat); + DBG_OBJ_MSG_START (); + + int lineIndex = findLineIndex (y); + DBG_OBJ_MSGF ("resize", 1, "Line index: %d (of %d).", + lineIndex, lines->size ()); + + // Nothing to do at all, when lineIndex >= lines->size (), + // i. e. the change is below the bottom of this widget. + if (lineIndex < lines->size ()) { + int wrapLineIndex; + if (lineIndex < 0) + // Rewrap all. + wrapLineIndex = 0; + else + wrapLineIndex = lineIndex; + + DBG_OBJ_MSGF ("resize", 1, "Rewrapping from line %d (of %d).", + wrapLineIndex, lines->size ()); + + if (vloat->getGenerator() == this) { + bool found = false; + // Sometimes, the respective word is not yet part of a + // line. Nothing to do, but because of the assertion below + // (and also for performace reasons) this should be + // considered. TODO: Integrate this below. + for (int wordIndex = + lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0; + !found && wordIndex < words->size(); wordIndex++) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF && + word->content.widget == vloat) + found = true; + } + + // We search for the line of the float reference. There are + // two cases when this is not the line corresponsing to y: + // + // (1) When the float was moved down, due to collisions with + // other floats: in this case, the line number gets + // smaller (since the float reference is before). + // + // (2) In some cases, the line number may become larger, due + // to the per-line optimization of the words: initially, + // lines->size() - 1 is assigned, but it may happen that + // the float reference is put into another line. + // + // Only in the first case, a correction is neccessary, but a + // test for the second case is useful. (TODO: I've forgotten + // why a correction is neccessary.) + // + // Searched is done in the following order: + // + // - wrapLineIndex, + // - wrapLineIndex - 1, + // - wrapLineIndex + 1, + // - wrapLineIndex - 2, + // - wrapLineIndex + 2, + // + // etc. until either the float reference has been found or + // all lines have been searched (the latter triggers an + // abortion). + + bool exceedsBeginning = false, exceedsEnd = false; + for (int i = 0; !found; i++) { + bool exceeds; + int lineIndex2; + if (i % 2 == 0) { + // even: +0, +1, +2, ... + lineIndex2 = wrapLineIndex + i / 2; + if (i > 0) + exceeds = exceedsEnd = lineIndex2 >= lines->size (); + else + exceeds = exceedsEnd = false; + } else { + // odd: -1, -2, ... + lineIndex2 = wrapLineIndex - (i + 1) / 2; + exceeds = exceedsBeginning = lineIndex2 < 0; + } + + if (exceedsBeginning && exceedsEnd) + break; + + if (!exceeds) { + Line *line = lines->getRef (lineIndex2); + for (int wordIndex = line->firstWord; + !found && wordIndex <= line->lastWord; wordIndex++) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF && + word->content.widget == vloat) { + found = true; + // Correct only by smaller values (case (1) above): + wrapLineIndex = misc::min (wrapLineIndex, lineIndex2); + } + } + } + } + + if (!found) + printBorderChangedErrorAndAbort (y, vloat, wrapLineIndex); + } + + DBG_OBJ_MSGF ("resize", 1, "Corrected to line %d.", wrapLineIndex); + + queueResize (OutOfFlowMgr::createRefNormalFlow (wrapLineIndex), true); + + // Notice that the line no. wrapLineIndex may not exist yet. + if (wrapLineIndex == 0) + lastWordDrawn = misc::min (lastWordDrawn, -1); + else + lastWordDrawn = misc::min (lastWordDrawn, + lines->getRef(wrapLineIndex - 1)->lastWord); + } + + DBG_OBJ_MSG_END (); +} + +void Textblock::printBorderChangedErrorAndAbort (int y, Widget *vloat, + int wrapLineIndex) +{ + // TODO Should be adjusted to recent changes in borderChanged(). + + printf ("*** This call failed! ***\n"); + printf ("[%p] borderChanged (%d, %p (%s))\n", + this, y, vloat, vloat->getClassName()); + printf (" Initial wrapLineIndex = line %d (of %d lines). " + "Has to be corrected.\n", wrapLineIndex, lines->size ()); + + printf (" search non-existing line (from %d to %d):\n", + lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0, + words->size() - 1); + for (int wordIndex = + lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0; + wordIndex < words->size(); wordIndex++) { + printf (" word %d: ", wordIndex); + printWordShort (words->getRef (wordIndex)); + printf ("\n"); + } + + for (int lineIndex2 = wrapLineIndex; lineIndex2 >= 0; + lineIndex2--) { + Line *line = lines->getRef (lineIndex2); + printf (" searching existing line %d (from %d to %d):\n", + lineIndex2, line->firstWord, line->lastWord); + for (int wordIndex = line->firstWord; + wordIndex <= line->lastWord; wordIndex++) { + printf (" word %d: ", wordIndex); + printWordShort (words->getRef (wordIndex)); + printf ("\n"); + } + } + + bool found2 = false; + for (int lineIndex2 = wrapLineIndex + 1; + !found2 && lineIndex2 < lines->size (); lineIndex2++) { + Line *line = lines->getRef (lineIndex2); + printf (" could have searched existing line %d " + "(from %d to %d):\n", + lineIndex2, line->firstWord, line->lastWord); + for (int wordIndex = line->firstWord; + !found2 && wordIndex <= line->lastWord; wordIndex++) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF && + word->content.widget == vloat) { + found2 = true; + printf (" word %d: ", wordIndex); + printWordShort (word); + printf ("\n"); + } + } + } + + lout::misc::assertNotReached (); +} + +Textblock *Textblock::getTextblockForLine (Line *line) +{ + return getTextblockForLine (line->firstWord, line->lastWord); +} + +Textblock *Textblock::getTextblockForLine (int lineNo) +{ + int firstWord = lineNo == 0 ? 0 :lines->getRef(lineNo - 1)->lastWord + 1; + int lastWord = lineNo < lines->size() ? + lines->getRef(lineNo)->lastWord : words->size() - 1; + return getTextblockForLine (firstWord, lastWord); +} + +Textblock *Textblock::getTextblockForLine (int firstWord, int lastWord) +{ + if (firstWord < words->size ()) { + //printf ("[%p] GET_TEXTBLOCK_FOR_LINE (%d, %d)\n", + // this, firstWord, lastWord); + + // A textblock is always between two line breaks, and so the + // first word of the line. + Word *word = words->getRef (firstWord); + + if (word->content.type == core::Content::WIDGET_IN_FLOW && + word->content.widget->instanceOf (Textblock::CLASS_ID)) { + //printf (" word %d: ", firstWord); + //printWordShort (word); + //printf ("\n"); + + return (Textblock*)word->content.widget; + } + } + + //printf (" nothing\n"); + return NULL; +} + +/** + * Includes margin, border, and padding. + */ +int Textblock::yOffsetOfPossiblyMissingLine (int lineNo) +{ + DBG_OBJ_MSGF ("line.yoffset", 0, + "<b>yOffsetOfPossiblyMissingLine</b> (%d <i>of %d</i>)", + lineNo, lines->size()); + DBG_OBJ_MSG_START (); + + int result; + + if (lineNo == 0) { + result = verticalOffset + getStyle()->boxOffsetY(); + DBG_OBJ_MSGF ("line.yoffset", 1, "first line: %d + %d = %d", + verticalOffset, getStyle()->boxOffsetY(), result); + } else { + Line *prevLine = lines->getRef (lineNo - 1); + result = verticalOffset + getStyle()->boxOffsetY() + + prevLine->top + prevLine->boxAscent + prevLine->boxDescent + + prevLine->breakSpace; + DBG_OBJ_MSGF ("line.yoffset", 1, + "other line: %d + %d + %d + (%d + %d) + %d = %d", + verticalOffset, getStyle()->boxOffsetY(), + prevLine->top, prevLine->boxAscent, prevLine->boxDescent, + prevLine->breakSpace, result); + } + + DBG_OBJ_MSG_END (); + + return result; +} + +int Textblock::heightOfPossiblyMissingLine (int lineNo) +{ + DBG_OBJ_MSGF ("line.height", 0, + "<b>heightOfPossiblyMissingLine</b> (%d <i>of %d</i>)", + lineNo, lines->size()); + DBG_OBJ_MSG_START (); + + int result; + + if (lineNo < lines->size()) { + // An existing line. + + Line *line = lines->getRef (lineNo); + + // This is sometimes called within addLine, so that the + // condition above is true, but line->boxAscent and + // line->boxDescent are not set appropriately. We have to + // accumulate the heights then. + + if (line->finished) { + DBG_OBJ_MSGF ("line.height", 1, + "exists and is finished; height = %d + %d = %d", + line->boxAscent, line->boxDescent, + line->boxAscent + line->boxDescent); + result = line->boxAscent + line->boxDescent; + } else { + DBG_OBJ_MSG ("line.height", 1, "exist but is not finished"); + result = misc::max (1, newLineAscent + newLineDescent); + } + } else if (lineNo == lines->size()) { + // The line to be constructed: some words exist, but not the + // line. Accumulate the word heights. + + // Old comment: Furthermore, this is in some cases incomplete: + // see doc/dw-out-of-flow.doc. -- Still the case? + + DBG_OBJ_MSG ("line.height", 1, "does not exist"); + result = misc::max (1, newLineAscent + newLineDescent); + } else + result = 1; + + DBG_OBJ_MSGF ("line.height", 0, "result = %d", result); + DBG_OBJ_MSG_END (); + + return result; +} + } // namespace dw |