aboutsummaryrefslogtreecommitdiff
path: root/dw
diff options
context:
space:
mode:
Diffstat (limited to 'dw')
-rw-r--r--dw/Makefile.am1
-rw-r--r--dw/fltkviewbase.cc6
-rw-r--r--dw/tablecell.cc4
-rw-r--r--dw/tablecell.hh2
-rw-r--r--dw/textblock.cc826
-rw-r--r--dw/textblock.hh142
-rw-r--r--dw/textblock_linebreaking.cc688
-rw-r--r--dw/types.hh6
8 files changed, 1091 insertions, 584 deletions
diff --git a/dw/Makefile.am b/dw/Makefile.am
index 3014b35d..4cd53099 100644
--- a/dw/Makefile.am
+++ b/dw/Makefile.am
@@ -68,6 +68,7 @@ libDw_widgets_a_SOURCES = \
tablecell.cc \
tablecell.hh \
textblock.cc \
+ textblock_linebreaking.cc \
textblock.hh
EXTRA_DIST = preview.xbm
diff --git a/dw/fltkviewbase.cc b/dw/fltkviewbase.cc
index 1d385cf9..240937e2 100644
--- a/dw/fltkviewbase.cc
+++ b/dw/fltkviewbase.cc
@@ -531,9 +531,9 @@ FltkWidgetView::~FltkWidgetView ()
}
void FltkWidgetView::drawText (core::style::Font *font,
- core::style::Color *color,
- core::style::Color::Shading shading,
- int X, int Y, const char *text, int len)
+ core::style::Color *color,
+ core::style::Color::Shading shading,
+ int X, int Y, const char *text, int len)
{
FltkFont *ff = (FltkFont*)font;
fl_font(ff->font, ff->size);
diff --git a/dw/tablecell.cc b/dw/tablecell.cc
index 5b93fe86..90dc310d 100644
--- a/dw/tablecell.cc
+++ b/dw/tablecell.cc
@@ -42,12 +42,12 @@ TableCell::~TableCell()
{
}
-void TableCell::wordWrap(int wordIndex)
+void TableCell::wordWrap(int wordIndex, bool wrapAll)
{
Textblock::Word *word;
const char *p;
- Textblock::wordWrap (wordIndex);
+ Textblock::wordWrap (wordIndex, wrapAll);
if (charWordIndex == -1) {
word = words->getRef (wordIndex);
diff --git a/dw/tablecell.hh b/dw/tablecell.hh
index 318d1f4e..4bb8633c 100644
--- a/dw/tablecell.hh
+++ b/dw/tablecell.hh
@@ -12,7 +12,7 @@ private:
int charWordIndex, charWordPos;
protected:
- void wordWrap(int wordIndex);
+ void wordWrap (int wordIndex, bool wrapAll);
int getValue ();
void setMaxValue (int maxValue, int value);
diff --git a/dw/textblock.cc b/dw/textblock.cc
index 3778687c..df0b926e 100644
--- a/dw/textblock.cc
+++ b/dw/textblock.cc
@@ -18,13 +18,12 @@
*/
-
#include "textblock.hh"
#include "../lout/msg.h"
#include "../lout/misc.hh"
#include <stdio.h>
-#include <limits.h>
+#include <math.h>
/*
* Local variables
@@ -51,8 +50,6 @@ Textblock::Textblock (bool limitTextWidth)
hasListitemValue = false;
innerPadding = 0;
line1Offset = 0;
- line1OffsetEff = 0;
- ignoreLine1OffsetSometimes = false;
mustQueueResize = false;
redrawY = 0;
lastWordDrawn = -1;
@@ -67,13 +64,12 @@ Textblock::Textblock (bool limitTextWidth)
* TODO: Some tests would be useful.
*/
lines = new misc::SimpleVector <Line> (1);
+ nonTemporaryLines = 0;
words = new misc::SimpleVector <Word> (1);
anchors = new misc::SimpleVector <Anchor> (1);
//DBG_OBJ_SET_NUM(this, "num_lines", num_lines);
- lastLineWidth = 0;
- lastLineParMax = 0;
wrapRef = -1;
//DBG_OBJ_SET_NUM(this, "last_line_width", last_line_width);
@@ -112,6 +108,7 @@ Textblock::~Textblock ()
delete word->content.widget;
word->style->unref ();
word->spaceStyle->unref ();
+ word->hyphenStyle->unref ();
}
for (int i = 0; i < anchors->size(); i++) {
@@ -139,11 +136,21 @@ Textblock::~Textblock ()
void Textblock::sizeRequestImpl (core::Requisition *requisition)
{
rewrap ();
+ showMissingLines ();
if (lines->size () > 0) {
Line *lastLine = lines->getRef (lines->size () - 1);
- requisition->width =
- misc::max (lastLine->maxLineWidth, lastLineWidth);
+ requisition->width = 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);
+
/* 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)->boxAscent;
@@ -151,7 +158,7 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
+ lastLine->boxAscent + lastLine->boxDescent -
lines->getRef(0)->boxAscent;
} else {
- requisition->width = lastLineWidth;
+ requisition->width = 0; // before: lastLineWidth;
requisition->ascent = 0;
requisition->descent = 0;
}
@@ -162,6 +169,9 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
if (requisition->width < availWidth)
requisition->width = availWidth;
+
+ PRINTF("[%p] SIZE_REQUEST: %d x %d + %d\n", this, requisition->width,
+ requisition->ascent, requisition->descent);
}
/**
@@ -205,23 +215,27 @@ void Textblock::getExtremesImpl (core::Extremes *extremes)
int wordIndex, lineIndex;
int parMax;
- //DBG_MSG (widget, "extremes", 0, "getExtremesImpl");
- //DBG_MSG_START (widget);
+ showMissingLines ();
if (lines->size () == 0) {
/* empty page */
extremes->minWidth = 0;
extremes->maxWidth = 0;
+
+ PRINTF ("GET_EXTREMES: empty (but %d words)\n", words->size());
} else if (wrapRef == -1) {
/* no rewrap necessary -> values in lines are up to date */
line = lines->getRef (lines->size () - 1);
extremes->minWidth = line->maxParMin;
- extremes->maxWidth = misc::max (line->maxParMax, lastLineParMax);
- //DBG_MSG (widget, "extremes", 0, "simple case");
+ extremes->maxWidth = line->maxParMax;
+
+ PRINTF ("GET_EXTREMES: no rewrap => %d, %d\n",
+ line->maxParMin, line->maxParMax);
} else {
/* Calculate the extremes, based on the values in the line from
where a rewrap is necessary. */
- //DBG_MSG (widget, "extremes", 0, "complex case");
+
+ PRINTF ("GET_EXTREMES: complex case ...\n");
if (wrapRef == 0) {
extremes->minWidth = 0;
@@ -232,97 +246,65 @@ void Textblock::getExtremesImpl (core::Extremes *extremes)
extremes->minWidth = line->maxParMin;
extremes->maxWidth = line->maxParMax;
parMax = line->parMax;
-
- //DBG_MSGF (widget, "extremes", 0, "parMin = %d", parMin);
}
- //_MSG ("*** parMin = %d\n", parMin);
-
int prevWordSpace = 0;
for (lineIndex = wrapRef; lineIndex < lines->size (); lineIndex++) {
- //DBG_MSGF (widget, "extremes", 0, "line %d", lineIndex);
- //DBG_MSG_START (widget);
- int parMin = 0;
-
line = lines->getRef (lineIndex);
+ int parMin = 0;
for (wordIndex = line->firstWord; wordIndex <= line->lastWord;
wordIndex++) {
word = words->getRef (wordIndex);
- getWordExtremes (word, &wordExtremes);
+ getWordExtremes (word, &wordExtremes);
if (wordIndex == 0) {
- wordExtremes.minWidth += line1OffsetEff;
- wordExtremes.maxWidth += line1OffsetEff;
- //DEBUG_MSG (DEBUG_SIZE_LEVEL + 1,
- // " (next plus %d)\n", line1OffsetEff);
+ wordExtremes.minWidth += line1Offset;
+ wordExtremes.maxWidth += line1Offset;
}
+ extremes->minWidth = misc::max (extremes->minWidth,
+ wordExtremes.minWidth);
- if (extremes->minWidth < wordExtremes.minWidth)
- extremes->minWidth = wordExtremes.minWidth;
-
- _MSG("parMax = %d, wordMaxWidth=%d, prevWordSpace=%d\n",
- parMax, wordExtremes.maxWidth, prevWordSpace);
if (word->content.type != core::Content::BREAK)
parMax += prevWordSpace;
parMax += wordExtremes.maxWidth;
- if (prevWord && !canBreakAfter(prevWord)) {
+ if (prevWord && !prevWord->badnessAndPenalty.lineMustBeBroken ())
parMin += prevWordSpace + wordExtremes.minWidth;
- } else {
+ else
parMin = wordExtremes.minWidth;
- }
- if (extremes->minWidth < parMin) {
+ if (extremes->minWidth < parMin)
extremes->minWidth = parMin;
- }
prevWordSpace = word->origSpace;
prevWord = word;
-
- //DEBUG_MSG (DEBUG_SIZE_LEVEL + 1,
- // " word %s: maxWidth = %d\n",
- // word->content.text,
- // word_extremes.maxWidth);
}
if ((words->getRef(line->lastWord)->content.type
== core::Content::BREAK ) ||
lineIndex == lines->size () - 1 ) {
-
- //DEBUG_MSG (DEBUG_SIZE_LEVEL + 2,
- // " parMax = %d, after word %d (%s)\n",
- // parMax, line->last_word - 1,
- // word->content.text);
-
- if (extremes->maxWidth < parMax)
- extremes->maxWidth = parMax;
-
+ extremes->maxWidth = misc::max (extremes->maxWidth, parMax);
prevWordSpace = 0;
parMax = 0;
}
-
- //DBG_MSG_END (widget);
}
- //DEBUG_MSG (DEBUG_SIZE_LEVEL + 3, " Result: %d, %d\n",
- // extremes->minWidth, extremes->maxWidth);
}
- //DBG_MSGF (widget, "extremes", 0, "width difference: %d + %d",
- // innerPadding, getStyle()->boxDiffWidth ());
-
int diff = innerPadding + getStyle()->boxDiffWidth ();
extremes->minWidth += diff;
extremes->maxWidth += diff;
-
- //DBG_MSG_END (widget);
}
void Textblock::sizeAllocateImpl (core::Allocation *allocation)
{
+ PRINTF ("SIZE_ALLOCATE: %d, %d, %d x %d + %d\n",
+ allocation->x, allocation->y, allocation->width,
+ allocation->ascent, allocation->descent);
+
int lineIndex, wordIndex;
Line *line;
Word *word;
@@ -461,16 +443,19 @@ void Textblock::markExtremesChange (int ref)
*/
void Textblock::markChange (int ref)
{
+ /* 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 neccessary. */
if (ref != -1) {
- //DBG_MSGF (page, "wrap", 0, "markChange (ref = %d)", ref);
-
if (wrapRef == -1)
wrapRef = ref;
else
wrapRef = misc::min (wrapRef, ref);
-
- //DBG_OBJ_SET_NUM (this, "wrap_ref", wrapRef);
}
+
+ PRINTF ("[%p] MARK_CHANGE (%d) => %d\n", this, ref, wrapRef);
}
void Textblock::setWidth (int width)
@@ -731,361 +716,6 @@ core::Iterator *Textblock::iterator (core::Content::Type mask, bool atEnd)
return new TextblockIterator (this, mask, atEnd);
}
-/*
- * ...
- *
- * availWidth is passed from wordWrap, to avoid calculating it twice.
- */
-void Textblock::justifyLine (Line *line, int availWidth)
-{
- /* To avoid rounding errors, the calculation is based on accumulated
- * values (*_cum). */
- int i;
- int origSpaceSum, origSpaceCum;
- int effSpaceDiffCum, lastEffSpaceDiffCum;
- int diff;
-
- diff = availWidth - lastLineWidth;
- if (diff > 0) {
- origSpaceSum = 0;
- for (i = line->firstWord; i < line->lastWord; i++)
- origSpaceSum += words->getRef(i)->origSpace;
-
- origSpaceCum = 0;
- lastEffSpaceDiffCum = 0;
- for (i = line->firstWord; i < line->lastWord; i++) {
- origSpaceCum += words->getRef(i)->origSpace;
-
- if (origSpaceCum == 0)
- effSpaceDiffCum = lastEffSpaceDiffCum;
- else
- effSpaceDiffCum = diff * origSpaceCum / origSpaceSum;
-
- words->getRef(i)->effSpace = words->getRef(i)->origSpace +
- (effSpaceDiffCum - lastEffSpaceDiffCum);
- //DBG_OBJ_ARRSET_NUM (this, "words.%d.effSpace", i,
- // words->getRef(i)->effSpace);
-
- lastEffSpaceDiffCum = effSpaceDiffCum;
- }
- }
-}
-
-
-Textblock::Line *Textblock::addLine (int wordIndex, bool newPar)
-{
- Line *lastLine;
-
- //DBG_MSG (page, "wrap", 0, "addLine");
- //DBG_MSG_START (page);
-
- lines->increase ();
- //DBG_OBJ_SET_NUM(this, "num_lines", lines->size ());
-
- //DEBUG_MSG (DEBUG_REWRAP_LEVEL, "--- new line %d in %p, with word %d of %d"
- // "\n", lines->size () - 1, page, word_ind, words->size());
-
- lastLine = lines->getRef (lines->size () - 1);
-
- if (lines->size () == 1) {
- lastLine->top = 0;
- lastLine->maxLineWidth = line1OffsetEff;
- lastLine->maxParMin = 0;
- lastLine->maxParMax = 0;
- } else {
- Line *prevLine = lines->getRef (lines->size () - 2);
-
- lastLine->top = prevLine->top + prevLine->boxAscent +
- prevLine->boxDescent + prevLine->breakSpace;
- lastLine->maxLineWidth = prevLine->maxLineWidth;
- lastLine->maxParMin = prevLine->maxParMin;
- lastLine->maxParMax = prevLine->maxParMax;
- }
-
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.top", lines->size () - 1,
- // lastLine->top);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.maxLineWidth", lines->size () - 1,
- // lastLine->maxLineWidth);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.maxParMin", lines->size () - 1,
- // lastLine->maxParMin);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.maxParMax", lines->size () - 1,
- // lastLine->maxParMax);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.parMin", lines->size () - 1,
- // lastLine->parMin);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.parMax", lines->size () - 1,
- // lastLine->parMax);
-
- lastLine->firstWord = wordIndex;
- lastLine->boxAscent = lastLine->contentAscent = 0;
- lastLine->boxDescent = lastLine->contentDescent = 0;
- lastLine->marginDescent = 0;
- lastLine->breakSpace = 0;
- lastLine->leftOffset = 0;
-
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.ascent", lines->size () - 1,
- // lastLine->boxAscent);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.descent", lines->size () - 1,
- // lastLine->boxDescent);
-
- /* update values in line */
- lastLine->maxLineWidth = misc::max (lastLine->maxLineWidth, lastLineWidth);
-
- if (lines->size () > 1)
- lastLineWidth = 0;
- else
- lastLineWidth = line1OffsetEff;
-
- if (newPar) {
- lastLine->maxParMax = misc::max (lastLine->maxParMax, lastLineParMax);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.maxParMax", lines->size () - 1,
- // lastLine->maxParMax);
-
- if (lines->size () > 1) {
- lastLineParMax = 0;
- } else {
- lastLineParMax = line1OffsetEff;
- }
-
- //DBG_OBJ_SET_NUM(this, "lastLineParMax", lastLineParMax);
- }
-
- lastLine->parMax = lastLineParMax;
-
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.parMin", lines->size () - 1,
- // lastLine->parMin);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.parMax", lines->size () - 1,
- // lastLine->parMax);
-
- //DBG_MSG_END (page);
- return lastLine;
-}
-
-/*
- * 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;
- int availWidth, lastSpace, leftOffset, len;
- bool newLine = false, newPar = false, canBreakBefore = true;
- core::Extremes wordExtremes;
-
- //DBG_MSGF (page, "wrap", 0, "wordWrap (%d): %s, width = %d",
- // wordIndex, words->getRef(wordIndex)->content.text),
- // words->getRef(wordIndex)->size.width);
- //DBG_MSG_START (page);
-
- availWidth = this->availWidth - getStyle()->boxDiffWidth() - innerPadding;
- if (limitTextWidth &&
- layout->getUsesViewport () &&
- availWidth > layout->getWidthViewport () - 10)
- 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 {
- int indent = 0;
-
- if (word->content.type == core::Content::WIDGET &&
- word->content.widget->blockLevel() == true) {
- /* don't use text-indent when nesting blocks */
- } else {
- if (core::style::isPerLength(getStyle()->textIndent)) {
- indent = misc::roundInt(this->availWidth *
- core::style::perLengthVal (getStyle()->textIndent));
- } else {
- indent = core::style::absLengthVal (getStyle()->textIndent);
- }
- }
- line1OffsetEff = line1Offset + indent;
- }
- }
-
- if (lines->size () == 0) {
- //DBG_MSG (page, "wrap", 0, "first line");
- newLine = true;
- newPar = true;
- lastLine = NULL;
- } else {
- Word *prevWord = words->getRef (wordIndex - 1);
-
- lastLine = lines->getRef (lines->size () - 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 (!canBreakAfter (prevWord)) {
- canBreakBefore = false;
- // no break within nowrap
- newLine = false;
- newPar = false;
- if (lastLineWidth + prevWord->origSpace + word->size.width >
- availWidth)
- markChange (lines->size () - 1);
- } else if (lastLine->firstWord != wordIndex) {
- // check if we need to break because nowrap sequence is following
- newLine = false;
- int lineWidthNeeded = lastLineWidth + prevWord->origSpace;
- for (int i = wordIndex; i < words->size (); i++) {
- Word *w = words->getRef (i);
-
- if (w->content.type == core::Content::BREAK ||
- (word->content.type == core::Content::WIDGET &&
- word->content.widget->blockLevel()))
- break;
-
- lineWidthNeeded += w->size.width;
-
- if (lineWidthNeeded > availWidth) {
- newLine = true;
- break;
- } else if (canBreakAfter (w)) {
- break;
- }
-
- lineWidthNeeded += w->origSpace;
- }
- }
- }
-
- if (newLine) {
- if (word->style->textAlign == core::style::TEXT_ALIGN_JUSTIFY &&
- lastLine != NULL && !newPar) {
- justifyLine (lastLine, availWidth);
- }
- lastLine = addLine (wordIndex, newPar);
- }
-
- lastLine->lastWord = wordIndex;
- lastLine->boxAscent = misc::max (lastLine->boxAscent, word->size.ascent);
- lastLine->boxDescent = misc::max (lastLine->boxDescent, word->size.descent);
-
- len = word->style->font->ascent;
- if (word->style->valign == core::style::VALIGN_SUPER)
- len += len / 2;
- lastLine->contentAscent = misc::max (lastLine->contentAscent, len);
-
- 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 (this, "lines.%d.ascent", lines->size () - 1,
- // lastLine->boxAscent);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.descent", lines->size () - 1,
- // lastLine->boxDescent);
-
- if (word->content.type == core::Content::WIDGET) {
- int collapseMarginTop = 0;
-
- lastLine->marginDescent =
- misc::max (lastLine->marginDescent,
- word->size.descent +
- word->content.widget->getStyle()->margin.bottom);
-
- if (lines->size () == 1 &&
- word->content.widget->blockLevel () &&
- getStyle ()->borderWidth.top == 0 &&
- getStyle ()->padding.top == 0) {
- // collapse top margins of parent element and its first child
- // see: http://www.w3.org/TR/CSS21/box.html#collapsing-margins
- collapseMarginTop = getStyle ()->margin.top;
- }
-
- lastLine->boxAscent =
- misc::max (lastLine->boxAscent,
- word->size.ascent,
- word->size.ascent
- + word->content.widget->getStyle()->margin.top
- - collapseMarginTop);
-
- } else {
- lastLine->marginDescent =
- misc::max (lastLine->marginDescent, lastLine->boxDescent);
-
- if (word->content.type == core::Content::BREAK)
- lastLine->breakSpace =
- misc::max (word->content.breakSpace,
- lastLine->marginDescent - lastLine->boxDescent,
- lastLine->breakSpace);
- }
-
- lastSpace = (wordIndex > 0) ? words->getRef(wordIndex - 1)->origSpace : 0;
-
- if (!newLine)
- lastLineWidth += lastSpace;
- if (!newPar) {
- lastLineParMax += lastSpace;
- }
-
- lastLineWidth += word->size.width;
-
- getWordExtremes (word, &wordExtremes);
- lastLineParMax += wordExtremes.maxWidth;
-
- if (!canBreakBefore) {
- lastLineParMin += wordExtremes.minWidth + lastSpace;
- /* This may also increase the accumulated minimum word width. */
- lastLine->maxParMin = misc::max (lastLine->maxParMin, lastLineParMin);
- } else {
- lastLineParMin = wordExtremes.minWidth;
- lastLine->maxParMin =
- misc::max (lastLine->maxParMin, wordExtremes.minWidth);
- }
-
- //DBG_OBJ_SET_NUM(this, "lastLine_par_min", lastLineParMin);
- //DBG_OBJ_SET_NUM(this, "lastLine_par_max", lastLineParMax);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.par_min", lines->size () - 1,
- // lastLine->par_min);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.par_max", lines->size () - 1,
- // lastLine->par_max);
- //DBG_OBJ_ARRSET_NUM (this, "lines.%d.max_word_min", lines->size () - 1,
- // lastLine->max_word_min);
-
- /* 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:
- case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */
- case core::style::TEXT_ALIGN_STRING: /* handled elsewhere (in the
- * 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;
- }
-
- /* For large lines (images etc), which do not fit into the viewport: */
- if (leftOffset < 0)
- leftOffset = 0;
-
- lastLine->leftOffset = leftOffset;
- }
- mustQueueResize = true;
-
- //DBG_MSG_END (page);
-}
-
/**
* Calculate the size of a widget within the page.
@@ -1144,93 +774,6 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size)
size->descent -= wstyle->margin.bottom;
}
-/**
- * Rewrap the page from the line from which this is necessary.
- * There are basically two times we'll want to do this:
- * either when the viewport is resized, or when the size changes on one
- * of the child widgets.
- */
-void Textblock::rewrap ()
-{
- int i, wordIndex;
- Word *word;
- Line *lastLine;
-
- if (wrapRef == -1)
- /* page does not have to be rewrapped */
- return;
-
- //DBG_MSGF (page, "wrap", 0,
- // "rewrap: wrapRef = %d, in page with %d word(s)",
- // wrapRef, words->size());
- //DBG_MSG_START (page);
-
- /* All lines up from wrapRef will be rebuild from the word list,
- * the line list up from this position is rebuild. */
- lines->setSize (wrapRef);
- lastLineWidth = 0;
- lastLineParMin = 0;
- //DBG_OBJ_SET_NUM(this, "num_lines", lines->size ());
- //DBG_OBJ_SET_NUM(this, "lastLine_width", lastLineWidth);
-
- /* In the word list, start at the last word plus one in the line before. */
- if (wrapRef > 0) {
- /* Note: In this case, wordWrap() will immediately find the need
- * to rewrap the line, since we start with the last one (plus one).
- * This is also the reason, why lastLineWidth is set
- * to the length of the line. */
- lastLine = lines->getRef (lines->size () - 1);
-
- lastLineParMax = lastLine->parMax;
-
- // take line1OffsetEff into account, if lastLine is the first line
- if (lines->size () - 1 == 0)
- lastLineWidth = line1OffsetEff;
-
- 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)->size.width;
- } else {
- lastLineParMax = 0;
-
- wordIndex = 0;
- }
-
- for (; wordIndex < words->size (); wordIndex++) {
- word = words->getRef (wordIndex);
-
- if (word->content.type == core::Content::WIDGET)
- calcWidgetSize (word->content.widget, &word->size);
- wordWrap (wordIndex);
-
- if (word->content.type == core::Content::WIDGET) {
- word->content.widget->parentRef = lines->size () - 1;
- //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
- // word->content.widget->parent_ref);
- }
-
- //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
- // "Assigning parent_ref = %d to rewrapped word %d, "
- // "in page with %d word(s)\n",
- // lines->size () - 1, wordIndex, words->size());
-
- /* todo_refactoring:
- if (word->content.type == DW_CONTENT_ANCHOR)
- p_Dw_gtk_viewport_change_anchor
- (widget, word->content.anchor,
- Dw_page_line_total_y_offset (page,
- &page->lines[lines->size () - 1]));
- */
- }
-
- /* Next time, the page will not have to be rewrapped. */
- wrapRef = -1;
-
- //DBG_MSG_END (page);
-}
-
/*
* Draw the decorations on a word.
*/
@@ -1314,14 +857,58 @@ void Textblock::drawText(core::View *view, core::style::Style *style,
}
/*
- * Draw a word of text.
+ * Draw a word of text. TODO New description;
*/
-void Textblock::drawWord(int wordIndex, core::View *view,core::Rectangle *area,
- int xWidget, int yWidgetBase)
+void Textblock::drawWord (Line *line, int wordIndex1, int wordIndex2,
+ core::View *view, core::Rectangle *area,
+ int xWidget, int yWidgetBase)
{
- Word *word = words->getRef(wordIndex);
+ core::style::Style *style = words->getRef(wordIndex1)->style;
+ bool drawHyphen = wordIndex2 == line->lastWord
+ && words->getRef(wordIndex2)->hyphenWidth > 0;
+
+ if (wordIndex1 == wordIndex2 && !drawHyphen) {
+ // Simple case, where copying in one buffer is not needed.
+ Word *word = words->getRef (wordIndex1);
+ drawWord0 (wordIndex1, wordIndex2, word->content.text, word->size.width,
+ style, view, area, xWidget, yWidgetBase);
+ } else {
+ // Concaternate all words in a new buffer.
+ int l = 0, totalWidth = 0;
+ for (int i = wordIndex1; i <= wordIndex2; i++) {
+ Word *w = words->getRef (i);
+ l += strlen (w->content.text);
+ totalWidth += w->size.width;
+ }
+
+ char text[l + (drawHyphen ? 2 : 0) + 1];
+ int p = 0;
+ for (int i = wordIndex1; i <= wordIndex2; i++) {
+ const char * t = words->getRef(i)->content.text;
+ strcpy (text + p, t);
+ p += strlen (t);
+ }
+
+ if(drawHyphen) {
+ text[p++] = 0xc2;
+ text[p++] = 0xad;
+ text[p++] = 0;
+ }
+
+ drawWord0 (wordIndex1, wordIndex2, text, totalWidth,
+ style, view, area, xWidget, yWidgetBase);
+ }
+}
+
+/**
+ * TODO Comment
+ */
+void Textblock::drawWord0 (int wordIndex1, int wordIndex2,
+ const char *text, int totalWidth,
+ core::style::Style *style, core::View *view,
+ core::Rectangle *area, int xWidget, int yWidgetBase)
+{
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. */
@@ -1333,33 +920,33 @@ void Textblock::drawWord(int wordIndex, core::View *view,core::Rectangle *area,
yWorldBase = yWidgetBase + allocation.y;
drawText (view, style, core::style::Color::SHADING_NORMAL, xWorld,
- yWorldBase, word->content.text, 0, strlen (word->content.text));
+ yWorldBase, text, 0, strlen (text));
if (style->textDecoration)
decorateText(view, style, core::style::Color::SHADING_NORMAL, xWorld,
- yWorldBase, word->size.width);
+ yWorldBase, totalWidth);
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);
+ if (hlStart[layer].index <= wordIndex1 &&
+ hlEnd[layer].index >= wordIndex2) {
+ const int wordLen = strlen (text);
int xStart, width;
int firstCharIdx = 0;
int lastCharIdx = wordLen;
- if (wordIndex == hlStart[layer].index)
+ if (wordIndex1 == hlStart[layer].index)
firstCharIdx = misc::min (hlStart[layer].nChar, wordLen);
- if (wordIndex == hlEnd[layer].index)
+ if (wordIndex2 == hlEnd[layer].index)
lastCharIdx = misc::min (hlEnd[layer].nChar, wordLen);
xStart = xWorld;
if (firstCharIdx)
- xStart += textWidth (word->content.text, 0, firstCharIdx, style);
+ xStart += textWidth (text, 0, firstCharIdx, style);
if (firstCharIdx == 0 && lastCharIdx == wordLen)
- width = word->size.width;
+ width = totalWidth;
else
- width = textWidth (word->content.text, firstCharIdx,
+ width = textWidth (text, firstCharIdx,
lastCharIdx - firstCharIdx, style);
if (width > 0) {
/* Highlight text */
@@ -1376,7 +963,7 @@ void Textblock::drawWord(int wordIndex, core::View *view,core::Rectangle *area,
/* Highlight the text. */
drawText (view, style, core::style::Color::SHADING_INVERSE, xStart,
- yWorldBase, word->content.text, firstCharIdx,
+ yWorldBase, text, firstCharIdx,
lastCharIdx - firstCharIdx);
if (style->textDecoration)
@@ -1445,18 +1032,12 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
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 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. */
-
for (int wordIndex = line->firstWord;
- wordIndex <= line->lastWord && xWidget < area->x + area->width;
+ wordIndex <= line->lastWord
+ /* TODO && xWidget < area->x + area->width*/;
wordIndex++) {
Word *word = words->getRef(wordIndex);
+ int wordSize = word->size.width;
if (xWidget + word->size.width + word->effSpace >= area->x) {
if (word->content.type == core::Content::TEXT ||
@@ -1470,12 +1051,26 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
if (child->intersects (area, &childArea))
child->draw (view, &childArea);
} else {
+ /* TODO: include in drawWord:
if (word->style->hasBackground ()) {
drawBox (view, word->style, area, xWidget,
yWidgetBase - line->boxAscent, word->size.width,
line->boxAscent + line->boxDescent, false);
- }
- drawWord(wordIndex, view, area, xWidget, yWidgetBase);
+ }*/
+ int wordIndex2 = wordIndex;
+ while (wordIndex2 < line->lastWord &&
+ words->getRef(wordIndex2)->hyphenWidth > 0 &&
+ word->style == words->getRef(wordIndex2 + 1)->style)
+ wordIndex2++;
+
+ drawWord(line, wordIndex, wordIndex2, view, area,
+ xWidget, yWidgetBase);
+ wordSize = 0;
+ for (int i = wordIndex; i <= wordIndex2; i++)
+ wordSize += words->getRef(i)->size.width;
+
+ wordIndex = wordIndex2;
+ word = words->getRef(wordIndex);
}
}
if (word->effSpace > 0 && wordIndex < line->lastWord &&
@@ -1492,7 +1087,7 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
}
}
- xWidget += word->size.width + word->effSpace;
+ xWidget += wordSize + word->effSpace;
}
}
@@ -1599,6 +1194,9 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace)
void Textblock::draw (core::View *view, core::Rectangle *area)
{
+ PRINTF ("DRAW: %d, %d, %d x %d\n",
+ area->x, area->y, area->width, area->height);
+
int lineIndex;
Line *line;
@@ -1629,10 +1227,11 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent,
word->size.width = width;
word->size.ascent = ascent;
word->size.descent = descent;
- word->origSpace = 0;
- word->effSpace = 0;
+ word->origSpace = word->effSpace = word->stretchability =
+ word->shrinkability = 0;
+ word->hyphenWidth = 0;
+ word->badnessAndPenalty.setPenaltyProhibitBreak ();
word->content.space = false;
- word->content.breakType = core::Content::BREAK_NO;
//DBG_OBJ_ARRSET_NUM (this, "words.%d.size.width", words->size() - 1,
// word->size.width);
@@ -1649,6 +1248,8 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent,
word->style = style;
word->spaceStyle = style;
+ word->hyphenStyle = style;
+ style->ref ();
style->ref ();
style->ref ();
@@ -1765,23 +1366,119 @@ void Textblock::calcTextSize (const char *text, size_t len,
/**
- * Add a word to the page structure.
+ * Add a word to the page structure. If it contains soft hyphens, it is
+ * divided.
*/
void Textblock::addText (const char *text, size_t len,
core::style::Style *style)
{
+ // Count hyphens.
+ int numHyphens = 0;
+ for (size_t i = 0; i < len - 1; i++)
+ // (0xc2, 0xad) is the UTF-8 representation of a soft hyphen (Unicode
+ // 0xc2).
+ if((unsigned char)text[i] == 0xc2 && (unsigned char)text[i + 1] == 0xad)
+ numHyphens++;
+
+ if (numHyphens == 0) {
+ // Simple (and often) case: no soft hyphens.
+ core::Requisition size;
+ calcTextSize (text, len, style, &size);
+ addText0 (text, len, style, &size);
+ } else {
+ PRINTF("HYPHENATION: '");
+ for (size_t i = 0; i < len; i++)
+ PUTCHAR(text[i]);
+ PRINTF("', with %d hyphen(s)\n", numHyphens);
+
+ // Store hyphen positions.
+ int n = 0, hyphenPos[numHyphens];
+ for (size_t i = 0; i < len - 1; i++)
+ if((unsigned char)text[i] == 0xc2 &&
+ (unsigned char)text[i + 1] == 0xad)
+ hyphenPos[n++] = i;
+
+ // Get text without hyphens. (There are numHyphens + 1 parts in the word,
+ // and 2 * numHyphens bytes less, 2 for each hyphen, are needed.)
+ char textWithoutHyphens[len - 2 * numHyphens];
+ int start = 0; // related to "text"
+ for (int i = 0; i < numHyphens + 1; i++) {
+ int end = (i == numHyphens) ? len : hyphenPos[i];
+ memmove (textWithoutHyphens + start - 2 * i, text + start,
+ end - start);
+ start = end + 2;
+ }
+
+ PRINTF("H... without hyphens: '");
+ for (size_t i = 0; i < len - 2 * numHyphens; i++)
+ PUTCHAR(textWithoutHyphens[i]);
+ PRINTF("'\n");
+
+ // Calc sizes.
+ core::Requisition wordSize[numHyphens + 1];
+
+ // The size of the last part is calculated in a simple way.
+ int lastStart = hyphenPos[numHyphens - 1] + 2;
+ calcTextSize (text + lastStart, len - lastStart, style,
+ &wordSize[numHyphens]);
+
+ PRINTF("H... [%d] '", numHyphens);
+ for (size_t i = 0; i < len - lastStart; i++)
+ PUTCHAR(text[i + lastStart]);
+ PRINTF("' -> %d\n", wordSize[numHyphens].width);
+
+ // The rest is more complicated. TODO Documentation.
+ for (int i = numHyphens - 1; i >= 0; i--) {
+ int start = (i == 0) ? 0 : hyphenPos[i - 1] - 2 * (i - 1);
+ calcTextSize (textWithoutHyphens + start,
+ len - 2 * numHyphens - start, style, &wordSize[i]);
+
+ PRINTF("H... [%d] '", i);
+ for (size_t j = 0; j < len - 2 * numHyphens - start; j++)
+ PUTCHAR(textWithoutHyphens[j + start]);
+ PRINTF("' -> %d\n", wordSize[i].width);
+
+ for (int j = i + 1; j < numHyphens + 1; j++) {
+ wordSize[i].width -= wordSize[j].width;
+ PRINTF("H... - %d = %d\n", wordSize[j].width, wordSize[i].width);
+ }
+ }
+
+ // Finished!
+ for (int i = 0; i < numHyphens + 1; i++) {
+ int start = (i == 0) ? 0 : hyphenPos[i - 1] + 2;
+ int end = (i == numHyphens) ? len : hyphenPos[i];
+ addText0 (text + start, end - start, style, &wordSize[i]);
+
+ PRINTF("H... [%d] '", i);
+ for (int j = start; j < end; j++)
+ PUTCHAR(text[j]);
+ PRINTF("' added\n");
+
+ if(i < numHyphens) {
+ addHyphen (style);
+ PRINTF("H... yphen added\n");
+ }
+ }
+ }
+}
+
+/**
+ * Add a word (without hyphens) to the page structure.
+ */
+void Textblock::addText0 (const char *text, size_t len,
+ core::style::Style *style, core::Requisition *size)
+{
Word *word;
- core::Requisition size;
- calcTextSize (text, len, style, &size);
- word = addWord (size.width, size.ascent, size.descent, style);
+ word = addWord (size->width, size->ascent, size->descent, style);
word->content.type = core::Content::TEXT;
word->content.text = layout->textZone->strndup(text, len);
//DBG_OBJ_ARRSET_STR (page, "words.%d.content.text", words->size() - 1,
// word->content.text);
- wordWrap (words->size () - 1);
+ wordWrap (words->size () - 1, false);
}
/**
@@ -1798,6 +1495,8 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
* 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);
@@ -1810,8 +1509,7 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
//DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1,
// word->content.widget);
- wordWrap (words->size () - 1);
- word->content.widget->parentRef = lines->size () - 1;
+ wordWrap (words->size () - 1, false);
//DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
// word->content.widget->parent_ref);
@@ -1879,11 +1577,17 @@ void Textblock::addSpace (core::style::Style *style)
// This is important e.g. to be able to break between foo and bar in:
// <span style="white-space:nowrap">foo </span> bar
addBreakOption (style);
-
+
if (!word->content.space) {
+ word->badnessAndPenalty.setPenalty (0);
word->content.space = true;
word->effSpace = word->origSpace = style->font->spaceWidth +
style->wordSpacing;
+ word->stretchability = word->origSpace / 2;
+ if(style->textAlign == core::style::TEXT_ALIGN_JUSTIFY)
+ word->shrinkability = word->origSpace / 3;
+ else
+ word->shrinkability = 0;
//DBG_OBJ_ARRSET_NUM (this, "words.%d.origSpace", wordIndex,
// word->origSpace);
@@ -1891,13 +1595,36 @@ void Textblock::addSpace (core::style::Style *style)
// word->effSpace);
//DBG_OBJ_ARRSET_NUM (this, "words.%d.content.space", wordIndex,
// word->content.space);
+
word->spaceStyle->unref ();
word->spaceStyle = style;
style->ref ();
+
+ accumulateWordData (wordIndex);
}
}
}
+void Textblock::addHyphen (core::style::Style *style)
+{
+ int wordIndex = words->size () - 1;
+
+ if (wordIndex >= 0) {
+ Word *word = words->getRef(wordIndex);
+
+ word->badnessAndPenalty.setPenalty (HYPHEN_BREAK);
+ //word->penalty = 0;
+ // TODO Optimize? Like spaces?
+ word->hyphenWidth = layout->textWidth (style->font, "\xc2\xad", 2);
+
+ word->hyphenStyle->unref ();
+ word->hyphenStyle = style;
+ style->ref ();
+
+ accumulateWordData (wordIndex);
+ }
+}
+
/**
* Cause a paragraph break
*/
@@ -1969,8 +1696,9 @@ void Textblock::addParbreak (int space, core::style::Style *style)
word = addWord (0, 0, 0, style);
word->content.type = core::Content::BREAK;
+ word->badnessAndPenalty.setPenaltyForceBreak ();
word->content.breakSpace = space;
- wordWrap (words->size () - 1);
+ wordWrap (words->size () - 1, false);
}
/*
@@ -1990,8 +1718,9 @@ void Textblock::addLinebreak (core::style::Style *style)
word = addWord (0, 0, 0, style);
word->content.type = core::Content::BREAK;
+ word->badnessAndPenalty.setPenaltyForceBreak ();
word->content.breakSpace = 0;
- wordWrap (words->size () - 1);
+ wordWrap (words->size () - 1, false);
}
@@ -2064,6 +1793,9 @@ void Textblock::handOverBreak (core::style::Style *style)
*/
void Textblock::flush ()
{
+ PRINTF ("[%p] FLUSH => %s (parentRef = %d)\n",
+ this, mustQueueResize ? "true" : "false", parentRef);
+
if (mustQueueResize) {
queueResize (-1, true);
mustQueueResize = false;
diff --git a/dw/textblock.hh b/dw/textblock.hh
index 3f98f878..b301db7d 100644
--- a/dw/textblock.hh
+++ b/dw/textblock.hh
@@ -1,15 +1,27 @@
#ifndef __DW_TEXTBLOCK_HH__
#define __DW_TEXTBLOCK_HH__
+#include <limits.h>
+
#include "core.hh"
#include "../lout/misc.hh"
+// These were used when improved line breaking and hyphenation were
+// implemented. Should be cleaned up; perhaps reactivate RTFL again.
+#define PRINTF(fmt, ...)
+#define PUTCHAR(ch)
+
namespace dw {
/**
* \brief A Widget for rendering text blocks, i.e. paragraphs or sequences
* of paragraphs.
*
+ * <div style="border: 2px solid #ff0000; padding: 0.5em 1em;
+ * background-color: #ffe0e0"><b>Warning:</b> The recent changes (line
+ * breaking and hyphenation) have not yet been incorporated into this
+ * documentation. See \ref dw-line-breaking.</div>
+ *
* <h3>Signals</h3>
*
* dw::Textblock uses the signals defined in
@@ -130,7 +142,50 @@ namespace dw {
*/
class Textblock: public core::Widget
{
+private:
+ /**
+ * This class encapsulates the badness/penalty calculation, and so
+ * (i) makes changes (hopefully) simpler, and (ii) hides the
+ * integer arithmetics (floating point arithmetics avoided for
+ * performance reasons). Unfortunately, the value range of the
+ * badness is not well defined, so fiddling with the penalties is a
+ * bit difficult.
+ */
+ class BadnessAndPenalty
+ {
+ private:
+ enum { TOO_LOOSE, TOO_TIGHT, BADNESS_VALUE } badnessState;
+ enum { FORCE_BREAK, PROHIBIT_BREAK, PENALTY_VALUE } penaltyState;
+ int badness, penalty;
+
+ // for debugging:
+ int totalWidth, idealWidth, totalStretchability, totalShrinkability;
+
+ int badnessInfinities ();
+ int penaltyInfinities ();
+ int badnessValue ();
+ int penaltyValue ();
+
+ public:
+ void calcBadness (int totalWidth, int idealWidth,
+ int totalStretchability, int totalShrinkability);
+ void setPenalty (int penalty);
+ void setPenaltyProhibitBreak ();
+ void setPenaltyForceBreak ();
+
+ bool lineTooTight ();
+ bool lineMustBeBroken ();
+ bool lineCanBeBroken ();
+ int compareTo (BadnessAndPenalty *other);
+
+ void print ();
+ };
+
protected:
+ enum {
+ HYPHEN_BREAK = 1000000 // to be tested and tuned
+ };
+
struct Line
{
int firstWord; /* first word's index in word vector */
@@ -150,9 +205,10 @@ protected:
int maxLineWidth; /* maximum of all line widths */
int maxParMin; /* maximum of all paragraph minima */
int maxParMax; /* maximum of all paragraph maxima */
- int parMax; /* the maximal total width down from the last
- * paragraph start, to the *beginning* of the
- * line */
+ int parMax; /* The maximal total width down from the last
+ * paragraph start, to the *ene* of this
+ * line. (Notice that the semantics have
+ * changed.) */
};
struct Word
@@ -161,13 +217,35 @@ protected:
core::Requisition size;
/* Space after the word, only if it's not a break: */
short origSpace; /* from font, set by addSpace */
+ short stretchability, shrinkability;
short effSpace; /* effective space, set by wordWrap,
* used for drawing etc. */
+ short hyphenWidth; /* Additional width, when a word is part
+ * (except the last part) of a hyphenationed
+ * word. Has to be added to the width, when
+ * this is the last word of the line, and
+ * "hyphenWidth > 0" is also used to decide
+ * weather to draw a hyphen. */
core::Content content;
+ // accumulated values, relative to the beginning of the line
+ int totalWidth; /* The sum of all word widths; plus all
+ spaces, excluding the one of this
+ word; plus the hypthen width of this
+ word (but of course, no hyphen
+ widths of previous words. In other
+ words: the value compared to the
+ ideal width of the line, if the line
+ would be broken after this word. */
+ int totalStretchability; // includes all *before* current word
+ int totalShrinkability; // includes all *before* current word
+ BadnessAndPenalty badnessAndPenalty; /* when line is broken after this
+ * word */
+
core::style::Style *style;
core::style::Style *spaceStyle; /* initially the same as of the word,
later set by a_Dw_page_add_space */
+ core::style::Style *hyphenStyle;
};
struct Anchor
@@ -236,13 +314,10 @@ protected:
/* These values are set by set_... */
int availWidth, availAscent, availDescent;
- int lastLineWidth;
- int lastLineParMin; /* width of the current non-breakable word sequence
- * used by wordWrap () */
- int lastLineParMax;
int wrapRef; /* [0 based] */
lout::misc::SimpleVector <Line> *lines;
+ int nonTemporaryLines;
lout::misc::SimpleVector <Word> *words;
lout::misc::SimpleVector <Anchor> *anchors;
@@ -254,25 +329,28 @@ protected:
void queueDrawRange (int index1, int index2);
void getWordExtremes (Word *word, core::Extremes *extremes);
- inline bool canBreakAfter (Word *word)
- {
- return word->content.breakType == core::Content::BREAK_OK;
- }
void markChange (int ref);
- void justifyLine (Line *line, int availWidth);
- Line *addLine (int wordInd, bool newPar);
+ void justifyLine (Line *line, int diff);
+ Line *addLine (int firstWord, int lastWord, bool temporary);
void calcWidgetSize (core::Widget *widget, core::Requisition *size);
void rewrap ();
- void decorateText(core::View *view, core::style::Style *style,
- core::style::Color::Shading shading,
- int x, int yBase, int width);
- void drawText(core::View *view, core::style::Style *style,
- core::style::Color::Shading shading, int x, int y,
- const char *text, int start, int len);
- void drawWord(int wordIndex, core::View *view, core::Rectangle *area,
- int xWidget, int yWidgetBase);
- void drawSpace(int wordIndex, core::View *view, core::Rectangle *area,
- int xWidget, int yWidgetBase);
+ void showMissingLines ();
+ void removeTemporaryLines ();
+
+ void decorateText (core::View *view, core::style::Style *style,
+ core::style::Color::Shading shading,
+ int x, int yBase, int width);
+ void drawText (core::View *view, core::style::Style *style,
+ core::style::Color::Shading shading, int x, int y,
+ const char *text, int start, int len);
+ void drawWord (Line *line, int wordIndex1, int wordIndex2, core::View *view,
+ core::Rectangle *area, int xWidget, int yWidgetBase);
+ void drawWord0 (int wordIndex1, int wordIndex2,
+ const char *text, int totalWidth,
+ core::style::Style *style, core::View *view,
+ core::Rectangle *area, int xWidget, int yWidgetBase);
+ void drawSpace (int wordIndex, core::View *view, core::Rectangle *area,
+ int xWidget, int yWidgetBase);
void drawLine (Line *line, core::View *view, core::Rectangle *area);
int findLineIndex (int y);
int findLineOfWord (int wordIndex);
@@ -348,7 +426,14 @@ protected:
bool sendSelectionEvent (core::SelectionState::EventType eventType,
core::MousePositionEvent *event);
- virtual void wordWrap(int wordIndex);
+ void accumulateWordExtremees (int firstWord, int lastWord,
+ int *maxOfMinWidth, int *sumOfMaxWidth);
+ virtual void wordWrap (int wordIndex, bool wrapAll);
+ void accumulateWordForLine (int lineIndex, int wordIndex);
+ void accumulateWordData(int wordIndex);
+ int calcAvailWidth ();
+ void initLine1Offset (int wordIndex);
+ void alignLine (Line *line);
void sizeRequestImpl (core::Requisition *requisition);
void getExtremesImpl (core::Extremes *extremes);
@@ -370,6 +455,9 @@ protected:
void removeChild (Widget *child);
+ void addText0 (const char *text, size_t len, core::style::Style *style,
+ core::Requisition *size);
+
public:
static int CLASS_ID;
@@ -388,14 +476,16 @@ public:
void addWidget (core::Widget *widget, core::style::Style *style);
bool addAnchor (const char *name, core::style::Style *style);
void addSpace(core::style::Style *style);
- inline void addBreakOption (core::style::Style *style)
+ inline void addBreakOption (core::style::Style *style) // TODO needed?
{
int wordIndex = words->size () - 1;
if (wordIndex >= 0 &&
style->whiteSpace != core::style::WHITE_SPACE_NOWRAP &&
style->whiteSpace != core::style::WHITE_SPACE_PRE)
- words->getRef(wordIndex)->content.breakType = core::Content::BREAK_OK;
+ words->getRef(wordIndex)->badnessAndPenalty.setPenaltyForceBreak ();
}
+
+ void addHyphen(core::style::Style *style);
void addParbreak (int space, core::style::Style *style);
void addLinebreak (core::style::Style *style);
diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc
new file mode 100644
index 00000000..7037330e
--- /dev/null
+++ b/dw/textblock_linebreaking.cc
@@ -0,0 +1,688 @@
+#include "textblock.hh"
+#include "../lout/msg.h"
+#include "../lout/misc.hh"
+
+#include <stdio.h>
+#include <math.h>
+
+using namespace lout;
+
+namespace dw {
+
+int Textblock::BadnessAndPenalty::badnessInfinities ()
+{
+ switch (badnessState) {
+ case TOO_LOOSE:
+ case TOO_TIGHT:
+ return 1;
+
+ case BADNESS_VALUE:
+ return 0;
+ }
+
+ // compiler happiness
+ lout::misc::assertNotReached ();
+ return 0;
+}
+
+int Textblock::BadnessAndPenalty::penaltyInfinities ()
+{
+ switch (penaltyState) {
+ case FORCE_BREAK:
+ return -1;
+
+ case PROHIBIT_BREAK:
+ return 1;
+
+ case PENALTY_VALUE:
+ return 0;
+ }
+
+ // compiler happiness
+ lout::misc::assertNotReached ();
+ return 0;
+}
+
+int Textblock::BadnessAndPenalty::badnessValue ()
+{
+ return badnessState == BADNESS_VALUE ? badness : 0;
+}
+
+int Textblock::BadnessAndPenalty::penaltyValue ()
+{
+ return penaltyState == PENALTY_VALUE ? penalty : 0;
+}
+
+void Textblock::BadnessAndPenalty::calcBadness (int totalWidth, int idealWidth,
+ int totalStretchability,
+ int totalShrinkability)
+{
+ this->totalWidth = totalWidth;
+ this->idealWidth = idealWidth;
+ this->totalStretchability = totalStretchability;
+ this->totalShrinkability = totalShrinkability;
+
+ if (totalWidth == idealWidth) {
+ badnessState = BADNESS_VALUE;
+ badness = 0;
+ } else if (totalWidth < idealWidth) {
+ if (totalStretchability == 0)
+ badnessState = TOO_LOOSE;
+ else {
+ int ratio = 100 * (idealWidth - totalWidth) / totalStretchability;
+ if (ratio > 1024)
+ badnessState = TOO_LOOSE;
+ else {
+ badnessState = BADNESS_VALUE;
+ badness = ratio * ratio * ratio;
+ }
+ }
+ } else { // if (word->totalWidth > availWidth)
+ if (totalShrinkability == 0)
+ badnessState = TOO_TIGHT;
+ else {
+ // Important: ratio is positive here.
+ int ratio = 100 * (totalWidth - idealWidth) / totalShrinkability;
+ if (ratio >= 100)
+ badnessState = TOO_TIGHT;
+ else {
+ badnessState = BADNESS_VALUE;
+ badness = ratio * ratio * ratio;
+ }
+ }
+ }
+}
+
+void Textblock::BadnessAndPenalty::setPenalty (int penalty)
+{
+ this->penalty = penalty;
+ penaltyState = PENALTY_VALUE;
+}
+
+void Textblock::BadnessAndPenalty::setPenaltyProhibitBreak ()
+{
+ penaltyState = PROHIBIT_BREAK;
+}
+
+void Textblock::BadnessAndPenalty::setPenaltyForceBreak ()
+{
+ penaltyState = FORCE_BREAK;
+}
+
+bool Textblock::BadnessAndPenalty::lineTooTight ()
+{
+ return badnessState == TOO_TIGHT;
+}
+
+bool Textblock::BadnessAndPenalty::lineMustBeBroken ()
+{
+ return penaltyState == FORCE_BREAK;
+}
+
+bool Textblock::BadnessAndPenalty::lineCanBeBroken ()
+{
+ return penaltyState != PROHIBIT_BREAK;
+}
+
+int Textblock::BadnessAndPenalty::compareTo (BadnessAndPenalty *other)
+{
+ int thisNumInfinities = badnessInfinities () + penaltyInfinities ();
+ int otherNumInfinities =
+ other->badnessInfinities () + other->penaltyInfinities ();
+ int thisValue = badnessValue () + penaltyValue ();
+ int otherValue = other->badnessValue () + other->penaltyValue ();
+
+ if (thisNumInfinities == otherNumInfinities)
+ return thisValue - otherValue;
+ else
+ return thisNumInfinities - otherNumInfinities;
+}
+
+void Textblock::BadnessAndPenalty::print ()
+{
+ switch (badnessState) {
+ case TOO_LOOSE:
+ PRINTF ("loose");
+ break;
+
+ case TOO_TIGHT:
+ PRINTF ("tight");
+ break;
+
+ case BADNESS_VALUE:
+ PRINTF ("%d", badness);
+ break;
+ }
+
+ PRINTF (" [%d + %d - %d vs. %d] + ", totalWidth, totalStretchability,
+ totalShrinkability, idealWidth);
+
+ switch (penaltyState) {
+ case FORCE_BREAK:
+ PRINTF ("-inf");
+ break;
+
+ case PROHIBIT_BREAK:
+ PRINTF ("inf");
+ break;
+
+ case PENALTY_VALUE:
+ PRINTF ("%d", penalty);
+ break;
+ }
+}
+
+/*
+ * ...
+ *
+ * diff ...
+ */
+void Textblock::justifyLine (Line *line, int diff)
+{
+ /* To avoid rounding errors, the calculation is based on accumulated
+ * values. */
+
+ if (diff > 0) {
+ int stretchabilitySum = 0;
+ for (int i = line->firstWord; i < line->lastWord; i++)
+ stretchabilitySum += words->getRef(i)->stretchability;
+
+ if (stretchabilitySum > 0) {
+ int stretchabilityCum = 0;
+ int spaceDiffCum = 0;
+ for (int i = line->firstWord; i < line->lastWord; i++) {
+ Word *word = words->getRef (i);
+ stretchabilityCum += word->stretchability;
+ int spaceDiff =
+ stretchabilityCum * diff / stretchabilitySum - spaceDiffCum;
+ spaceDiffCum += spaceDiff;
+
+ PRINTF (" %d (of %d): diff = %d\n", i, words->size (),
+ spaceDiff);
+
+ word->effSpace = word->origSpace + spaceDiff;
+ }
+ }
+ } else if (diff < 0) {
+ int shrinkabilitySum = 0;
+ for (int i = line->firstWord; i < line->lastWord; i++)
+ shrinkabilitySum += words->getRef(i)->shrinkability;
+
+ if (shrinkabilitySum > 0) {
+ int shrinkabilityCum = 0;
+ int spaceDiffCum = 0;
+ for (int i = line->firstWord; i < line->lastWord; i++) {
+ Word *word = words->getRef (i);
+ shrinkabilityCum += word->shrinkability;
+ int spaceDiff =
+ shrinkabilityCum * diff / shrinkabilitySum - spaceDiffCum;
+ spaceDiffCum += spaceDiff;
+
+ word->effSpace = word->origSpace + spaceDiff;
+ }
+ }
+ }
+}
+
+
+Textblock::Line *Textblock::addLine (int firstWord, int lastWord,
+ bool temporary)
+{
+ PRINTF ("[%p] ADD_LINE (%d, %d)\n", this, firstWord, lastWord);
+
+ Word *lastWordOfLine = words->getRef(lastWord);
+ // Word::totalWidth includes the hyphen (which is what we want here).
+ int lineWidth = lastWordOfLine->totalWidth;
+ int maxOfMinWidth, sumOfMaxWidth;
+ accumulateWordExtremees (firstWord, lastWord, &maxOfMinWidth,
+ &sumOfMaxWidth);
+
+ PRINTF (" words[%d]->totalWidth = %d\n", lastWord,
+ lastWordOfLine->totalWidth);
+
+ lines->increase ();
+ if(!temporary) {
+ // If the last line was temporary, this will be temporary, too, even
+ // if not requested.
+ if (lines->size () == 1 || nonTemporaryLines == lines->size () -1)
+ nonTemporaryLines = lines->size ();
+ }
+
+ PRINTF ("nonTemporaryLines = %d\n", nonTemporaryLines);
+
+ int lineIndex = lines->size () - 1;
+ Line *line = lines->getRef (lineIndex);
+
+ line->firstWord = firstWord;
+ line->lastWord = lastWord;
+ line->boxAscent = line->contentAscent = 0;
+ line->boxDescent = line->contentDescent = 0;
+ line->marginDescent = 0;
+ line->breakSpace = 0;
+ line->leftOffset = 0;
+
+ if (lines->size () == 1) {
+ line->top = 0;
+
+ // TODO What to do with this one: lastLine->maxLineWidth = line1OffsetEff;
+ line->maxLineWidth = lineWidth;
+ line->maxParMin = maxOfMinWidth;
+ line->parMax = line->maxParMax = sumOfMaxWidth;
+ } else {
+ Line *prevLine = lines->getRef (lines->size () - 2);
+
+ line->top = prevLine->top + prevLine->boxAscent +
+ prevLine->boxDescent + prevLine->breakSpace;
+
+ line->maxLineWidth = misc::max (lineWidth, prevLine->maxLineWidth);
+ line->maxParMin = misc::max (maxOfMinWidth, prevLine->maxParMin);
+
+ Word *lastWordOfPrevLine = words->getRef (prevLine->lastWord);
+ if (lastWordOfPrevLine->content.type == core::Content::BREAK)
+ // This line starts a new paragraph.
+ line->parMax = sumOfMaxWidth;
+ else
+ // This line continues the paragraph from prevLine.
+ line->parMax = prevLine->parMax + sumOfMaxWidth;
+
+ line->maxParMax = misc::max (line->parMax, prevLine->maxParMax);
+
+ }
+
+ for(int i = line->firstWord; i <= line->lastWord; i++)
+ accumulateWordForLine (lineIndex, i);
+
+ PRINTF (" line[%d].top = %d\n", lines->size () - 1, line->top);
+ PRINTF (" line[%d].boxAscent = %d\n", lines->size () - 1, line->boxAscent);
+ PRINTF (" line[%d].boxDescent = %d\n",
+ lines->size () - 1, line->boxDescent);
+ PRINTF (" line[%d].contentAscent = %d\n", lines->size () - 1,
+ line->contentAscent);
+ PRINTF (" line[%d].contentDescent = %d\n",
+ lines->size () - 1, line->contentDescent);
+
+ alignLine (line);
+ mustQueueResize = true;
+
+ return line;
+}
+
+void Textblock::accumulateWordExtremees (int firstWord, int lastWord,
+ int *maxOfMinWidth, int *sumOfMaxWidth)
+{
+ *maxOfMinWidth = *sumOfMaxWidth = 0;
+
+ for (int i = firstWord; i <= lastWord; i++) {
+ Word *word = words->getRef (i);
+ core::Extremes extremes;
+ getWordExtremes (word, &extremes);
+
+ *maxOfMinWidth = misc::min (*maxOfMinWidth, extremes.minWidth);
+ *sumOfMaxWidth += (extremes.maxWidth + word->origSpace);
+ // Regarding the sum: if this is the end of the paragraph, it
+ // does not matter, since word->space is 0 in this case.
+ }
+}
+
+/*
+ * 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, bool wrapAll)
+{
+ Word *word;
+ //core::Extremes wordExtremes;
+
+ if (!wrapAll)
+ removeTemporaryLines ();
+
+ initLine1Offset (wordIndex);
+
+ word = words->getRef (wordIndex);
+ word->effSpace = word->origSpace;
+
+ accumulateWordData (wordIndex);
+
+ bool newLine;
+ do {
+ bool tempNewLine = false;
+ int firstIndex = lines->size() == 0 ?
+ 0 : lines->getRef(lines->size() - 1)->lastWord + 1;
+ int searchUntil;
+
+ if (wrapAll && wordIndex >= firstIndex && wordIndex == words->size() -1) {
+ newLine = true;
+ searchUntil = wordIndex;
+ tempNewLine = true;
+ PRINTF ("NEW LINE: last word\n");
+ } else if (wordIndex >= firstIndex &&
+ word->badnessAndPenalty.lineMustBeBroken ()) {
+ newLine = true;
+ searchUntil = wordIndex;
+ PRINTF ("NEW LINE: forced break\n");
+ } else if (wordIndex > firstIndex &&
+ word->badnessAndPenalty.lineTooTight () &&
+ words->getRef(wordIndex- 1)
+ ->badnessAndPenalty.lineCanBeBroken ()) {
+ // TODO Comment the last condition (also below where the minimun is
+ // searched for)
+ newLine = true;
+ searchUntil = wordIndex - 1;
+ PRINTF ("NEW LINE: line too tight\n");
+ } else
+ newLine = false;
+
+ if(newLine) {
+ PRINTF (" searching from %d to %d\n", firstIndex, searchUntil);
+
+ accumulateWordData (wordIndex);
+
+ int breakPos = -1;
+ for (int i = firstIndex; i <= searchUntil; i++) {
+ Word *w = words->getRef(i);
+
+ if(word->content.type && core::Content::REAL_CONTENT) {
+ PRINTF (" %d (of %d): ", i, words->size ());
+
+ switch(w->content.type) {
+ case core::Content::TEXT:
+ PRINTF ("\"%s\"", w->content.text);
+ break;
+ case core::Content::WIDGET:
+ PRINTF ("<widget: %p>\n", w->content.widget);
+ break;
+ case core::Content::BREAK:
+ PRINTF ("<break>\n");
+ break;
+ default:
+ PRINTF ("<?>\n");
+ break;
+ }
+
+ PRINTF (" [%d / %d + %d - %d] => ",
+ w->size.width, w->origSpace, w->stretchability,
+ w->shrinkability);
+ w->badnessAndPenalty.print ();
+ PRINTF ("\n");
+ }
+
+
+ // TODO: is this condition needed:
+ // if(w->badnessAndPenalty.lineCanBeBroken ()) ?
+
+ if (breakPos == -1 ||
+ w->badnessAndPenalty.compareTo
+ (&words->getRef(breakPos)->badnessAndPenalty) <= 0)
+ // "<=" instead of "<" in the next lines tends to result in more
+ // words per line -- theoretically. Practically, the case "=="
+ // will never occur.
+ breakPos = i;
+ }
+
+ if (wrapAll && searchUntil == words->size () - 1) {
+ // Since no break and no space is added, the last word
+ // will have a penalty of inf. Actually, it should be -inf,
+ // since it is the last word. However, since more words may
+ // follow, the penalty is not changesd, but here, the search
+ // is corrected (maybe only temporary).
+ Word *lastWord = words->getRef (searchUntil);
+ BadnessAndPenalty correctedBap = lastWord->badnessAndPenalty;
+ correctedBap.setPenaltyForceBreak ();
+ if (correctedBap.compareTo
+ (&words->getRef(breakPos)->badnessAndPenalty) <= 0)
+ breakPos = searchUntil;
+ }
+
+ PRINTF (" new line from %d to %d\n", firstIndex, breakPos);
+ addLine (firstIndex, breakPos, tempNewLine);
+ PRINTF (" accumulating again from %d to %d\n",
+ breakPos + 1, wordIndex);
+
+ for(int i = breakPos + 1; i <= wordIndex; i++)
+ accumulateWordData (i);
+ }
+ } while (newLine);
+}
+
+void Textblock::accumulateWordForLine (int lineIndex, int wordIndex)
+{
+ Line *line = lines->getRef (lineIndex);
+ Word *word = words->getRef (wordIndex);
+
+ PRINTF (" %d + %d / %d + %d\n", line->boxAscent, line->boxDescent,
+ word->size.ascent, word->size.descent);
+
+ line->boxAscent = misc::max (line->boxAscent, word->size.ascent);
+ line->boxDescent = misc::max (line->boxDescent, word->size.descent);
+
+ int len = word->style->font->ascent;
+ if (word->style->valign == core::style::VALIGN_SUPER)
+ len += len / 2;
+ line->contentAscent = misc::max (line->contentAscent, len);
+
+ len = word->style->font->descent;
+ if (word->style->valign == core::style::VALIGN_SUB)
+ len += word->style->font->ascent / 3;
+ line->contentDescent = misc::max (line->contentDescent, len);
+
+ if (word->content.type == core::Content::WIDGET) {
+ int collapseMarginTop = 0;
+
+ line->marginDescent =
+ misc::max (line->marginDescent,
+ word->size.descent +
+ word->content.widget->getStyle()->margin.bottom);
+
+ if (lines->size () == 1 &&
+ word->content.widget->blockLevel () &&
+ getStyle ()->borderWidth.top == 0 &&
+ getStyle ()->padding.top == 0) {
+ // collapse top margins of parent element and its first child
+ // see: http://www.w3.org/TR/CSS21/box.html#collapsing-margins
+ collapseMarginTop = getStyle ()->margin.top;
+ }
+
+ line->boxAscent =
+ misc::max (line->boxAscent,
+ word->size.ascent,
+ word->size.ascent
+ + word->content.widget->getStyle()->margin.top
+ - collapseMarginTop);
+
+ word->content.widget->parentRef = lineIndex;
+ } else {
+ line->marginDescent =
+ misc::max (line->marginDescent, line->boxDescent);
+
+ if (word->content.type == core::Content::BREAK)
+ line->breakSpace =
+ misc::max (word->content.breakSpace,
+ line->marginDescent - line->boxDescent,
+ line->breakSpace);
+ }
+}
+
+void Textblock::accumulateWordData (int wordIndex)
+{
+ PRINTF ("[%p] ACCUMULATE_WORD_DATA: %d\n", this, wordIndex);
+
+ Word *word = words->getRef (wordIndex);
+ int availWidth = calcAvailWidth (); // todo: variable? parameter?
+
+ if (wordIndex == 0 ||
+ (lines->size () > 0 &&
+ wordIndex == lines->getRef(lines->size () - 1)->lastWord + 1)) {
+ // first word of the (not neccessarily yet existing) line
+ word->totalWidth = word->size.width;
+ word->totalStretchability = 0;
+ word->totalShrinkability = 0;
+ } else {
+ Word *prevWord = words->getRef (wordIndex - 1);
+
+ word->totalWidth = prevWord->totalWidth
+ + prevWord->origSpace - prevWord->hyphenWidth
+ + word->size.width + word->hyphenWidth;
+ word->totalStretchability =
+ prevWord->totalStretchability + prevWord->stretchability;
+ word->totalShrinkability =
+ prevWord->totalShrinkability + prevWord->shrinkability;
+ }
+
+ PRINTF(" line width: %d of %d\n", word->totalWidth, availWidth);
+ PRINTF(" spaces: + %d - %d\n",
+ word->totalStretchability, word->totalShrinkability);
+
+ word->badnessAndPenalty.calcBadness (word->totalWidth, availWidth,
+ word->totalStretchability,
+ word->totalShrinkability);
+}
+
+int Textblock::calcAvailWidth ()
+{
+ int availWidth =
+ this->availWidth - getStyle()->boxDiffWidth() - innerPadding;
+ if (limitTextWidth &&
+ layout->getUsesViewport () &&
+ availWidth > layout->getWidthViewport () - 10)
+ availWidth = layout->getWidthViewport () - 10;
+
+ //PRINTF("[%p] CALC_AVAIL_WIDTH => %d - %d - %d = %d\n",
+ // this, this->availWidth, getStyle()->boxDiffWidth(), innerPadding,
+ // availWidth);
+
+ return availWidth;
+}
+
+void Textblock::initLine1Offset (int wordIndex)
+{
+ Word *word = words->getRef (wordIndex);
+
+ /* Test whether line1Offset can be used. */
+ if (wordIndex == 0) {
+ if (ignoreLine1OffsetSometimes &&
+ line1Offset + word->size.width > availWidth) {
+ line1OffsetEff = 0;
+ } else {
+ int indent = 0;
+
+ if (word->content.type == core::Content::WIDGET &&
+ word->content.widget->blockLevel() == true) {
+ /* don't use text-indent when nesting blocks */
+ } else {
+ if (core::style::isPerLength(getStyle()->textIndent)) {
+ indent = misc::roundInt(this->availWidth *
+ core::style::perLengthVal (getStyle()->textIndent));
+ } else {
+ indent = core::style::absLengthVal (getStyle()->textIndent);
+ }
+ }
+ line1OffsetEff = line1Offset + indent;
+ }
+ }
+}
+
+/**
+ * Align the line.
+ *
+ * \todo Use block's style instead once paragraphs become proper blocks.
+ */
+void Textblock::alignLine (Line *line)
+{
+ int availWidth = calcAvailWidth ();
+ Word *firstWord = words->getRef (line->firstWord);
+ Word *lastWord = words->getRef (line->lastWord);
+
+ for (int i = line->firstWord; i < line->lastWord; i++)
+ words->getRef(i)->origSpace = words->getRef(i)->effSpace;
+
+ if (firstWord->content.type != core::Content::BREAK) {
+ switch (firstWord->style->textAlign) {
+ case core::style::TEXT_ALIGN_LEFT:
+ case core::style::TEXT_ALIGN_STRING: /* handled elsewhere (in the
+ * future)? */
+ line->leftOffset = 0;
+ break;
+ case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */
+ line->leftOffset = 0;
+ if(lastWord->content.type != core::Content::BREAK &&
+ line->lastWord != words->size () - 1) {
+ PRINTF (" justifyLine => %d vs. %d\n",
+ lastWord->totalWidth, availWidth);
+ justifyLine (line, availWidth - lastWord->totalWidth);
+ }
+ break;
+ case core::style::TEXT_ALIGN_RIGHT:
+ line->leftOffset = availWidth - lastWord->totalWidth;
+ break;
+ case core::style::TEXT_ALIGN_CENTER:
+ line->leftOffset = (availWidth - lastWord->totalWidth) / 2;
+ break;
+ default:
+ /* compiler happiness */
+ line->leftOffset = 0;
+ }
+
+ /* For large lines (images etc), which do not fit into the viewport: */
+ if (line->leftOffset < 0)
+ line->leftOffset = 0;
+ }
+}
+
+/**
+ * Rewrap the page from the line from which this is necessary.
+ * There are basically two times we'll want to do this:
+ * either when the viewport is resized, or when the size changes on one
+ * of the child widgets.
+ */
+void Textblock::rewrap ()
+{
+ PRINTF ("[%p] REWRAP: wrapRef = %d\n", this, wrapRef);
+
+ if (wrapRef == -1)
+ /* page does not have to be rewrapped */
+ return;
+
+ /* All lines up from wrapRef will be rebuild from the word list,
+ * the line list up from this position is rebuild. */
+ lines->setSize (wrapRef);
+ nonTemporaryLines = misc::min (nonTemporaryLines, wrapRef);
+
+ int firstWord;
+ if (lines->size () > 0)
+ firstWord = lines->getLastRef()->lastWord + 1;
+ else
+ firstWord = 0;
+
+ for (int i = firstWord; i < words->size (); i++) {
+ Word *word = words->getRef (i);
+
+ if (word->content.type == core::Content::WIDGET)
+ calcWidgetSize (word->content.widget, &word->size);
+
+ wordWrap (i, false);
+
+ if (word->content.type == core::Content::WIDGET) {
+ word->content.widget->parentRef = lines->size () - 1;
+ }
+ }
+
+ /* Next time, the page will not have to be rewrapped. */
+ wrapRef = -1;
+}
+
+void Textblock::showMissingLines ()
+{
+ int firstWordToWrap = lines->size () > 0 ?
+ lines->getRef(lines->size () - 1)->lastWord + 1 : 0;
+ for (int i = firstWordToWrap; i < words->size (); i++)
+ wordWrap (i, true);
+}
+
+
+void Textblock::removeTemporaryLines ()
+{
+ lines->setSize (nonTemporaryLines);
+}
+
+} // namespace dw
diff --git a/dw/types.hh b/dw/types.hh
index eac834ff..65983fad 100644
--- a/dw/types.hh
+++ b/dw/types.hh
@@ -194,16 +194,12 @@ struct Content
REAL_CONTENT = 0xff ^ (START | END),
SELECTION_CONTENT = TEXT | WIDGET | BREAK
};
- enum BreakType {
- BREAK_NO,
- BREAK_OK
- };
+
/* Content is embedded in struct Word therefore we
* try to be space efficient.
*/
short type;
bool space;
- unsigned char breakType;
union {
const char *text;
Widget *widget;