diff options
Diffstat (limited to 'dw/textblock.cc')
-rw-r--r-- | dw/textblock.cc | 401 |
1 files changed, 325 insertions, 76 deletions
diff --git a/dw/textblock.cc b/dw/textblock.cc index 9edea207..03f15f63 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -24,13 +24,14 @@ #include "../lout/unicode.hh" #include <stdio.h> -#include <math.h> +#include <math.h> // remove again? +#include <limits.h> /* * Local variables */ - /* The tooltip under mouse pointer in current textblock. No ref. hold. - * (having one per view looks not worth the extra clutter). */ +/* The tooltip under mouse pointer in current textblock. No ref. hold. + * (having one per view looks not worth the extra clutter). */ static dw::core::style::Tooltip *hoverTooltip = NULL; @@ -144,6 +145,7 @@ Textblock::Textblock (bool limitTextWidth) nonTemporaryLines = 0; words = new misc::NotSoSimpleVector <Word> (1); anchors = new misc::SimpleVector <Anchor> (1); + outOfFlowMgr = NULL; //DBG_OBJ_SET_NUM(this, "num_lines", num_lines); @@ -181,8 +183,9 @@ Textblock::~Textblock () for (int i = 0; i < words->size(); i++) { Word *word = words->getRef (i); - if (word->content.type == core::Content::WIDGET) + if (word->content.type == core::Content::WIDGET_IN_FLOW) delete word->content.widget; + /** \todo Widget references? What about texts? */ word->style->unref (); word->spaceStyle->unref (); } @@ -198,6 +201,9 @@ Textblock::~Textblock () delete words; delete anchors; + if(outOfFlowMgr) + delete outOfFlowMgr; + /* Make sure we don't own widgets anymore. Necessary before call of parent class destructor. (???) */ words = NULL; @@ -252,6 +258,21 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) requisition->ascent += getStyle()->boxOffsetY (); requisition->descent += getStyle()->boxRestHeight (); + // Dealing with parts out of flow, which may overlap the borders of + // the text block. Base lines are ignored here: they do not play a + // role (currently) and caring about them (for the future) would + // cause too much problems. + + if (outOfFlowMgr) { + int oofWidth, oofHeight; + outOfFlowMgr->getSize (requisition->width, + requisition->ascent + requisition->descent, + &oofWidth, &oofHeight); + requisition->width = misc::max (requisition->width, oofWidth); + if (oofHeight > requisition->ascent + requisition->descent) + requisition->descent = oofHeight - requisition->ascent; + } + if (requisition->width < availWidth) requisition->width = availWidth; @@ -264,7 +285,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 { @@ -312,6 +333,19 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) extremes->minWidth += diff; extremes->maxWidth += diff; + if (outOfFlowMgr) { + int oofMinWidth, oofMaxWidth; + outOfFlowMgr->getExtremes (extremes->minWidth, extremes->maxWidth, + &oofMinWidth, &oofMaxWidth); + + //printf ("[%p] extremes: %d / %d, corrected: %d / %d\n", + // this, extremes->minWidth, extremes->maxWidth, + // oofMinWidth, oofMaxWidth); + + extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth); + extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth); + } + PRINTF ("[%p] GET_EXTREMES => %d / %d\n", this, extremes->minWidth, extremes->maxWidth); } @@ -338,7 +372,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) { line = lines->getRef (lineIndex); - xCursor = lineXOffsetWidget (line); + xCursor = line->offsetCompleteWidget; for (wordIndex = line->firstWord; wordIndex <= line->lastWord; wordIndex++) { @@ -348,7 +382,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) redrawY = misc::min (redrawY, lineYOffsetWidget (line)); } - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { /** \todo Justification within the line is done here. */ childAllocation.x = xCursor + allocation->x; /* align=top: @@ -420,6 +454,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) } } + if(outOfFlowMgr) + outOfFlowMgr->sizeAllocate(allocation); + for (int i = 0; i < anchors->size(); i++) { Anchor *anchor = anchors->getRef(i); int y; @@ -450,47 +487,114 @@ void Textblock::resizeDrawImpl () void Textblock::markSizeChange (int ref) { + if (OutOfFlowMgr::isRefOutOfFlow (ref)) { + assert (outOfFlowMgr != NULL); + outOfFlowMgr->markSizeChange (ref); + } else { + PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", + this, ref, wrapRefLines); + + /* By the way: ref == -1 may have two different causes: (i) flush() + calls "queueResize (-1, true)", when no rewrapping is necessary; + and (ii) a word may have parentRef == -1 , when it is not yet + added to a line. In the latter case, nothing has to be done + now, but addLine(...) will do everything necessary. */ + if (ref != -1) { + if (wrapRefLines == -1) + wrapRefLines = OutOfFlowMgr::getLineNoFromRef (ref); + else + wrapRefLines = misc::min (wrapRefLines, + OutOfFlowMgr::getLineNoFromRef (ref)); + } + + PRINTF (" ... => %d\n", wrapRefLine); + + // It seems that sometimes (even without floats) the lines + // structure is changed, so that wrapRefLines may refers to a + // line which does not exist anymore. Should be examined + // again. Until then, setting wrapRefLines to the same value is + // a workaround. + markExtremesChange (ref); + } +} + +void Textblock::markExtremesChange (int ref) +{ PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", this, ref, wrapRefLines); - /* By the way: ref == -1 may have two different causes: (i) flush() - calls "queueResize (-1, true)", when no rewrapping is necessary; - and (ii) a word may have parentRef == -1 , when it is not yet - added to a line. In the latter case, nothing has to be done - now, but addLine(...) will do everything necessary. */ - if (ref != -1) { - if (wrapRefLines == -1) - wrapRefLines = ref; - else - wrapRefLines = misc::min (wrapRefLines, ref); + if (OutOfFlowMgr::isRefOutOfFlow (ref)) { + assert (outOfFlowMgr != NULL); + outOfFlowMgr->markExtremesChange (ref); + } else { + PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n", + this, ref, wrapRefParagraphs); + + /* By the way: ref == -1 may have two different causes: (i) flush() + calls "queueResize (-1, true)", when no rewrapping is necessary; + and (ii) a word may have parentRef == -1 , when it is not yet + added to a line. In the latter case, nothing has to be done + now, but addLine(...) will do everything necessary. */ + if (ref != -1) { + if (wrapRefParagraphs == -1) + wrapRefParagraphs = OutOfFlowMgr::getLineNoFromRef (ref); + else + wrapRefParagraphs = + misc::min (wrapRefParagraphs, + OutOfFlowMgr::getLineNoFromRef (ref)); + } + + PRINTF (" ... => %d\n", wrapRefParagraphs); } +} - PRINTF (" ... => %d\n", wrapRefLine); +void Textblock::notifySetAsTopLevel() +{ + PRINTF ("%p becomes toplevel\n", this); + containingBlock = this; + PRINTF ("-> %p is its own containing block\n", this); +} - // It seems that sometimes the lines structure is changed, so that - // wrapRefLines may refers to a line which does not exist - // anymore. Should be examined again. Until then, setting - // wrapRefLines to the same value is a workaround. - markExtremesChange (ref); +bool Textblock::isContainingBlock (Widget *widget) +{ + return + // Of course, only textblocks are considered as containing + // blocks. + widget->instanceOf (Textblock::CLASS_ID) && + // The second condition: that this block is "out of flow", in a + // wider sense. + (// The toplevel widget is "out of flow", since there is no + // parent, and so no context. + widget->getParent() == NULL || + // A similar reasoning applies to a widget with another parent + // than a textblock (typical example: a table cell (this is + // also a text block) within a table widget). + !widget->getParent()->instanceOf (Textblock::CLASS_ID) || + // Finally, "out of flow" in a narrower sense: floats and + // absolute positions. + OutOfFlowMgr::isWidgetOutOfFlow (widget)); } -void Textblock::markExtremesChange (int ref) +void Textblock::notifySetParent () { - PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n", - this, ref, wrapRefParagraphs); - - /* By the way: ref == -1 may have two different causes: (i) flush() - calls "queueResize (-1, true)", when no rewrapping is necessary; - and (ii) a word may have parentRef == -1 , when it is not yet - added to a line. In the latter case, nothing has to be done - now, but addLine(...) will do everything necessary. */ - if (ref != -1) { - if (wrapRefParagraphs == -1) - wrapRefParagraphs = ref; - else - wrapRefParagraphs = misc::min (wrapRefParagraphs, ref); - } + PRINTF ("%p becomes a child of %p\n", this, getParent()); - PRINTF (" ... => %d\n", wrapRefParagraphs); + // 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) @@ -504,7 +608,7 @@ void Textblock::setWidth (int width) // words->size()); availWidth = width; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; redrawY = 0; } @@ -519,7 +623,7 @@ void Textblock::setAscent (int ascent) // words->size()); availAscent = ascent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; } } @@ -533,7 +637,7 @@ void Textblock::setDescent (int descent) // words->size()); availDescent = descent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; } } @@ -650,11 +754,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; @@ -749,8 +853,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; @@ -1133,7 +1238,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; @@ -1144,10 +1249,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; @@ -1291,7 +1396,7 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) if (yWidgetBase + line->boxDescent <= y) return NULL; - xCursor = lineXOffsetWidget (line); + xCursor = line->offsetCompleteWidget; for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { word = words->getRef (wordIndex); lastXCursor = xCursor; @@ -1337,6 +1442,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area) drawLine (line, view, area); } + + if(outOfFlowMgr) + outOfFlowMgr->draw(view, area); } /** @@ -1750,6 +1858,12 @@ 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); + // 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); } @@ -1758,28 +1872,42 @@ void Textblock::addText0 (const char *text, size_t len, short flags, */ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) { - Word *word; - core::Requisition size; - /* We first assign -1 as parent_ref, since the call of widget->size_request * will otherwise let this Textblock be rewrapped from the beginning. * (parent_ref is actually undefined, but likely has the value 0.) At the, * end of this function, the correct value is assigned. */ widget->parentRef = -1; - PRINTF ("%p becomes child of %p\n", widget, this); - - widget->setParent (this); widget->setStyle (style); - calcWidgetSize (widget, &size); - word = addWord (size.width, size.ascent, size.descent, 0, style); + PRINTF ("adding the %s %p to %p ...\n", + widget->getClassName(), widget, this); + + if (OutOfFlowMgr::isWidgetOutOfFlow (widget)) { + PRINTF (" -> out of flow.\n"); + + if (containingBlock->outOfFlowMgr == NULL) + containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock); + + widget->setParent (containingBlock); + widget->setGenerator (this); + containingBlock->outOfFlowMgr->addWidget (widget, this); + Word *word = addWord (0, 0, 0, false, style); + word->content.type = core::Content::WIDGET_OOF_REF; + word->content.widget = widget; + word->style = style; + } else { + PRINTF (" -> within flow.\n"); - word->content.type = core::Content::WIDGET; - word->content.widget = widget; + widget->setParent (this); - //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1, - // word->content.widget); + core::Requisition size; + calcWidgetSize (widget, &size); + Word *word = + addWord (size.width, size.ascent, size.descent, 0, style); + word->content.type = core::Content::WIDGET_IN_FLOW; + word->content.widget = widget; + } processWord (words->size () - 1); //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref", @@ -1934,7 +2062,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 @@ -1943,23 +2071,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 = @@ -1968,7 +2096,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; } } @@ -1976,6 +2105,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) } /* Otherwise continue to examine parents. */ } + /* Return in any case. */ return; } @@ -2024,7 +2154,6 @@ void Textblock::addLinebreak (core::style::Style *style) processWord (words->size () - 1); } - /** * \brief Search recursively through widget. * @@ -2033,6 +2162,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; @@ -2043,6 +2176,14 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) return NULL; } + // First, search for widgets out of flow, notably floats, since + // there are cases where they overlap child textblocks. Should + // later be refined using z-index. + Widget *oofWidget = + outOfFlowMgr ? outOfFlowMgr->getWidgetAtPoint (x, y, level) : NULL; + if (oofWidget) + return oofWidget; + lineIndex = findLineIndex (y - allocation.y); if (lineIndex < 0 || lineIndex >= lines->size ()) { @@ -2054,7 +2195,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); @@ -2135,7 +2276,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, @@ -2187,4 +2328,112 @@ void Textblock::queueDrawRange (int index1, int index2) } } +void Textblock::borderChanged (int y) +{ + PRINTF ("[%p] Border has changed: %d\n", this, y); + + int lineIndex = findLineIndex (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; + + PRINTF ("[%p] Rewrapping from line %d.\n", this, wrapLineIndex); + queueResize (OutOfFlowMgr::createRefNormalFlow (wrapLineIndex), true); + } +} + +Textblock *Textblock::getTextblockForLine (Line *line) +{ + return getTextblockForLine (line->firstWord, line->lastWord); +} + +Textblock *Textblock::getTextblockForLine (int lineNo) +{ + int firstWord = lineNo == 0 ? 0 :lines->getRef(lineNo - 1)->lastWord + 1; + int lastWord = lineNo < lines->size() ? + lines->getRef(lineNo)->lastWord : words->size() - 1; + return getTextblockForLine (firstWord, lastWord); +} + +Textblock *Textblock::getTextblockForLine (int firstWord, int lastWord) +{ + if (firstWord < words->size ()) { + for (int wordIndex = firstWord; wordIndex <= lastWord; + wordIndex++) { + Word *word = words->getRef (wordIndex); + + if (word->content.type == core::Content::WIDGET_IN_FLOW && + word->content.widget->instanceOf (Textblock::CLASS_ID)) { + //printf ("[%p] (line %d of %d (from %d to %d), word %d) ", + // this, lineNo, lines->size (), firstWord, lastWord, + // wordIndex); + //printWordShort (word); + //printf ("\n"); + + return (Textblock*)word->content.widget; + } + } + } + + return NULL; +} + +/** + * Includes margin, border, and padding. + */ +int Textblock::topOfPossiblyMissingLine (int lineNo) +{ + if (lineNo == 0) + return getStyle()->boxOffsetY(); + else { + Line *prevLine = lines->getRef (lineNo - 1); + return prevLine->top + prevLine->boxAscent + prevLine->boxDescent + + prevLine->breakSpace + getStyle()->boxOffsetY(); + } +} + +int Textblock::heightOfPossiblyMissingLine (int lineNo) +{ + PRINTF ("[%p] HEIGHT_OF_POSSIBLY_MISSING_LINE (%d)\n", this, lineNo); + + if (lineNo < lines->size()) { + // An existing line. + Line *line = lines->getRef (lineNo); + PRINTF (" exists; height = %d + %d = %d\n", line->boxAscent, + line->boxDescent, line->boxAscent + line->boxDescent); + return line->boxAscent + line->boxDescent; + } else if (lineNo == lines->size()) { + // The line to be constructed: some words exist, but not the + // line. Accumulate the word heights. TODO Could be faster by + // accumulating them when words are added. + + // Furthermore, this is in some cases incomplete: see + // doc/dw-out-of-flow.doc. + + int h = 1; + int firstWord = lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0; + + PRINTF (" does not exist; accumulating words from %d to %d\n", + firstWord, words->size()); + + for (int i = firstWord; i < words->size(); i++) { + Word *word = words->getRef (i); + h = misc::max (h, word->size.ascent + word->size.descent); + //printf (" word %d: ", i); + //printWordShort (word); + //printf (" => %d + %d = %d\n", word->size.ascent, word->size.descent, + // word->size.ascent + word->size.descent); + } + PRINTF (" => %d\n", h); + return h; + } else + return 1; +} + } // namespace dw |