diff options
author | Johannes Hofmann <Johannes.Hofmann@gmx.de> | 2010-08-20 23:24:19 +0200 |
---|---|---|
committer | Johannes Hofmann <Johannes.Hofmann@gmx.de> | 2010-08-20 23:24:19 +0200 |
commit | f5c598b518d1f906148534d015f50075d3e8242d (patch) | |
tree | 21dd70add5b366c3dd80641b77f6b18e0baa009e /dw/textblock.cc | |
parent | e98d02a01ffeb18ede86af025e51ae1ec011c75a (diff) | |
parent | 5f0fc0e48b8cbee7e1795935da0abff6627fd498 (diff) |
merge
Diffstat (limited to 'dw/textblock.cc')
-rw-r--r-- | dw/textblock.cc | 1276 |
1 files changed, 658 insertions, 618 deletions
diff --git a/dw/textblock.cc b/dw/textblock.cc index 6bfd4d90..33d3a1b0 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -14,18 +14,20 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "textblock.hh" #include "table.hh" // Yes, this is ugly. -- SG +#include "../lout/msg.h" #include "../lout/misc.hh" #include <stdio.h> #include <math.h> // remove again #include <limits.h> +using namespace lout; + namespace dw { int Textblock::CLASS_ID = -1; @@ -34,15 +36,16 @@ Textblock::Textblock (bool limitTextWidth) { registerName ("dw::Textblock", &CLASS_ID); setFlags (USES_HINTS); + setButtonSensitive(true); - listItem = false; + hasListitemValue = false; innerPadding = 0; line1Offset = 0; line1OffsetEff = 0; ignoreLine1OffsetSometimes = false; mustQueueResize = false; redrawY = 0; - lastWordDrawn = 0; + lastWordDrawn = -1; /* * The initial sizes of lines and words should not be @@ -56,6 +59,7 @@ Textblock::Textblock (bool limitTextWidth) lines = new misc::SimpleVector <Line> (1); words = new misc::SimpleVector <Word> (1); leftFloatSide = rightFloatSide = NULL; + anchors = new misc::SimpleVector <Anchor> (1); //DBG_OBJ_SET_NUM(page, "num_lines", num_lines); @@ -97,17 +101,20 @@ Textblock::~Textblock () Word *word = words->getRef (i); if (word->content.type == core::Content::WIDGET) delete word->content.widget; - else if (word->content.type == core::Content::ANCHOR) - /* This also frees the names (see removeAnchor() and related). */ - removeAnchor(word->content.anchor); - word->style->unref (); word->spaceStyle->unref (); } + for (int i = 0; i < anchors->size(); i++) { + Anchor *anchor = anchors->getRef (i); + /* This also frees the names (see removeAnchor() and related). */ + removeAnchor(anchor->name); + } + delete lines; delete words; - + delete anchors; + if(leftFloatSide) delete leftFloatSide; if(rightFloatSide) @@ -133,11 +140,12 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) Line *lastLine = lines->getRef (lines->size () - 1); requisition->width = misc::max (lastLine->maxLineWidth, lastLineWidth); - /* Note: the break_space of the last line is ignored, so breaks + /* Note: the breakSpace of the last line is ignored, so breaks at the end of a textblock are not visible. */ - requisition->ascent = lines->getRef(0)->ascent; + requisition->ascent = lines->getRef(0)->boxAscent; requisition->descent = lastLine->top - + lastLine->ascent + lastLine->descent - lines->getRef(0)->ascent; + + lastLine->boxAscent + lastLine->boxDescent - + lines->getRef(0)->boxAscent; } else { requisition->width = lastLineWidth; requisition->ascent = 0; @@ -237,21 +245,23 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) for (lineIndex = wrapRef; lineIndex < lines->size (); lineIndex++) { //DBG_MSGF (widget, "extremes", 0, "line %d", lineIndex); //DBG_MSG_START (widget); + core::style::WhiteSpace ws; line = lines->getRef (lineIndex); - nowrap = - words->getRef(line->firstWord)->style->whiteSpace - != core::style::WHITE_SPACE_NORMAL; + ws = words->getRef(line->firstWord)->style->whiteSpace; + nowrap = ws == core::style::WHITE_SPACE_PRE || + ws == core::style::WHITE_SPACE_NOWRAP; //DEBUG_MSG (DEBUG_SIZE_LEVEL, " line %d (of %d), nowrap = %d\n", // lineIndex, page->num_lines, nowrap); - for (wordIndex = line->firstWord; wordIndex < line->lastWord; + for (wordIndex = line->firstWord; wordIndex <= line->lastWord; wordIndex++) { word = words->getRef (wordIndex); getWordExtremes (word, &wordExtremes); /* For the first word, we simply add the line1_offset. */ + /* This test looks questionable */ if (ignoreLine1OffsetSometimes && wordIndex == 0) { wordExtremes.minWidth += line1Offset; //DEBUG_MSG (DEBUG_SIZE_LEVEL + 1, @@ -266,8 +276,8 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) extremes->minWidth = wordExtremes.minWidth; } - //printf("parMax = %d, wordMaxWidth=%d, prevWordSpace=%d\n", - // parMax, wordExtremes.maxWidth, prevWordSpace); + _MSG("parMax = %d, wordMaxWidth=%d, prevWordSpace=%d\n", + parMax, wordExtremes.maxWidth, prevWordSpace); if (word->content.type != core::Content::BREAK) parMax += prevWordSpace; parMax += wordExtremes.maxWidth; @@ -279,11 +289,9 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) // word_extremes.maxWidth); } - if ((line->lastWord > line->firstWord && - words->getRef(line->lastWord - 1)->content.type + if ((words->getRef(line->lastWord)->content.type == core::Content::BREAK ) || lineIndex == lines->size () - 1 ) { - word = words->getRef (line->lastWord - 1); //DEBUG_MSG (DEBUG_SIZE_LEVEL + 2, // " parMax = %d, after word %d (%s)\n", @@ -335,7 +343,6 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) int xCursor; core::Allocation childAllocation; core::Allocation *oldChildAllocation; - int wordInLine; if (allocation->width != this->allocation.width) { redrawY = 0; @@ -345,17 +352,15 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) line = lines->getRef (lineIndex); xCursor = lineXOffsetWidget (line); - wordInLine = 0; - for (wordIndex = line->firstWord; wordIndex < line->lastWord; + for (wordIndex = line->firstWord; wordIndex <= line->lastWord; wordIndex++) { word = words->getRef (wordIndex); - if (wordIndex == lastWordDrawn) { + if (wordIndex == lastWordDrawn + 1) { redrawY = misc::min (redrawY, lineYOffsetWidget (line)); } - switch (word->content.type) { - case core::Content::WIDGET: + if (word->content.type == core::Content::WIDGET) { /** \todo Justification within the line is done here. */ childAllocation.x = xCursor + allocation->x; /* align=top: @@ -367,7 +372,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) * http://www.dillo.org/test/img/ */ childAllocation.y = lineYOffsetCanvasAllocation (line, allocation) - + (line->ascent - word->size.ascent); + + (line->boxAscent - word->size.ascent); // - word->content.widget->getStyle()->margin.top; childAllocation.width = word->size.width; childAllocation.ascent = word->size.ascent; @@ -376,9 +381,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) // + word->content.widget->getStyle()->margin.bottom; oldChildAllocation = word->content.widget->getAllocation(); - - if (childAllocation.x != oldChildAllocation->x || - childAllocation.y != oldChildAllocation->y || + + if (childAllocation.x != oldChildAllocation->x || + childAllocation.y != oldChildAllocation->y || childAllocation.width != oldChildAllocation->width) { /* The child widget has changed its position or its width * so we need to redraw from this line onwards. @@ -397,38 +402,53 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) * child might be a table covering the whole page so we would * end up redrawing the whole screen over and over. * The drawing of the child content is left to the child itself. + * However this optimization is only possible if the widget is + * the only word in the line apart from an optional BREAK. + * Otherwise the height change of the widget could change the + * position of other words in the line, requiring a + * redraw of the complete line. */ - int childChangedY = - misc::min(childAllocation.y - allocation->y + - childAllocation.ascent + childAllocation.descent, - oldChildAllocation->y - this->allocation.y + - oldChildAllocation->ascent + oldChildAllocation->descent); - - redrawY = misc::min (redrawY, childChangedY); + if (line->lastWord == line->firstWord || + (line->lastWord == line->firstWord + 1 && + words->getRef (line->lastWord)->content.type == + core::Content::BREAK)) { + + int childChangedY = + misc::min(childAllocation.y - allocation->y + + childAllocation.ascent + childAllocation.descent, + oldChildAllocation->y - this->allocation.y + + oldChildAllocation->ascent + + oldChildAllocation->descent); + + redrawY = misc::min (redrawY, childChangedY); + } else { + redrawY = misc::min (redrawY, lineYOffsetWidget (line)); + } } - word->content.widget->sizeAllocate (&childAllocation); - break; - - case core::Content::ANCHOR: - changeAnchor (word->content.anchor, - lineYOffsetCanvasAllocation (line, allocation)); - break; - - default: - wordInLine++; - // make compiler happy - break; } xCursor += (word->size.width + word->effSpace); } } - + if(leftFloatSide) leftFloatSide->sizeAllocate(allocation); if(rightFloatSide) rightFloatSide->sizeAllocate(allocation); + + for (int i = 0; i < anchors->size(); i++) { + Anchor *anchor = anchors->getRef(i); + int y; + + if (anchor->wordIndex >= words->size()) { + y = allocation->y + allocation->ascent + allocation->descent; + } else { + Line *line = lines->getRef(findLineOfWord (anchor->wordIndex)); + y = lineYOffsetCanvasAllocation (line, allocation); + } + changeAnchor (anchor->name, y); + } } void Textblock::resizeDrawImpl () @@ -594,28 +614,22 @@ bool Textblock::motionNotifyImpl (core::EventMotion *event) if (event->state & core::BUTTON1_MASK) return sendSelectionEvent (core::SelectionState::BUTTON_MOTION, event); else { - int linkOld, wordIndex; - core::style::Tooltip *tooltipOld; - - wordIndex = findWord (event->xWidget, event->yWidget); + bool inSpace; + int linkOld = hoverLink; + core::style::Tooltip *tooltipOld = hoverTooltip; + const Word *word = findWord (event->xWidget, event->yWidget, &inSpace); // cursor from word or widget style - if (wordIndex == -1) + if (word == NULL) { setCursor (getStyle()->cursor); - else - setCursor (words->getRef(wordIndex)->style->cursor); - - linkOld = hoverLink; - tooltipOld = hoverTooltip; - - if (wordIndex == -1) { hoverLink = -1; hoverTooltip = NULL; } else { - hoverLink = words->getRef(wordIndex)->style->x_link; - hoverTooltip = words->getRef(wordIndex)->style->x_tooltip; + core::style::Style *style = inSpace ? word->spaceStyle : word->style; + setCursor (style->cursor); + hoverLink = style->x_link; + hoverTooltip = style->x_tooltip; } - // Show/hide tooltip if (tooltipOld != hoverTooltip) { if (tooltipOld) @@ -626,7 +640,7 @@ bool Textblock::motionNotifyImpl (core::EventMotion *event) hoverTooltip->onMotion (); if (hoverLink != linkOld) - return emitLinkEnter (hoverLink, -1, -1, -1); + return layout->emitLinkEnter (this, hoverLink, -1, -1, -1); else return hoverLink != -1; } @@ -639,7 +653,11 @@ void Textblock::enterNotifyImpl (core::EventCrossing *event) void Textblock::leaveNotifyImpl (core::EventCrossing *event) { hoverLink = -1; - (void) emitLinkEnter (hoverLink, -1, -1, -1); + (void) layout->emitLinkEnter (this, hoverLink, -1, -1, -1); + if (hoverTooltip) { + hoverTooltip->onLeave(); + hoverTooltip = NULL; + } } /** @@ -651,114 +669,110 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, core::Iterator *it; Line *line, *lastLine; int nextWordStartX, wordStartX, wordX, nextWordX, yFirst, yLast; - int charPos = 0, prevPos, wordIndex, lineIndex, link; + int charPos = 0, link = -1, prevPos, wordIndex, lineIndex; Word *word; - bool found, withinContent, r; - - if (words->size () == 0) - // no contens at all - return false; + bool found, r, withinContent = true; - // In most cases true, so set here: - link = -1; - withinContent = true; - - lastLine = lines->getRef (lines->size () - 1); - yFirst = lineYOffsetCanvasI (0); - yLast = - lineYOffsetCanvas (lastLine) + lastLine->ascent + lastLine->descent; - if (event->yCanvas < yFirst) { - // Above the first line: take the first word. - withinContent = false; - wordIndex = 0; - charPos = 0; - } else if (event->yCanvas >= yLast) { - // Below the last line: take the last word. + if (words->size () == 0) { withinContent = false; - wordIndex = words->size () - 1; - word = words->getRef (wordIndex); - charPos = word->content.type == core::Content::TEXT ? - strlen (word->content.text) : 0; + wordIndex = -1; } else { - lineIndex = findLineIndex (event->yWidget); - line = lines->getRef (lineIndex); - - // Pointer within the break space? - if (event->yWidget > - (lineYOffsetWidget (line) + line->ascent + line->descent)) { - // Choose this break. + lastLine = lines->getRef (lines->size () - 1); + yFirst = lineYOffsetCanvasI (0); + yLast = lineYOffsetCanvas (lastLine) + lastLine->boxAscent + + lastLine->boxDescent; + if (event->yCanvas < yFirst) { + // Above the first line: take the first word. withinContent = false; - wordIndex = line->lastWord - 1; + wordIndex = 0; charPos = 0; - } else if (event->xWidget < lineXOffsetWidget (line)) { - // Left of the first word in the line. - wordIndex = line->firstWord; + } else if (event->yCanvas >= yLast) { + // Below the last line: take the last word. withinContent = false; - charPos = 0; + wordIndex = words->size () - 1; + word = words->getRef (wordIndex); + charPos = word->content.type == core::Content::TEXT ? + strlen (word->content.text) : 0; } else { - nextWordStartX = lineXOffsetWidget (line); - found = false; - for (wordIndex = line->firstWord; - !found && wordIndex < line->lastWord; - wordIndex++) { - word = words->getRef (wordIndex); - wordStartX = nextWordStartX; - nextWordStartX += word->size.width + word->effSpace; - - if (event->xWidget >= wordStartX && - event->xWidget < nextWordStartX) { - // We have found the word. - if (word->content.type == core::Content::TEXT) { - // Search the character the mouse pointer is in. - // nextWordX is the right side of this character. - charPos = 0; - while ((nextWordX = wordStartX + - layout->textWidth (word->style->font, - word->content.text, charPos)) - <= event->xWidget) - charPos = layout->nextGlyph (word->content.text, charPos); - - // The left side of this character. - prevPos = layout->prevGlyph (word->content.text, charPos); - wordX = wordStartX + layout->textWidth (word->style->font, - word->content.text, - prevPos); - - // If the mouse pointer is left from the middle, use the left - // position, otherwise, use the right one. - if (event->xWidget <= (wordX + nextWordX) / 2) - charPos = prevPos; - } else { - // Depends on whether the pointer is within the left or - // right half of the (non-text) word. - if (event->xWidget >= - (wordStartX + nextWordStartX) / 2) - charPos = core::SelectionState::END_OF_WORD; - else + lineIndex = findLineIndex (event->yWidget); + line = lines->getRef (lineIndex); + + // Pointer within the break space? + if (event->yWidget > + (lineYOffsetWidget (line) + line->boxAscent + line->boxDescent)) { + // Choose this break. + withinContent = false; + wordIndex = line->lastWord; + charPos = 0; + } else if (event->xWidget < lineXOffsetWidget (line)) { + // Left of the first word in the line. + wordIndex = line->firstWord; + withinContent = false; + charPos = 0; + } else { + nextWordStartX = lineXOffsetWidget (line); + found = false; + for (wordIndex = line->firstWord; + !found && wordIndex <= line->lastWord; + wordIndex++) { + word = words->getRef (wordIndex); + wordStartX = nextWordStartX; + nextWordStartX += word->size.width + word->effSpace; + + if (event->xWidget >= wordStartX && + event->xWidget < nextWordStartX) { + // We have found the word. + if (word->content.type == core::Content::TEXT) { + // Search the character the mouse pointer is in. + // nextWordX is the right side of this character. charPos = 0; + while ((nextWordX = wordStartX + + layout->textWidth (word->style->font, + word->content.text, charPos)) + <= event->xWidget) + charPos = layout->nextGlyph (word->content.text, + charPos); + // The left side of this character. + prevPos = layout->prevGlyph (word->content.text, charPos); + wordX = wordStartX + layout->textWidth (word->style->font, + word->content.text, + prevPos); + + // If the mouse pointer is left from the middle, use the + // left position, otherwise, use the right one. + if (event->xWidget <= (wordX + nextWordX) / 2) + charPos = prevPos; + } else { + // Depends on whether the pointer is within the left or + // right half of the (non-text) word. + if (event->xWidget >= + (wordStartX + nextWordStartX) / 2) + charPos = core::SelectionState::END_OF_WORD; + else + charPos = 0; + } + + found = true; + link = word->style ? word->style->x_link : -1; + break; } - - found = true; - link = word->style ? word->style->x_link : -1; - break; } - } - if (!found) { - // No word found in this line (i.e. we are on the right side), - // take the last of this line. - withinContent = false; - wordIndex = line->lastWord - 1; - if (wordIndex >= words->size ()) - wordIndex--; - word = words->getRef (wordIndex); - charPos = word->content.type == core::Content::TEXT ? - strlen (word->content.text) : - (int)core::SelectionState::END_OF_WORD; + if (!found) { + // No word found in this line (i.e. we are on the right side), + // take the last of this line. + withinContent = false; + wordIndex = line->lastWord; + if (wordIndex >= words->size ()) + wordIndex--; + word = words->getRef (wordIndex); + charPos = word->content.type == core::Content::TEXT ? + strlen (word->content.text) : + (int)core::SelectionState::END_OF_WORD; + } } } } - it = new TextblockIterator (this, core::Content::SELECTION_CONTENT, wordIndex); r = selectionHandleEvent (eventType, it, charPos, link, event, @@ -794,12 +808,12 @@ void Textblock::justifyLine (Line *line, int availWidth) diff = availWidth - lastLineWidth; if (diff > 0) { origSpaceSum = 0; - for (i = line->firstWord; i < line->lastWord - 1; i++) + for (i = line->firstWord; i < line->lastWord; i++) origSpaceSum += words->getRef(i)->origSpace; origSpaceCum = 0; lastEffSpaceDiffCum = 0; - for (i = line->firstWord; i < line->lastWord - 1; i++) { + for (i = line->firstWord; i < line->lastWord; i++) { origSpaceCum += words->getRef(i)->origSpace; if (origSpaceCum == 0) @@ -818,9 +832,9 @@ void Textblock::justifyLine (Line *line, int availWidth) } -void Textblock::addLine (int wordInd, bool newPar) +Textblock::Line *Textblock::addLine (int wordIndex, bool newPar) { - Line *lastLine, *plastLine; + Line *lastLine; //DBG_MSG (page, "wrap", 0, "Dw_page_add_line"); //DBG_MSG_START (page); @@ -833,29 +847,19 @@ void Textblock::addLine (int wordInd, bool newPar) lastLine = lines->getRef (lines->size () - 1); - if (lines->size () == 1) - plastLine = NULL; - else - plastLine = lines->getRef (lines->size () - 2); - - if (plastLine) { - /* second or more lines: copy values of last line */ - lastLine->top = - plastLine->top + plastLine->ascent + - plastLine->descent + plastLine->breakSpace; - lastLine->maxLineWidth = plastLine->maxLineWidth; - lastLine->maxWordMin = plastLine->maxWordMin; - lastLine->maxParMax = plastLine->maxParMax; - lastLine->parMin = plastLine->parMin; - lastLine->parMax = plastLine->parMax; - } else { - /* first line: initialize values */ + if (lines->size () == 1) { lastLine->top = 0; lastLine->maxLineWidth = line1OffsetEff; lastLine->maxWordMin = 0; lastLine->maxParMax = 0; - lastLine->parMin = line1OffsetEff; - lastLine->parMax = line1OffsetEff; + } else { + Line *prevLine = lines->getRef (lines->size () - 2); + + lastLine->top = prevLine->top + prevLine->boxAscent + + prevLine->boxDescent + prevLine->breakSpace; + lastLine->maxLineWidth = prevLine->maxLineWidth; + lastLine->maxWordMin = prevLine->maxWordMin; + lastLine->maxParMax = prevLine->maxParMax; } //DBG_OBJ_ARRSET_NUM (page, "lines.%d.top", page->num_lines - 1, @@ -871,9 +875,9 @@ void Textblock::addLine (int wordInd, bool newPar) //DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMax", page->num_lines - 1, // lastLine->parMax); - lastLine->firstWord = wordInd; - lastLine->ascent = 0; - lastLine->descent = 0; + lastLine->firstWord = wordIndex; + lastLine->boxAscent = lastLine->contentAscent = 0; + lastLine->boxDescent = lastLine->contentDescent = 0; lastLine->marginDescent = 0; lastLine->breakSpace = 0; lastLine->leftOffset = 0; @@ -881,9 +885,9 @@ void Textblock::addLine (int wordInd, bool newPar) lastLine->boxRight = 0; //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1, - // lastLine->ascent); + // lastLine->boxAscent); //DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1, - // lastLine->descent); + // lastLine->boxDescent); /* update values in line */ lastLine->maxLineWidth = misc::max (lastLine->maxLineWidth, lastLineWidth); @@ -898,6 +902,10 @@ void Textblock::addLine (int wordInd, bool newPar) //DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxParMax", page->num_lines - 1, // lastLine->maxParMax); + /* The following code looks questionable (especially since the values + * will be overwritten). In any case, line1OffsetEff is probably + * supposed to go into lastLinePar*, not lastLine->par*. + */ if (lines->size () > 1) { lastLine->parMin = 0; lastLine->parMax = 0; @@ -921,18 +929,19 @@ void Textblock::addLine (int wordInd, bool newPar) // lastLine->parMax); //DBG_MSG_END (page); + return lastLine; } /* - * This method is called in two cases: (i) when a word is added (by - * Dw_page_add_word), and (ii) when a page has to be (partially) - * rewrapped. It does word wrap, and adds new lines, if necesary. + * This method is called in two cases: (i) when a word is added + * (ii) when a page has to be (partially) rewrapped. It does word wrap, + * and adds new lines if necessary. */ void Textblock::wordWrap(int wordIndex) { Line *lastLine; - Word *word, *prevWord; - int availWidth, lastSpace, leftOffset; + Word *word; + int availWidth, lastSpace, leftOffset, len; bool newLine = false, newPar = false; core::Extremes wordExtremes; @@ -948,6 +957,17 @@ void Textblock::wordWrap(int wordIndex) availWidth = layout->getWidthViewport () - 10; word = words->getRef (wordIndex); + word->effSpace = word->origSpace; + + /* Test whether line1Offset can be used. */ + if (wordIndex == 0) { + if (ignoreLine1OffsetSometimes && + line1Offset + word->size.width > availWidth) { + line1OffsetEff = 0; + } else { + line1OffsetEff = line1Offset; + } + } if(word->content.type == dw::core::Content::FLOAT_REF) { @@ -955,10 +975,12 @@ void Textblock::wordWrap(int wordIndex) int y = allocation.y - containingBox->allocation.y + getStyle()->boxOffsetY() + (line ? line->top : 0); - int lineHeight = line ? line->ascent + line->descent : 0; + int lineHeight = line ? line->boxAscent + line->boxDescent : 0; containingBox->handleFloatInContainer(word->content.widget, misc::max(lines->size() - 1, 0), y, lastLineWidth, lineHeight); } + + if (lines->size () == 0) { //DBG_MSG (page, "wrap", 0, "first line"); @@ -966,72 +988,61 @@ void Textblock::wordWrap(int wordIndex) newPar = true; lastLine = NULL; } else { + Word *prevWord = words->getRef (wordIndex - 1); + lastLine = lines->getRef (lines->size () - 1); - if (lines->size () > 0) { - prevWord = words->getRef (wordIndex - 1); - if (prevWord->content.type == core::Content::BREAK) { - //DBG_MSG (page, "wrap", 0, "after a break"); - /* previous word is a break */ - newLine = true; - newPar = true; - } else if (word->style->whiteSpace - != core::style::WHITE_SPACE_NORMAL) { - //DBG_MSGF (page, "wrap", 0, "no wrap (white_space = %d)", - // word->style->white_space); - newLine = false; - newPar = false; - } else { - if (lastLine->firstWord != wordIndex) { - /* Does new word fit into the last line? */ - //DBG_MSGF (page, "wrap", 0, - // "word %d (%s) fits? (%d + %d + %d <= %d)...", - // word_ind, a_Dw_content_html (&word->content), - // page->lastLine_width, prevWord->orig_space, - // word->size.width, availWidth); - newLine = (lastLineWidth + prevWord->origSpace + - word->size.width - > availWidth - - (lastLine->boxLeft + lastLine->boxRight)); - //DBG_MSGF (page, "wrap", 0, "... %s.", - // newLine ? "No" : "Yes"); - } - } + if (prevWord->content.type == core::Content::BREAK) { + //DBG_MSG (page, "wrap", 0, "after a break"); + /* previous word is a break */ + newLine = true; + newPar = true; + } else if (word->style->whiteSpace == core::style::WHITE_SPACE_NOWRAP || + word->style->whiteSpace == core::style::WHITE_SPACE_PRE) { + //DBG_MSGF (page, "wrap", 0, "no wrap (white_space = %d)", + // word->style->white_space); + newLine = false; + newPar = false; + } else if (lastLine->firstWord != wordIndex) { + /* Does new word fit into the last line? */ + //DBG_MSGF (page, "wrap", 0, + // "word %d (%s) fits? (%d + %d + %d <= %d)...", + // word_ind, a_Dw_content_html (&word->content), + // page->lastLine_width, prevWord->orig_space, + // word->size.width, availWidth); + newLine = lastLineWidth + prevWord->origSpace + word->size.width > + availWidth - (lastLine->boxLeft + lastLine->boxRight); + //DBG_MSGF (page, "wrap", 0, "... %s.", + // newLine ? "No" : "Yes"); } } - /* Has sometimes the wrong value. */ - word->effSpace = word->origSpace; - //DBG_OBJ_ARRSET_NUM (page,"words.%d.eff_space", word_ind, word->eff_space); - - /* Test, whether line1_offset can be used. */ - if (wordIndex == 0) { - if (ignoreLine1OffsetSometimes) { - if (line1Offset + word->size.width > availWidth) - line1OffsetEff = 0; - else - line1OffsetEff = line1Offset; - } else - line1OffsetEff = line1Offset; + if (newLine) { + if (word->style->textAlign == core::style::TEXT_ALIGN_JUSTIFY && + lastLine != NULL && !newPar) { + justifyLine (lastLine, availWidth); + } + lastLine = addLine (wordIndex, newPar); } - if (lastLine != NULL && newLine && !newPar && - word->style->textAlign == core::style::TEXT_ALIGN_JUSTIFY) - justifyLine (lastLine, availWidth); + lastLine->lastWord = wordIndex; + lastLine->boxAscent = misc::max (lastLine->boxAscent, word->size.ascent); + lastLine->boxDescent = misc::max (lastLine->boxDescent, word->size.descent); - if (newLine) { - addLine (wordIndex, newPar); - lastLine = lines->getRef (lines->size () - 1); - } + len = word->style->font->ascent; + if (word->style->valign == core::style::VALIGN_SUPER) + len += len / 2; + lastLine->contentAscent = misc::max (lastLine->contentAscent, len); - lastLine->lastWord = wordIndex + 1; - lastLine->ascent = misc::max (lastLine->ascent, (int) word->size.ascent); - lastLine->descent = misc::max (lastLine->descent, (int) word->size.descent); + len = word->style->font->descent; + if (word->style->valign == core::style::VALIGN_SUB) + len += word->style->font->ascent / 3; + lastLine->contentDescent = misc::max (lastLine->contentDescent, len); //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1, - // lastLine->ascent); + // lastLine->boxAscent); //DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1, - // lastLine->descent); + // lastLine->boxDescent); if (word->content.type == core::Content::WIDGET) { lastLine->marginDescent = @@ -1049,49 +1060,52 @@ void Textblock::wordWrap(int wordIndex) /* Here, we know already what the break and the bottom margin * contributed to the space before this line. */ - lastLine->ascent = - misc::max (lastLine->ascent, + lastLine->boxAscent = + misc::max (lastLine->boxAscent, word->size.ascent + word->content.widget->getStyle()->margin.top); //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1, - // lastLine->ascent); + // lastLine->boxAscent); } - } else + } else { lastLine->marginDescent = - misc::max (lastLine->marginDescent, lastLine->descent); + misc::max (lastLine->marginDescent, lastLine->boxDescent); - getWordExtremes (word, &wordExtremes); - lastSpace = (wordIndex > 0) ? words->getRef(wordIndex - 1)->origSpace : 0; + if (word->content.type == core::Content::BREAK) + lastLine->breakSpace = + misc::max (word->content.breakSpace, + lastLine->marginDescent - lastLine->boxDescent, + lastLine->breakSpace); + } - if (word->content.type == core::Content::BREAK) - lastLine->breakSpace = - misc::max (word->content.breakSpace, - lastLine->marginDescent - lastLine->descent, - lastLine->breakSpace); + lastSpace = (wordIndex > 0) ? words->getRef(wordIndex - 1)->origSpace : 0; - lastLineWidth += word->size.width; if (!newLine) lastLineWidth += lastSpace; - - lastLineParMin += wordExtremes.maxWidth; - lastLineParMax += wordExtremes.maxWidth; if (!newPar) { lastLineParMin += lastSpace; lastLineParMax += lastSpace; } - if (word->style->whiteSpace != core::style::WHITE_SPACE_NORMAL) { + lastLineWidth += word->size.width; + + getWordExtremes (word, &wordExtremes); + lastLineParMin += wordExtremes.maxWidth; /* Why maxWidth? */ + lastLineParMax += wordExtremes.maxWidth; + + if (word->style->whiteSpace == core::style::WHITE_SPACE_NOWRAP || + word->style->whiteSpace == core::style::WHITE_SPACE_PRE) { lastLine->parMin += wordExtremes.minWidth + lastSpace; /* This may also increase the accumulated minimum word width. */ lastLine->maxWordMin = misc::max (lastLine->maxWordMin, lastLine->parMin); /* NOTE: Most code relies on that all values of nowrap are equal for all * words within one line. */ - } else - /* Simple case. */ + } else { lastLine->maxWordMin = misc::max (lastLine->maxWordMin, wordExtremes.minWidth); + } //DBG_OBJ_SET_NUM(page, "lastLine_par_min", page->lastLine_par_min); //DBG_OBJ_SET_NUM(page, "lastLine_par_max", page->lastLine_par_max); @@ -1102,8 +1116,9 @@ void Textblock::wordWrap(int wordIndex) //DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_word_min", page->num_lines - 1, // lastLine->max_word_min); - /* Finally, justify the line. Breaks are ignored, since the HTML - * parser sometimes assignes the wrong style to them. (TODO: ) */ + /* Align the line. + * \todo Use block's style instead once paragraphs become proper blocks. + */ if (word->content.type != core::Content::BREAK) { switch (word->style->textAlign) { case core::style::TEXT_ALIGN_LEFT: @@ -1112,15 +1127,12 @@ void Textblock::wordWrap(int wordIndex) * future) */ leftOffset = 0; break; - case core::style::TEXT_ALIGN_RIGHT: leftOffset = availWidth - lastLineWidth; break; - case core::style::TEXT_ALIGN_CENTER: leftOffset = (availWidth - lastLineWidth) / 2; break; - default: /* compiler happiness */ leftOffset = 0; @@ -1130,22 +1142,22 @@ void Textblock::wordWrap(int wordIndex) if (leftOffset < 0) leftOffset = 0; - if (listItem && lastLine == lines->getRef (0)) { + if (hasListitemValue && lastLine == lines->getRef (0)) { /* List item markers are always on the left. */ lastLine->leftOffset = 0; words->getRef(0)->effSpace = words->getRef(0)->origSpace + leftOffset; //DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", 0, // page->words[0].eff_space); - } else + } else { lastLine->leftOffset = leftOffset; - + } + int y = allocation.y - containingBox->allocation.y + getStyle()->boxOffsetX () + lastLine->top; lastLine->boxLeft = calcLeftFloatBorder(y, this); lastLine->boxRight = calcRightFloatBorder(y, this); } - mustQueueResize = true; //DBG_MSG_END (page); @@ -1160,6 +1172,7 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size) { core::Requisition requisition; int availWidth, availAscent, availDescent; + core::style::Style *wstyle = widget->getStyle(); /* We ignore line1_offset[_eff]. */ availWidth = this->availWidth - getStyle()->boxDiffWidth () - innerPadding; @@ -1171,39 +1184,37 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size) widget->setAscent (availAscent); widget->setDescent (availDescent); widget->sizeRequest (size); - size->ascent -= widget->getStyle()->margin.top; - size->descent -= widget->getStyle()->margin.bottom; +// size->ascent -= wstyle->margin.top; +// size->descent -= wstyle->margin.bottom; } else { /* TODO: Use margin.{top|bottom} here, like above. * (No harm for the next future.) */ - if (widget->getStyle()->width == core::style::LENGTH_AUTO || - widget->getStyle()->height == core::style::LENGTH_AUTO) + if (wstyle->width == core::style::LENGTH_AUTO || + wstyle->height == core::style::LENGTH_AUTO) widget->sizeRequest (&requisition); - if (widget->getStyle()->width == core::style::LENGTH_AUTO) + if (wstyle->width == core::style::LENGTH_AUTO) size->width = requisition.width; - else if (core::style::isAbsLength (widget->getStyle()->width)) + else if (core::style::isAbsLength (wstyle->width)) /* Fixed lengths are only applied to the content, so we have to * add padding, border and margin. */ - size->width = core::style::absLengthVal (widget->getStyle()->width) - + widget->getStyle()->boxDiffWidth (); + size->width = core::style::absLengthVal (wstyle->width) + + wstyle->boxDiffWidth (); else - size->width = - (int) (core::style::perLengthVal (widget->getStyle()->width) - * availWidth); + size->width = (int) (core::style::perLengthVal (wstyle->width) + * availWidth); - if (widget->getStyle()->height == core::style::LENGTH_AUTO) { + if (wstyle->height == core::style::LENGTH_AUTO) { size->ascent = requisition.ascent; size->descent = requisition.descent; - } else if (core::style::isAbsLength (widget->getStyle()->height)) { + } else if (core::style::isAbsLength (wstyle->height)) { /* Fixed lengths are only applied to the content, so we have to * add padding, border and margin. */ - size->ascent = - core::style::absLengthVal (widget->getStyle()->height) - + widget->getStyle()->boxDiffHeight (); + size->ascent = core::style::absLengthVal (wstyle->height) + + wstyle->boxDiffHeight (); size->descent = 0; } else { - double len = core::style::perLengthVal (widget->getStyle()->height); + double len = core::style::perLengthVal (wstyle->height); size->ascent = (int) (len * availAscent); size->descent = (int) (len * availDescent); } @@ -1238,8 +1249,7 @@ void Textblock::rewrap () //DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines); //DBG_OBJ_SET_NUM(page, "lastLine_width", page->lastLine_width); - /* In the word list, we start at the last word, plus one (see definition - * of last_word), in the line before. */ + /* In the word list, start at the last word plus one in the line before. */ if (wrapRef > 0) { /* Note: In this case, Dw_page_real_word_wrap will immediately find * the need to rewrap the line, since we start with the last one (plus @@ -1250,11 +1260,11 @@ void Textblock::rewrap () lastLineParMin = lastLine->parMin; lastLineParMax = lastLine->parMax; - wordIndex = lastLine->lastWord; - for (i = lastLine->firstWord; i < lastLine->lastWord - 1; i++) + wordIndex = lastLine->lastWord + 1; + for (i = lastLine->firstWord; i < lastLine->lastWord; i++) lastLineWidth += (words->getRef(i)->size.width + words->getRef(i)->origSpace); - lastLineWidth += words->getRef(lastLine->lastWord - 1)->size.width; + lastLineWidth += words->getRef(lastLine->lastWord)->size.width; } else { lastLineParMin = 0; lastLineParMax = 0; @@ -1298,6 +1308,156 @@ void Textblock::rewrap () } /* + * Draw the decorations on a word. + */ +void Textblock::decorateText(core::View *view, core::style::Style *style, + core::style::Color::Shading shading, + int x, int yBase, int width) +{ + int y; + + if (style->textDecoration & core::style::TEXT_DECORATION_UNDERLINE) { + y = yBase + 1; + view->drawLine (style->color, shading, x, y, x + width - 1, y); + } + if (style->textDecoration & core::style::TEXT_DECORATION_OVERLINE) { + y = yBase - style->font->ascent + 1; + view->drawLine (style->color, shading, x, y, x + width - 1, y); + } + if (style->textDecoration & core::style::TEXT_DECORATION_LINE_THROUGH) { + int height = 1 + style->font->xHeight / 10; + + y = yBase + (style->font->descent - style->font->ascent) / 2; + view->drawRectangle (style->color, shading, true, x, y, width, height); + } +} + +/* + * Draw a word of text. + */ +void Textblock::drawText(int wordIndex, core::View *view,core::Rectangle *area, + int xWidget, int yWidgetBase) +{ + Word *word = words->getRef(wordIndex); + int xWorld = allocation.x + xWidget; + core::style::Style *style = word->style; + int yWorldBase; + + /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */ + if (style->valign == core::style::VALIGN_SUB) + yWidgetBase += style->font->ascent / 3; + else if (style->valign == core::style::VALIGN_SUPER) { + yWidgetBase -= style->font->ascent / 2; + } + yWorldBase = yWidgetBase + allocation.y; + + view->drawText (style->font, style->color, + core::style::Color::SHADING_NORMAL, xWorld, yWorldBase, + word->content.text, strlen (word->content.text)); + + if (style->textDecoration) + decorateText(view, style, core::style::Color::SHADING_NORMAL, xWorld, + yWorldBase, word->size.width); + + for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { + if (hlStart[layer].index <= wordIndex && + hlEnd[layer].index >= wordIndex) { + const int wordLen = strlen (word->content.text); + int xStart, width; + int firstCharIdx = 0; + int lastCharIdx = wordLen; + + if (wordIndex == hlStart[layer].index) + firstCharIdx = misc::min (hlStart[layer].nChar, wordLen); + + if (wordIndex == hlEnd[layer].index) + lastCharIdx = misc::min (hlEnd[layer].nChar, wordLen); + + xStart = xWorld; + if (firstCharIdx) + xStart += layout->textWidth (style->font, word->content.text, + firstCharIdx); + if (firstCharIdx == 0 && lastCharIdx == wordLen) + width = word->size.width; + else + width = layout->textWidth (style->font, + word->content.text + firstCharIdx, + lastCharIdx - firstCharIdx); + if (width > 0) { + /* Highlight text */ + core::style::Color *wordBgColor; + + if (!(wordBgColor = style->backgroundColor)) + wordBgColor = getBgColor(); + + /* Draw background for highlighted text. */ + view->drawRectangle ( + wordBgColor, core::style::Color::SHADING_INVERSE, true, xStart, + yWorldBase - style->font->ascent, width, + style->font->ascent + style->font->descent); + + /* Highlight the text. */ + view->drawText (style->font, style->color, + core::style::Color::SHADING_INVERSE, xStart, + yWorldBase, word->content.text + firstCharIdx, + lastCharIdx - firstCharIdx); + + if (style->textDecoration) + decorateText(view, style, core::style::Color::SHADING_INVERSE, + xStart, yWorldBase, width); + } + } + } +} + +/* + * Draw a space. + */ +void Textblock::drawSpace(int wordIndex, core::View *view, + core::Rectangle *area, int xWidget, int yWidgetBase) +{ + Word *word = words->getRef(wordIndex); + int xWorld = allocation.x + xWidget; + int yWorldBase; + core::style::Style *style = word->spaceStyle; + bool highlight = false; + + /* Adjust the space baseline if it is <SUP>-ed or <SUB>-ed */ + if (style->valign == core::style::VALIGN_SUB) + yWidgetBase += style->font->ascent / 3; + else if (style->valign == core::style::VALIGN_SUPER) { + yWidgetBase -= style->font->ascent / 2; + } + yWorldBase = allocation.y + yWidgetBase; + + for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { + if (hlStart[layer].index <= wordIndex && + hlEnd[layer].index > wordIndex) { + highlight = true; + break; + } + } + if (highlight) { + core::style::Color *spaceBgColor; + + if (!(spaceBgColor = style->backgroundColor)) + spaceBgColor = getBgColor(); + + view->drawRectangle ( + spaceBgColor, core::style::Color::SHADING_INVERSE, true, xWorld, + yWorldBase - style->font->ascent, word->effSpace, + style->font->ascent + style->font->descent); + } + if (style->textDecoration) { + core::style::Color::Shading shading = highlight ? + core::style::Color::SHADING_INVERSE : + core::style::Color::SHADING_NORMAL; + + decorateText(view, style, shading, xWorld, yWorldBase, word->effSpace); + } +} + +/* * Paint a line * - x and y are toplevel dw coordinates (Question: what Dw? Changed. Test!) * - area is used always (ev. set it to event->area) @@ -1305,219 +1465,56 @@ void Textblock::rewrap () */ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) { - Word *word; - int wordIndex; - int xWidget, yWidget, xWorld, yWorld, yWorldBase; - int startHL, widthHL; - int wordLen; - int diff, effHLStart, effHLEnd, layer; - core::Widget *child; - core::Rectangle childArea; - core::style::Color *color, *thisBgColor, *wordBgColor; + int xWidget = lineXOffsetWidget(line); + int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; /* Here's an idea on how to optimize this routine to minimize the number - * of calls to gdk_draw_string: + * of drawing calls: * * Copy the text from the words into a buffer, adding a new word * only if: the attributes match, and the spacing is either zero or * equal to the width of ' '. In the latter case, copy a " " into * the buffer. Then draw the buffer. */ - thisBgColor = getBgColor (); + for (int wordIndex = line->firstWord; + wordIndex <= line->lastWord && xWidget < area->x + area->width; + wordIndex++) { + Word *word = words->getRef(wordIndex); - xWidget = lineXOffsetWidget(line); - xWorld = allocation.x + xWidget; - yWidget = lineYOffsetWidget (line); - yWorld = allocation.y + yWidget; - yWorldBase = yWorld + line->ascent; + if (xWidget + word->size.width + word->effSpace >= area->x) { + if (word->content.type == core::Content::TEXT || + word->content.type == core::Content::WIDGET) { - for (wordIndex = line->firstWord; wordIndex < line->lastWord; - wordIndex++) { - word = words->getRef(wordIndex); - diff = 0; - color = word->style->color; - - //DBG_OBJ_ARRSET_NUM (page, "words.%d.<i>drawn at</i>.x", wordIndex, - // xWidget); - //DBG_OBJ_ARRSET_NUM (page, "words.%d.<i>drawn at</i>.y", wordIndex, - // yWidget); - - switch (word->content.type) { - case core::Content::TEXT: - if (word->style->backgroundColor) - wordBgColor = word->style->backgroundColor; - else - wordBgColor = thisBgColor; - - /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */ - if (word->style->valign == core::style::VALIGN_SUB) - diff = word->size.ascent / 2; - else if (word->style->valign == core::style::VALIGN_SUPER) - diff -= word->size.ascent / 3; - - /* Draw background (color, image), when given. */ - if (word->style->hasBackground () && word->size.width > 0) - drawBox (view, word->style, area, - xWidget, yWidget + line->ascent - word->size.ascent, - word->size.width, word->size.ascent + word->size.descent, - false); - - /* Draw space background (color, image), when given. */ - if (word->spaceStyle->hasBackground () && word->effSpace > 0) - drawBox (view, word->spaceStyle, area, - xWidget + word->size.width, - yWidget + line->ascent - word->size.ascent, - word->effSpace, word->size.ascent + word->size.descent, - false); - view->drawText (word->style->font, color, - core::style::Color::SHADING_NORMAL, - xWorld, yWorldBase + diff, - word->content.text, strlen (word->content.text)); - - /* underline */ - if (word->style->textDecoration & - core::style::TEXT_DECORATION_UNDERLINE) - view->drawLine (color, core::style::Color::SHADING_NORMAL, - xWorld, yWorldBase + 1 + diff, - xWorld + word->size.width - 1, - yWorldBase + 1 + diff); - if (wordIndex + 1 < line->lastWord && - (word->spaceStyle->textDecoration - & core::style::TEXT_DECORATION_UNDERLINE)) - view->drawLine (word->spaceStyle->color, - core::style::Color::SHADING_NORMAL, - xWorld + word->size.width, - yWorldBase + 1 + diff, - xWorld + word->size.width + word->effSpace - 1, - yWorldBase + 1 + diff); - - /* strike-through */ - if (word->style->textDecoration - & core::style::TEXT_DECORATION_LINE_THROUGH) - view->drawLine (color, core::style::Color::SHADING_NORMAL, - xWorld, - yWorldBase - word->size.ascent / 2 + diff, - xWorld + word->size.width - 1, - yWorldBase - word->size.ascent / 2 + diff); - if (wordIndex + 1 < line->lastWord && - (word->spaceStyle->textDecoration - & core::style::TEXT_DECORATION_LINE_THROUGH)) - view->drawLine (word->spaceStyle->color, - core::style::Color::SHADING_NORMAL, - xWorld + word->size.width, - yWorldBase - word->size.ascent / 2 + diff, - xWorld + word->size.width + word->effSpace - 1, - yWorldBase - word->size.ascent / 2 + diff); - - for (layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { - if (hlStart[layer].index <= wordIndex && - hlEnd[layer].index >= wordIndex) { - - wordLen = strlen (word->content.text); - effHLEnd = misc::min (wordLen, hlEnd[layer].nChar); - effHLStart = 0; - if (wordIndex == hlStart[layer].index) - effHLStart = misc::min (hlStart[layer].nChar, wordLen); - - effHLEnd = wordLen; - if (wordIndex == hlEnd[layer].index) - effHLEnd = misc::min (hlEnd[layer].nChar, wordLen); - - startHL = xWorld + layout->textWidth (word->style->font, - word->content.text, - effHLStart); - widthHL = - layout->textWidth (word->style->font, - word->content.text + effHLStart, - effHLEnd - effHLStart); - - // If the space after this word highlighted, and this word - // is not the last one in this line, highlight also the - // space. - /** \todo This should also be done with spaces after non-text - * words, but this is not yet defined very well. */ - if (wordIndex < hlEnd[layer].index && - wordIndex < words->size () && - wordIndex != line->lastWord - 1) - widthHL += word->effSpace; - - - if (widthHL != 0) { - /* Draw background for highlighted text. */ - view->drawRectangle (wordBgColor, - core::style::Color::SHADING_INVERSE, - true, startHL, - yWorldBase - word->size.ascent, - widthHL, - word->size.ascent + word->size.descent); - - /* Highlight the text. */ - view->drawText (word->style->font, - color, core::style::Color::SHADING_INVERSE, - startHL, yWorldBase + diff, - word->content.text + effHLStart, - effHLEnd - effHLStart); - - /* underline and strike-through */ - if (word->style->textDecoration - & core::style::TEXT_DECORATION_UNDERLINE) - view->drawLine (color, - core::style::Color::SHADING_INVERSE, - startHL, yWorldBase + 1 + diff, - startHL + widthHL - 1, - yWorldBase + 1 + diff); - if (word->style->textDecoration - & core::style::TEXT_DECORATION_LINE_THROUGH) - view->drawLine (color, - core::style::Color::SHADING_INVERSE, - startHL, - yWorldBase - word->size.ascent / 2 + diff, - startHL + widthHL - 1, - yWorldBase - word->size.ascent / 2 - + diff); + if (word->size.width > 0) { + if (word->style->hasBackground ()) { + drawBox (view, word->style, area, xWidget, + yWidgetBase - line->boxAscent, word->size.width, + line->boxAscent + line->boxDescent, false); } - } - } - break; - - case core::Content::WIDGET: - child = word->content.widget; - if (child->intersects (area, &childArea)) - child->draw (view, &childArea); - break; + if (word->content.type == core::Content::WIDGET) { + core::Widget *child = word->content.widget; + core::Rectangle childArea; - case core::Content::ANCHOR: case core::Content::BREAK: - /* nothing - an anchor/break isn't seen */ - /* - * Historical note: - * > BUG: sometimes anchors have x_space; - * > we subtract that just in case --EG - * This is inconsistent with other parts of the code, so it should - * be tried to prevent this earlier.--SG - */ - /* - * x_viewport -= word->size.width + word->eff_space; - * xWidget -= word->size.width + word->eff_space; - */ -#if 0 - /* Useful for testing: draw breaks. */ - if (word->content.type == DW_CONTENT_BREAK) - gdk_draw_rectangle (window, color, TRUE, - p_Dw_widget_xWorld_to_viewport (widget, - widget->allocation.x + - Dw_page_line_total_x_offset(page, line)), - y_viewport_base + line->descent, - DW_WIDGET_CONTENT_WIDTH(widget), - word->content.break_space); -#endif - break; + if (child->intersects (area, &childArea)) + child->draw (view, &childArea); + } else { + drawText(wordIndex, view, area, xWidget, yWidgetBase); + } + } + if (word->effSpace > 0 && wordIndex < line->lastWord && + words->getRef(wordIndex + 1)->content.type != + core::Content::BREAK) { + if (word->spaceStyle->hasBackground ()) + drawBox (view, word->spaceStyle, area, + xWidget + word->size.width, + yWidgetBase - line->boxAscent, word->effSpace, + line->boxAscent + line->boxDescent, false); + drawSpace(wordIndex, view, area, xWidget + word->size.width, + yWidgetBase); + } - default: - fprintf (stderr, "BUG!!! at (%d, %d).\n", xWorld, yWorldBase + diff); - break; + } } - - xWorld += word->size.width + word->effSpace; xWidget += word->size.width + word->effSpace; } } @@ -1534,19 +1531,19 @@ int Textblock::findLineIndex (int y) while ( step > 1 ) { index = low + step; if (index <= maxIndex && - lineYOffsetWidgetI (index) < y) + lineYOffsetWidgetI (index) <= y) low = index; step = (step + 1) >> 1; } - if (low < maxIndex && lineYOffsetWidgetI (low + 1) < y) + if (low < maxIndex && lineYOffsetWidgetI (low + 1) <= y) low++; /* * This new routine returns the line number between (top) and - * (top + size.ascent + size.descent + break_space): the space + * (top + size.ascent + size.descent + breakSpace): the space * _below_ the line is considered part of the line. Old routine - * returned line number between (top - previous_line->break_space) + * returned line number between (top - previous_line->breakSpace) * and (top + size.ascent + size.descent): the space _above_ the * line was considered part of the line. This is important for * Dw_page_find_link() --EG @@ -1562,13 +1559,13 @@ int Textblock::findLineOfWord (int wordIndex) { int high = lines->size () - 1, index, low = 0; - //g_return_val_if_fail (word_index >= 0, -1); - //g_return_val_if_fail (word_index < page->num_words, -1); + if (wordIndex < 0 || wordIndex >= words->size ()) + return -1; while (true) { index = (low + high) / 2; if (wordIndex >= lines->getRef(index)->firstWord) { - if (wordIndex < lines->getRef(index)->lastWord) + if (wordIndex <= lines->getRef(index)->lastWord) return index; else low = index + 1; @@ -1580,29 +1577,36 @@ int Textblock::findLineOfWord (int wordIndex) /** * \brief Find the index of the word, or -1. */ -int Textblock::findWord (int x, int y) +Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) { int lineIndex, wordIndex; - int xCursor, lastXCursor; + int xCursor, lastXCursor, yWidgetBase; Line *line; Word *word; + *inSpace = false; + if ((lineIndex = findLineIndex (y)) >= lines->size ()) - return -1; + return NULL; line = lines->getRef (lineIndex); - if (lineYOffsetWidget (line) + line->ascent + line->descent <= y) - return -1; + yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; + if (yWidgetBase + line->boxDescent <= y) + return NULL; xCursor = lineXOffsetWidget (line); - for (wordIndex = line->firstWord; wordIndex < line->lastWord; wordIndex++) { + for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { word = words->getRef (wordIndex); lastXCursor = xCursor; xCursor += word->size.width + word->effSpace; - if (lastXCursor <= x && xCursor > x) - return wordIndex; + if (lastXCursor <= x && xCursor > x && + y > yWidgetBase - word->size.ascent && + y <= yWidgetBase + word->size.descent) { + *inSpace = x >= xCursor - word->effSpace; + return word; + } } - return -1; + return NULL; } void Textblock::draw (core::View *view, core::Rectangle *area) @@ -1670,37 +1674,67 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent, /** * Calculate the size of a text word. */ -void Textblock::calcTextSize (const char *text, core::style::Style *style, +void Textblock::calcTextSize (const char *text, size_t len, + core::style::Style *style, core::Requisition *size) { - size->width = - layout->textWidth (style->font, text, strlen (text)); + size->width = layout->textWidth (style->font, text, len); size->ascent = style->font->ascent; size->descent = style->font->descent; + /* + * For 'normal' line height, just use ascent and descent from font. + * For absolute/percentage, line height is relative to font size, which + * is (irritatingly) smaller than ascent+descent. + */ + if (style->lineHeight != core::style::LENGTH_AUTO) { + int height, leading; + float factor = style->font->size; + + factor /= (style->font->ascent + style->font->descent); + + size->ascent = size->ascent * factor + 0.5; + size->descent = size->descent * factor + 0.5; + + /* TODO: The containing block's line-height property gives a minimum + * height for the line boxes. (Even when it's set to 'normal', i.e., + * AUTO? Apparently.) Once all block elements make Textblocks or + * something, this can be handled. + */ + if (core::style::isAbsLength (style->lineHeight)) + height = core::style::absLengthVal(style->lineHeight); + else + height = core::style::perLengthVal(style->lineHeight) * + style->font->size; + leading = height - style->font->size; + + size->ascent += leading / 2; + size->descent += leading - (leading / 2); + } + /* In case of a sub or super script we increase the word's height and * potentially the line's height. */ if (style->valign == core::style::VALIGN_SUB) - size->descent += (size->ascent / 2); + size->descent += (style->font->ascent / 3); else if (style->valign == core::style::VALIGN_SUPER) - size->ascent += (size->ascent / 3); + size->ascent += (style->font->ascent / 2); } /** - * Add a word to the page structure. Stashes the argument pointer in - * the page data structure so that it will be deallocated on destroy. + * Add a word to the page structure. */ -void Textblock::addText (const char *text, core::style::Style *style) +void Textblock::addText (const char *text, size_t len, + core::style::Style *style) { Word *word; core::Requisition size; - calcTextSize (text, style, &size); + calcTextSize (text, len, style, &size); word = addWord (size.width, size.ascent, size.descent, style); word->content.type = core::Content::TEXT; - word->content.text = layout->textZone->strdup(text); + word->content.text = layout->textZone->strndup(text, len); //DBG_OBJ_ARRSET_STR (page, "words.%d.content.text", page->num_words - 1, // word->content.text); @@ -1717,7 +1751,7 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) core::Requisition size; /* We first assign -1 as parent_ref, since the call of widget->size_request - * will otherwise let this DwPage be rewrapped from the beginning. + * 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; @@ -1749,7 +1783,7 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) /** - * Add an anchor to the page. "name" is copied, so no strdup is neccessary for + * Add an anchor to the page. "name" is copied, so no strdup is necessary for * the caller. * * Return true on success, and false, when this anchor had already been @@ -1757,11 +1791,10 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) */ bool Textblock::addAnchor (const char *name, core::style::Style *style) { - Word *word; char *copy; int y; - // Since an anchor does not take any space, it is safe to call + // Since an anchor does not take any space, it is safe to call // addAnchor already here. if (wasAllocated ()) { if (lines->size () == 0) @@ -1774,15 +1807,17 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style) if (copy == NULL) /** - * \todo It may be neccessary for future uses to save the anchor in + * \todo It may be necessary for future uses to save the anchor in * some way, e.g. when parts of the widget tree change. */ return false; else { - word = addWord (0, 0, 0, style); - word->content.type = core::Content::ANCHOR; - word->content.anchor = copy; - wordWrap (words->size () - 1); + Anchor *anchor; + + anchors->increase(); + anchor = anchors->getRef(anchors->size() - 1); + anchor->name = copy; + anchor->wordIndex = words->size(); return true; } } @@ -1793,22 +1828,15 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style) */ void Textblock::addSpace (core::style::Style *style) { - int nl, nw; - int space; + int wordIndex = words->size () - 1; - nl = lines->size () - 1; - if (nl >= 0) { - nw = words->size () - 1; - if (nw >= 0) { - /* TODO: remove this test case */ - //if (page->words[nw].orig_space != 0) { - // _MSG(" a_Dw_page_add_space:: already existing space!!!\n"); - //} + if (wordIndex >= 0) { + Word *word = words->getRef(wordIndex); - space = style->font->spaceWidth; - words->getRef(nw)->origSpace = space; - words->getRef(nw)->effSpace = space; - words->getRef(nw)->content.space = true; + if (!word->content.space) { + word->content.space = true; + word->effSpace = word->origSpace = style->font->spaceWidth + + style->wordSpacing; //DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", nw, // page->words[nw].orig_space); @@ -1816,9 +1844,8 @@ void Textblock::addSpace (core::style::Style *style) // page->words[nw].eff_space); //DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", nw, // page->words[nw].content.space); - - words->getRef(nw)->spaceStyle->unref (); - words->getRef(nw)->spaceStyle = style; + word->spaceStyle->unref (); + word->spaceStyle = style; style->ref (); } } @@ -1830,45 +1857,41 @@ void Textblock::addSpace (core::style::Style *style) */ void Textblock::addParbreak (int space, core::style::Style *style) { - Word *word, *word2 = NULL; // Latter for compiler happiness, search! - bool isfirst; - Widget *widget; - int lineno; + Word *word; /* 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.) */ if (words->size () == 0 || - (listItem && words->size () == 1)) { + (hasListitemValue && words->size () == 1)) { /* This is a bit hackish: If a break is added as the first/second word of a page, and the parent widget is also a - DwPage, and there is a break before -- this is the case when + Textblock, and there is a break before -- this is the case when 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 break_space. */ + /* Find the widget where to adjust the breakSpace. */ for (widget = this; widget->getParent() && widget->getParent()->instanceOf (Textblock::CLASS_ID); widget = widget->getParent ()) { Textblock *textblock2 = (Textblock*)widget->getParent (); - if (textblock2->listItem) - isfirst = (textblock2->words->get(1).content.type - == core::Content::WIDGET - && textblock2->words->get(1).content.widget == widget); - else - isfirst = (textblock2->words->get(0).content.type - == core::Content::WIDGET - && textblock2->words->get(0).content.widget == widget); + int index = textblock2->hasListitemValue ? 1 : 0; + bool isfirst = (textblock2->words->getRef(index)->content.type + == core::Content::WIDGET + && textblock2->words->getRef(index)->content.widget + == widget); if (!isfirst) { /* The page we searched for has been found. */ + Word *word2; // ABC - lineno = widget->parentRef >> 3; + int lineno = widget->parentRef >> 3; if (lineno > 0 && (word2 = textblock2->words->getRef(textblock2->lines - ->get(lineno - 1).firstWord)) && + ->getRef(lineno - 1)->firstWord)) && word2->content.type == core::Content::BREAK) { if (word2->content.breakSpace < space) { word2->content.breakSpace = space; @@ -1897,7 +1920,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) misc::max (word->content.breakSpace, space); lastLine->breakSpace = misc::max (word->content.breakSpace, - lastLine->marginDescent - lastLine->descent, + lastLine->marginDescent - lastLine->boxDescent, lastLine->breakSpace); return; } @@ -1916,7 +1939,7 @@ void Textblock::addLinebreak (core::style::Style *style) Word *word; if (words->size () == 0 || - words->get(words->size () - 1).content.type == core::Content::BREAK) + words->getRef(words->size () - 1)->content.type == core::Content::BREAK) // An <BR> in an empty line gets the height of the current font // (why would someone else place it here?), ... word = addWord (0, style->font->ascent, style->font->descent, style); @@ -1926,7 +1949,6 @@ void Textblock::addLinebreak (core::style::Style *style) word->content.type = core::Content::BREAK; word->content.breakSpace = 0; - word->style = style; wordWrap (words->size () - 1); } @@ -1949,9 +1971,9 @@ void Textblock::addFloatIntoGenerator (core::Widget *widget, core::style::Style /** * \brief Search recursively through widget. - * + * * This is an optimized version of the general - * dw::core::Widget::getWidgetAtPoint method. + * dw::core::Widget::getWidgetAtPoint method. */ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) { @@ -1973,7 +1995,7 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) line = lines->getRef (lineIndex); - for (wordIndex = line->firstWord; wordIndex < line->lastWord; wordIndex++) { + for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { Word *word = words->getRef (wordIndex); if (word->content.type == core::Content::WIDGET) { @@ -1996,19 +2018,16 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) */ void Textblock::handOverBreak (core::style::Style *style) { - #if 0 - MISSING - DwPageLine *last_line; - DwWidget *parent; - - if (page->num_lines == 0) - return; + if (lines->size() > 0) { + Widget *parent; + Line *lastLine = lines->getRef (lines->size () - 1); - last_line = &page->lines[page->num_lines - 1]; - if (last_line->break_space != 0 && - (parent = DW_WIDGET(page)->parent) && DW_IS_PAGE (parent)) - a_Dw_page_add_parbreak (DW_PAGE (parent), last_line->break_space, style); -#endif + if (lastLine->breakSpace != 0 && (parent = getParent()) && + parent->instanceOf (Textblock::CLASS_ID)) { + Textblock *textblock2 = (Textblock*) parent; + textblock2->addParbreak(lastLine->breakSpace, style); + } + } } /* @@ -2039,10 +2058,10 @@ void Textblock::changeLinkColor (int link, int newColor) for (int lineIndex = 0; lineIndex < lines->size(); lineIndex++) { bool changed = false; Line *line = lines->getRef (lineIndex); - int wordIndex; + int wordIdx; - for (wordIndex = line->firstWord;wordIndex < line->lastWord;wordIndex++){ - Word *word = words->getRef(wordIndex); + for (wordIdx = line->firstWord; wordIdx <= line->lastWord; wordIdx++){ + Word *word = words->getRef(wordIdx); if (word->style->x_link == link) { core::style::StyleAttrs styleAttrs; @@ -2051,14 +2070,14 @@ void Textblock::changeLinkColor (int link, int newColor) case core::Content::TEXT: { core::style::Style *old_style = word->style; styleAttrs = *old_style; - styleAttrs.color = core::style::Color::createSimple (layout, - newColor); + styleAttrs.color = core::style::Color::create (layout, + newColor); word->style = core::style::Style::create (layout, &styleAttrs); old_style->unref(); old_style = word->spaceStyle; styleAttrs = *old_style; - styleAttrs.color = core::style::Color::createSimple (layout, - newColor); + styleAttrs.color = core::style::Color::create (layout, + newColor); word->spaceStyle = core::style::Style::create(layout, &styleAttrs); old_style->unref(); @@ -2067,10 +2086,10 @@ void Textblock::changeLinkColor (int link, int newColor) case core::Content::WIDGET: { core::Widget *widget = word->content.widget; styleAttrs = *widget->getStyle(); - styleAttrs.color = core::style::Color::createSimple (layout, - newColor); + styleAttrs.color = core::style::Color::create (layout, + newColor); styleAttrs.setBorderColor( - core::style::Color::createShaded(layout, newColor)); + core::style::Color::create (layout, newColor)); widget->setStyle( core::style::Style::create (layout, &styleAttrs)); break; @@ -2083,7 +2102,7 @@ void Textblock::changeLinkColor (int link, int newColor) } if (changed) queueDrawArea (0, lineYOffsetWidget(line), allocation.width, - line->ascent + line->descent); + line->boxAscent + line->boxDescent); } } @@ -2325,7 +2344,7 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, else if (index >= textblock->words->size ()) content.type = core::Content::END; else - content = textblock->words->get(index).content; + content = textblock->words->getRef(index)->content; } object::Object *Textblock::TextblockIterator::clone() @@ -2337,23 +2356,23 @@ int Textblock::TextblockIterator::compareTo(misc::Comparable *other) { return index - ((TextblockIterator*)other)->index; } - + bool Textblock::TextblockIterator::next () { Textblock *textblock = (Textblock*)getWidget(); if (content.type == core::Content::END) return false; - + do { index++; if (index >= textblock->words->size ()) { content.type = core::Content::END; return false; } - } while ((textblock->words->get(index).content.type & getMask()) == 0); + } while ((textblock->words->getRef(index)->content.type & getMask()) == 0); - content = textblock->words->get(index).content; + content = textblock->words->getRef(index)->content; return true; } @@ -2363,16 +2382,16 @@ bool Textblock::TextblockIterator::prev () if (content.type == core::Content::START) return false; - + do { index--; if (index < 0) { content.type = core::Content::START; return false; } - } while ((textblock->words->get(index).content.type & getMask()) == 0); + } while ((textblock->words->getRef(index)->content.type & getMask()) == 0); - content = textblock->words->get(index).content; + content = textblock->words->getRef(index)->content; return true; } @@ -2440,16 +2459,19 @@ void Textblock::queueDrawRange (int index1, int index2) to = misc::min (to, words->size () - 1); to = misc::max (to, 0); - int line1 = findLineOfWord (from); - int line2 = findLineOfWord (to); + int line1idx = findLineOfWord (from); + int line2idx = findLineOfWord (to); + + if (line1idx >= 0 && line2idx >= 0) { + Line *line1 = lines->getRef (line1idx), + *line2 = lines->getRef (line2idx); + int y = lineYOffsetWidget (line1) + line1->boxAscent - + line1->contentAscent; + int h = lineYOffsetWidget (line2) + line2->boxAscent + + line2->contentDescent - y; - queueDrawArea (0, - lineYOffsetWidgetI (line1), - allocation.width, - lineYOffsetWidgetI (line2) - - lineYOffsetWidgetI (line1) - + lines->getRef (line2)->ascent - + lines->getRef (line2)->descent); + queueDrawArea (0, y, allocation.width, h); + } } void Textblock::TextblockIterator::getAllocation (int start, int end, @@ -2462,13 +2484,31 @@ void Textblock::TextblockIterator::getAllocation (int start, int end, allocation->x = textblock->allocation.x + textblock->lineXOffsetWidget (line); - for (int i = line->firstWord; i < index; i++) - allocation->x += textblock->words->getRef(i)->size.width; - allocation->y = - textblock->allocation.y - + textblock->lineYOffsetWidget (line) + line->ascent - word->size.ascent; + 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->layout->textWidth (word->style->font, + word->content.text, + start); + } + 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->layout->textWidth (word->style->font, + word->content.text + start, + end - start); + } + } allocation->ascent = word->size.ascent; allocation->descent = word->size.descent; } |