dillo  3.0.5
About: dillo is a small, fast, extensible Web browser particularly suitable for older or smaller computers and embedded systems (but only limited or no support for frames, CSS, JavaScript, Java).
  Fossies Dox: dillo-3.0.5.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

textblock.cc
Go to the documentation of this file.
1 /*
2  * Dillo Widget
3  *
4  * Copyright 2005-2007, 2012-2013 Sebastian Geerken <sgeerken@dillo.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 
21 #include "textblock.hh"
22 #include "../lout/msg.h"
23 #include "../lout/misc.hh"
24 #include "../lout/unicode.hh"
25 #include "../lout/debug.hh"
26 
27 #include <stdio.h>
28 #include <math.h>
29 
30 /*
31  * Local variables
32  */
33  /* The tooltip under mouse pointer in current textblock. No ref. hold.
34  * (having one per view looks not worth the extra clutter). */
36 
37 
38 using namespace lout;
39 using namespace lout::unicode;
40 
41 namespace dw {
42 
43 int Textblock::CLASS_ID = -1;
44 
45 Textblock::WordImgRenderer::WordImgRenderer (Textblock *textblock,
46  int wordNo)
47 {
48  //printf ("new WordImgRenderer %p\n", this);
49 
50  this->textblock = textblock;
51  this->wordNo = wordNo;
52  dataSet = false;
53 }
54 
55 Textblock::WordImgRenderer::~WordImgRenderer ()
56 {
57  //printf ("delete WordImgRenderer %p\n", this);
58 }
59 
60 void Textblock::WordImgRenderer::setData (int xWordWidget, int lineNo)
61 {
62  dataSet = true;
63  this->xWordWidget = xWordWidget;
64  this->lineNo = lineNo;
65 }
66 
67 bool Textblock::WordImgRenderer::readyToDraw ()
68 {
69  //print ();
70  //printf ("\n");
71 
72  return dataSet && textblock->wasAllocated ()
73  && wordNo < textblock->words->size()
74  && lineNo < textblock->lines->size();
75 }
76 
77 void Textblock::WordImgRenderer::getBgArea (int *x, int *y, int *width,
78  int *height)
79 {
80  // TODO Subtract margin and border (padding box)?
81  Line *line = textblock->lines->getRef (lineNo);
82  *x = textblock->allocation.x + this->xWordWidget;
83  *y = textblock->lineYOffsetCanvas (line);
84  *width = textblock->words->getRef(wordNo)->size.width;
85  *height = line->boxAscent + line->boxDescent;
86 }
87 
88 void Textblock::WordImgRenderer::getRefArea (int *xRef, int *yRef,
89  int *widthRef, int *heightRef)
90 {
91  // See comment in Widget::drawBox about the reference area.
92  textblock->getPaddingArea (xRef, yRef, widthRef, heightRef);
93 }
94 
95 core::style::Style *Textblock::WordImgRenderer::getStyle ()
96 {
97  return textblock->words->getRef(wordNo)->style;
98 }
99 
100 void Textblock::WordImgRenderer::draw (int x, int y, int width, int height)
101 {
102  textblock->queueDrawArea (x - textblock->allocation.x,
103  y - textblock->allocation.y, width, height);
104 
105 }
106 
107 void Textblock::WordImgRenderer::print ()
108 {
109  printf ("%p: word #%d, ", this, wordNo);
110  if (wordNo < textblock->words->size())
111  textblock->printWordShort (textblock->words->getRef(wordNo));
112  else
113  printf ("<word %d does not exist>", wordNo);
114  printf (", data set: %s", dataSet ? "yes" : "no");
115 }
116 
117 void Textblock::SpaceImgRenderer::getBgArea (int *x, int *y, int *width,
118  int *height)
119 {
120  WordImgRenderer::getBgArea (x, y, width, height);
121  *x += *width;
122  *width = textblock->words->getRef(wordNo)->effSpace;
123 }
124 
125 core::style::Style *Textblock::SpaceImgRenderer::getStyle ()
126 {
127  return textblock->words->getRef(wordNo)->spaceStyle;
128 }
129 
130 void Textblock::SpaceImgRenderer::print ()
131 {
132  printf ("%p: word FOR SPACE #%d, ", this, wordNo);
133  if (wordNo < textblock->words->size())
134  textblock->printWordShort (textblock->words->getRef(wordNo));
135  else
136  printf ("<word %d does not exist>", wordNo);
137  printf (", data set: %s", dataSet ? "yes" : "no");
138 }
139 
140 // ----------------------------------------------------------------------
141 
142 Textblock::DivChar Textblock::divChars[NUM_DIV_CHARS] = {
143  // soft hyphen (U+00AD)
144  { "\xc2\xad", true, false, true, PENALTY_HYPHEN, -1 },
145  // simple hyphen-minus: same penalties like automatic or soft hyphens
146  { "-", false, true, true, -1, PENALTY_HYPHEN },
147  // (unconditional) hyphen (U+2010): handled exactly like minus-hyphen.
148  { "\xe2\x80\x90", false, true, true, -1, PENALTY_HYPHEN },
149  // em dash (U+2014): breaks on both sides are allowed (but see below).
150  { "\xe2\x80\x94", false, true, false,
151  PENALTY_EM_DASH_LEFT, PENALTY_EM_DASH_RIGHT }
152 };
153 
154 // Standard values are defined here. The values are already multiplied
155 // with 100.
156 //
157 // Some examples (details are described in doc/dw-line-breaking.doc):
158 //
159 // 0 = Perfect line; as penalty used for normal spaces.
160 
161 // 1 (100 here) = A justified line with spaces having 150% or 67% of
162 // the ideal space width has this as badness.
163 //
164 // 8 (800 here) = A justified line with spaces twice as wide as
165 // ideally has this as badness.
166 //
167 // The second value is used when the line before ends with a hyphen,
168 // dash etc.
169 
170 int Textblock::penalties[PENALTY_NUM][2] = {
171  // Penalties for all hyphens.
172  { 100, 800 },
173  // Penalties for a break point *left* of an em-dash: rather large,
174  // so that a break on the *right* side is preferred.
175  { 800, 800 },
176  // Penalties for a break point *right* of an em-dash: like hyphens.
177  { 100, 800 }
178 };
179 
180 int Textblock::stretchabilityFactor = 100;
181 
190 const char *Textblock::hyphenDrawChar = "-";
191 
192 void Textblock::setPenaltyHyphen (int penaltyHyphen)
193 {
194  penalties[PENALTY_HYPHEN][0] = penaltyHyphen;
195 }
196 
197 void Textblock::setPenaltyHyphen2 (int penaltyHyphen2)
198 {
199  penalties[PENALTY_HYPHEN][1] = penaltyHyphen2;
200 }
201 
202 void Textblock::setPenaltyEmDashLeft (int penaltyLeftEmDash)
203 {
204  penalties[PENALTY_EM_DASH_LEFT][0] = penaltyLeftEmDash;
205  penalties[PENALTY_EM_DASH_LEFT][1] = penaltyLeftEmDash;
206 }
207 
208 void Textblock::setPenaltyEmDashRight (int penaltyRightEmDash)
209 {
210  penalties[PENALTY_EM_DASH_RIGHT][0] = penaltyRightEmDash;
211 }
212 
213 void Textblock::setPenaltyEmDashRight2 (int penaltyRightEmDash2)
214 {
215  penalties[PENALTY_EM_DASH_RIGHT][1] = penaltyRightEmDash2;
216 }
217 
218 void Textblock::setStretchabilityFactor (int stretchabilityFactor)
219 {
220  Textblock::stretchabilityFactor = stretchabilityFactor;
221 }
222 
223 Textblock::Textblock (bool limitTextWidth)
224 {
225  DBG_OBJ_CREATE ("dw::Textblock");
226  registerName ("dw::Textblock", &CLASS_ID);
227  setFlags (BLOCK_LEVEL);
228  setFlags (USES_HINTS);
229  setButtonSensitive(true);
230 
231  hasListitemValue = false;
232  innerPadding = 0;
233  line1Offset = 0;
234  ignoreLine1OffsetSometimes = false;
235  mustQueueResize = false;
236  redrawY = 0;
237  lastWordDrawn = -1;
238 
239  /*
240  * The initial sizes of lines and words should not be
241  * too high, since this will waste much memory with tables
242  * containing many small cells. The few more calls to realloc
243  * should not decrease the speed considerably.
244  * (Current setting is for minimal memory usage. An interesting fact
245  * is that high values decrease speed due to memory handling overhead!)
246  * TODO: Some tests would be useful.
247  */
248  paragraphs = new misc::SimpleVector <Paragraph> (1);
249  lines = new misc::SimpleVector <Line> (1);
250  nonTemporaryLines = 0;
251  words = new misc::NotSoSimpleVector <Word> (1);
252  anchors = new misc::SimpleVector <Anchor> (1);
253 
254  //DBG_OBJ_SET_NUM(this, "num_lines", num_lines);
255 
256  wrapRefLines = wrapRefParagraphs = -1;
257 
258  //DBG_OBJ_SET_NUM(this, "last_line_width", last_line_width);
259  //DBG_OBJ_SET_NUM(this, "last_line_par_min", last_line_par_min);
260  //DBG_OBJ_SET_NUM(this, "last_line_par_max", last_line_par_max);
261  //DBG_OBJ_SET_NUM(this, "wrap_ref", wrap_ref);
262 
263  hoverLink = -1;
264 
265  // random values
266  availWidth = 100;
267  availAscent = 100;
268  availDescent = 0;
269 
270  this->limitTextWidth = limitTextWidth;
271 
272  for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
273  /* hlStart[layer].index > hlEnd[layer].index means no highlighting */
274  hlStart[layer].index = 1;
275  hlStart[layer].nChar = 0;
276  hlEnd[layer].index = 0;
277  hlEnd[layer].nChar = 0;
278  }
279 }
280 
281 Textblock::~Textblock ()
282 {
283  _MSG("Textblock::~Textblock\n");
284 
285  /* make sure not to call a free'd tooltip (very fast overkill) */
286  hoverTooltip = NULL;
287 
288  for (int i = 0; i < words->size(); i++) {
289  Word *word = words->getRef (i);
290 
291  if (word->content.type == core::Content::WIDGET)
292  delete word->content.widget;
293 
294  removeWordImgRenderer (i);
295  removeSpaceImgRenderer (i);
296 
297  word->style->unref ();
298  word->spaceStyle->unref ();
299  }
300 
301  for (int i = 0; i < anchors->size(); i++) {
302  Anchor *anchor = anchors->getRef (i);
303  /* This also frees the names (see removeAnchor() and related). */
304  removeAnchor(anchor->name);
305  }
306 
307  delete paragraphs;
308  delete lines;
309  delete words;
310  delete anchors;
311 
312  /* Make sure we don't own widgets anymore. Necessary before call of
313  parent class destructor. (???) */
314  words = NULL;
315 
316  DBG_OBJ_DELETE ();
317 }
318 
324 void Textblock::sizeRequestImpl (core::Requisition *requisition)
325 {
326  PRINTF ("[%p] SIZE_REQUEST: ...\n", this);
327 
328  rewrap ();
329  showMissingLines ();
330 
331  if (lines->size () > 0) {
332  Line *lastLine = lines->getRef (lines->size () - 1);
333  requisition->width = lastLine->maxLineWidth;
334 
335  PRINTF ("[%p] SIZE_REQUEST: lastLine->maxLineWidth = %d\n",
336  this, lastLine->maxLineWidth);
337 
338  PRINTF ("[%p] SIZE_REQUEST: lines[0]->boxAscent = %d\n",
339  this, lines->getRef(0)->boxAscent);
340  PRINTF ("[%p] SIZE_REQUEST: lines[%d]->top = %d\n",
341  this, lines->size () - 1, lastLine->top);
342  PRINTF ("[%p] SIZE_REQUEST: lines[%d]->boxAscent = %d\n",
343  this, lines->size () - 1, lastLine->boxAscent);
344  PRINTF ("[%p] SIZE_REQUEST: lines[%d]->boxDescent = %d\n",
345  this, lines->size () - 1, lastLine->boxDescent);
346 
347  /* Note: the breakSpace of the last line is ignored, so breaks
348  at the end of a textblock are not visible. */
349  requisition->ascent = lines->getRef(0)->boxAscent;
350  requisition->descent = lastLine->top
351  + lastLine->boxAscent + lastLine->boxDescent -
352  lines->getRef(0)->boxAscent;
353  } else {
354  requisition->width = 0; // before: lastLineWidth;
355  requisition->ascent = 0;
356  requisition->descent = 0;
357  }
358 
359  PRINTF ("[%p] SIZE_REQUEST: inner padding = %d, boxDiffWidth = %d\n",
360  this, innerPadding, getStyle()->boxDiffWidth ());
361 
362  requisition->width += innerPadding + getStyle()->boxDiffWidth ();
363  requisition->ascent += getStyle()->boxOffsetY ();
364  requisition->descent += getStyle()->boxRestHeight ();
365 
366  if (requisition->width < availWidth)
367  requisition->width = availWidth;
368 
369  PRINTF ("[%p] SIZE_REQUEST: %d x %d + %d\n", this, requisition->width,
370  requisition->ascent, requisition->descent);
371 }
372 
376 void Textblock::getWordExtremes (Word *word, core::Extremes *extremes)
377 {
378  if (word->content.type == core::Content::WIDGET) {
379  if (word->content.widget->usesHints ())
380  word->content.widget->getExtremes (extremes);
381  else {
383  (word->content.widget->getStyle()->width)) {
384  extremes->minWidth = 0;
385  if (word->content.widget->hasContents ())
386  extremes->maxWidth = 1000000;
387  else
388  extremes->maxWidth = 0;
389  } else if (core::style::isAbsLength
390  (word->content.widget->getStyle()->width)) {
391  /* Fixed lengths are only applied to the content, so we have to
392  * add padding, border and margin. */
393  extremes->minWidth = extremes->maxWidth =
395  ->width)
396  + word->style->boxDiffWidth ();
397  } else
398  word->content.widget->getExtremes (extremes);
399  }
400  } else {
401  extremes->minWidth = word->size.width;
402  extremes->maxWidth = word->size.width;
403  }
404 }
405 
406 void Textblock::getExtremesImpl (core::Extremes *extremes)
407 {
408  PRINTF ("[%p] GET_EXTREMES ...\n", this);
409 
410  fillParagraphs ();
411 
412  if (paragraphs->size () == 0) {
413  /* empty page */
414  extremes->minWidth = 0;
415  extremes->maxWidth = 0;
416  } else {
417  Paragraph *lastPar = paragraphs->getLastRef ();
418  extremes->minWidth = lastPar->maxParMin;
419  extremes->maxWidth = lastPar->maxParMax;
420  }
421 
422  int diff = innerPadding + getStyle()->boxDiffWidth ();
423  extremes->minWidth += diff;
424  extremes->maxWidth += diff;
425 
426  PRINTF ("[%p] GET_EXTREMES => %d / %d\n",
427  this, extremes->minWidth, extremes->maxWidth);
428 }
429 
430 
431 void Textblock::sizeAllocateImpl (core::Allocation *allocation)
432 {
433  PRINTF ("[%p] SIZE_ALLOCATE: %d, %d, %d x %d + %d\n",
434  this, allocation->x, allocation->y, allocation->width,
435  allocation->ascent, allocation->descent);
436 
437  showMissingLines ();
438 
439  int lineIndex, wordIndex;
440  Line *line;
441  Word *word;
442  int xCursor;
443  core::Allocation childAllocation;
444  core::Allocation *oldChildAllocation;
445 
446  if (allocation->width != this->allocation.width) {
447  redrawY = 0;
448  }
449 
450  for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) {
451  line = lines->getRef (lineIndex);
452  xCursor = lineXOffsetWidget (line);
453 
454  for (wordIndex = line->firstWord; wordIndex <= line->lastWord;
455  wordIndex++) {
456  word = words->getRef (wordIndex);
457 
458  if (wordIndex == lastWordDrawn + 1) {
459  redrawY = misc::min (redrawY, lineYOffsetWidget (line));
460  }
461 
462  if (word->content.type == core::Content::WIDGET) {
464  childAllocation.x = xCursor + allocation->x;
465  /* align=top:
466  childAllocation.y = line->top + allocation->y;
467  */
468 
469  /* align=bottom (base line) */
470  /* Commented lines break the n2 and n3 test cases at
471  * http://www.dillo.org/test/img/ */
472  childAllocation.y =
473  lineYOffsetCanvasAllocation (line, allocation)
474  + (line->boxAscent - word->size.ascent)
475  - word->content.widget->getStyle()->margin.top;
476  childAllocation.width = word->size.width;
477  childAllocation.ascent = word->size.ascent
478  + word->content.widget->getStyle()->margin.top;
479  childAllocation.descent = word->size.descent
480  + word->content.widget->getStyle()->margin.bottom;
481 
482  oldChildAllocation = word->content.widget->getAllocation();
483 
484  if (childAllocation.x != oldChildAllocation->x ||
485  childAllocation.y != oldChildAllocation->y ||
486  childAllocation.width != oldChildAllocation->width) {
487  /* The child widget has changed its position or its width
488  * so we need to redraw from this line onwards.
489  */
490  redrawY = misc::min (redrawY, lineYOffsetWidget (line));
491  if (word->content.widget->wasAllocated ()) {
492  redrawY = misc::min (redrawY,
493  oldChildAllocation->y - this->allocation.y);
494  }
495 
496  } else if (childAllocation.ascent + childAllocation.descent !=
497  oldChildAllocation->ascent + oldChildAllocation->descent) {
498  /* The child widget has changed its height. We need to redraw
499  * from where it changed.
500  * It's important not to draw from the line base, because the
501  * child might be a table covering the whole page so we would
502  * end up redrawing the whole screen over and over.
503  * The drawing of the child content is left to the child itself.
504  * However this optimization is only possible if the widget is
505  * the only word in the line apart from an optional BREAK.
506  * Otherwise the height change of the widget could change the
507  * position of other words in the line, requiring a
508  * redraw of the complete line.
509  */
510  if (line->lastWord == line->firstWord ||
511  (line->lastWord == line->firstWord + 1 &&
512  words->getRef (line->lastWord)->content.type ==
513  core::Content::BREAK)) {
514 
515  int childChangedY =
516  misc::min(childAllocation.y - allocation->y +
517  childAllocation.ascent + childAllocation.descent,
518  oldChildAllocation->y - this->allocation.y +
519  oldChildAllocation->ascent +
520  oldChildAllocation->descent);
521 
522  redrawY = misc::min (redrawY, childChangedY);
523  } else {
524  redrawY = misc::min (redrawY, lineYOffsetWidget (line));
525  }
526  }
527  word->content.widget->sizeAllocate (&childAllocation);
528  }
529 
530  xCursor += (word->size.width + word->effSpace);
531  }
532  }
533 
534  for (int i = 0; i < anchors->size(); i++) {
535  Anchor *anchor = anchors->getRef(i);
536  int y;
537 
538  if (anchor->wordIndex >= words->size()) {
539  y = allocation->y + allocation->ascent + allocation->descent;
540  } else {
541  Line *line = lines->getRef(findLineOfWord (anchor->wordIndex));
542  y = lineYOffsetCanvasAllocation (line, allocation);
543  }
544  changeAnchor (anchor->name, y);
545  }
546 }
547 
548 void Textblock::resizeDrawImpl ()
549 {
550  queueDrawArea (0, redrawY, allocation.width, getHeight () - redrawY);
551  if (lines->size () > 0) {
552  Line *lastLine = lines->getRef (lines->size () - 1);
553  /* Remember the last word that has been drawn so we can ensure to
554  * draw any new added words (see sizeAllocateImpl()).
555  */
556  lastWordDrawn = lastLine->lastWord;
557  }
558 
559  redrawY = getHeight ();
560 }
561 
562 void Textblock::markSizeChange (int ref)
563 {
564  PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", this, ref, wrapRefLines);
565 
566  /* By the way: ref == -1 may have two different causes: (i) flush()
567  calls "queueResize (-1, true)", when no rewrapping is necessary;
568  and (ii) a word may have parentRef == -1 , when it is not yet
569  added to a line. In the latter case, nothing has to be done
570  now, but addLine(...) will do everything necessary. */
571  if (ref != -1) {
572  if (wrapRefLines == -1)
573  wrapRefLines = ref;
574  else
575  wrapRefLines = misc::min (wrapRefLines, ref);
576  }
577 
578  PRINTF (" ... => %d\n", wrapRefLine);
579 
580  // It seems that sometimes the lines structure is changed, so that
581  // wrapRefLines may refers to a line which does not exist
582  // anymore. Should be examined again. Until then, setting
583  // wrapRefLines to the same value is a workaround.
584  markExtremesChange (ref);
585 }
586 
587 void Textblock::markExtremesChange (int ref)
588 {
589  PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n",
590  this, ref, wrapRefParagraphs);
591 
592  /* By the way: ref == -1 may have two different causes: (i) flush()
593  calls "queueResize (-1, true)", when no rewrapping is necessary;
594  and (ii) a word may have parentRef == -1 , when it is not yet
595  added to a line. In the latter case, nothing has to be done
596  now, but addLine(...) will do everything necessary. */
597  if (ref != -1) {
598  if (wrapRefParagraphs == -1)
599  wrapRefParagraphs = ref;
600  else
601  wrapRefParagraphs = misc::min (wrapRefParagraphs, ref);
602  }
603 
604  PRINTF (" ... => %d\n", wrapRefParagraphs);
605 }
606 
607 void Textblock::setWidth (int width)
608 {
609  /* If limitTextWidth is set to YES, a queueResize() may also be
610  * necessary. */
611  if (availWidth != width || limitTextWidth) {
612  //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
613  // "setWidth: Calling queueResize, "
614  // "in page with %d word(s)\n",
615  // words->size());
616 
617  availWidth = width;
618  queueResize (0, false);
619  mustQueueResize = false;
620  redrawY = 0;
621  }
622 }
623 
624 void Textblock::setAscent (int ascent)
625 {
626  if (availAscent != ascent) {
627  //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
628  // "setAscent: Calling queueResize, "
629  // "in page with %d word(s)\n",
630  // words->size());
631 
632  availAscent = ascent;
633  queueResize (0, false);
634  mustQueueResize = false;
635  }
636 }
637 
638 void Textblock::setDescent (int descent)
639 {
640  if (availDescent != descent) {
641  //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
642  // "setDescent: Calling queueResize, "
643  // "in page with %d word(s)\n",
644  // words->size());
645 
646  availDescent = descent;
647  queueResize (0, false);
648  mustQueueResize = false;
649  }
650 }
651 
652 bool Textblock::buttonPressImpl (core::EventButton *event)
653 {
654  return sendSelectionEvent (core::SelectionState::BUTTON_PRESS, event);
655 }
656 
657 bool Textblock::buttonReleaseImpl (core::EventButton *event)
658 {
659  return sendSelectionEvent (core::SelectionState::BUTTON_RELEASE, event);
660 }
661 
662 /*
663  * Handle motion inside the widget
664  * (special care is necessary when switching from another widget,
665  * because hoverLink and hoverTooltip are meaningless then).
666  */
667 bool Textblock::motionNotifyImpl (core::EventMotion *event)
668 {
669  if (event->state & core::BUTTON1_MASK)
670  return sendSelectionEvent (core::SelectionState::BUTTON_MOTION, event);
671  else {
672  bool inSpace;
673  int linkOld = hoverLink;
674  core::style::Tooltip *tooltipOld = hoverTooltip;
675  const Word *word = findWord (event->xWidget, event->yWidget, &inSpace);
676 
677  // cursor from word or widget style
678  if (word == NULL) {
679  setCursor (getStyle()->cursor);
680  hoverLink = -1;
681  hoverTooltip = NULL;
682  } else {
683  core::style::Style *style = inSpace ? word->spaceStyle : word->style;
684  setCursor (style->cursor);
685  hoverLink = style->x_link;
686  hoverTooltip = style->x_tooltip;
687  }
688 
689  // Show/hide tooltip
690  if (tooltipOld != hoverTooltip) {
691  if (tooltipOld)
692  tooltipOld->onLeave ();
693  if (hoverTooltip)
694  hoverTooltip->onEnter ();
695  } else if (hoverTooltip)
697 
698  _MSG("MN tb=%p tooltipOld=%p hoverTooltip=%p\n",
699  this, tooltipOld, hoverTooltip);
700  if (hoverLink != linkOld) {
701  /* LinkEnter with hoverLink == -1 is the same as LinkLeave */
702  return layout->emitLinkEnter (this, hoverLink, -1, -1, -1);
703  } else {
704  return hoverLink != -1;
705  }
706  }
707 }
708 
709 void Textblock::enterNotifyImpl (core::EventCrossing *event)
710 {
711  _MSG(" tb=%p, ENTER NotifyImpl hoverTooltip=%p\n", this, hoverTooltip);
712  /* reset hoverLink so linkEnter is detected */
713  hoverLink = -2;
714 }
715 
716 void Textblock::leaveNotifyImpl (core::EventCrossing *event)
717 {
718  _MSG(" tb=%p, LEAVE NotifyImpl: hoverTooltip=%p\n", this, hoverTooltip);
719 
720  /* leaving the viewport can't be handled by motionNotifyImpl() */
721  if (hoverLink >= 0)
722  layout->emitLinkEnter (this, -1, -1, -1, -1);
723 
724  if (hoverTooltip) {
726  hoverTooltip = NULL;
727  }
728 }
729 
733 bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
735 {
736  core::Iterator *it;
737  int wordIndex;
738  int charPos = 0, link = -1;
739  bool r;
740 
741  if (words->size () == 0) {
742  wordIndex = -1;
743  } else {
744  Line *lastLine = lines->getRef (lines->size () - 1);
745  int yFirst = lineYOffsetCanvasI (0);
746  int yLast = lineYOffsetCanvas (lastLine) + lastLine->boxAscent +
747  lastLine->boxDescent;
748  if (event->yCanvas < yFirst) {
749  // Above the first line: take the first word.
750  wordIndex = 0;
751  } else if (event->yCanvas >= yLast) {
752  // Below the last line: take the last word.
753  wordIndex = words->size () - 1;
754  charPos = core::SelectionState::END_OF_WORD;
755  } else {
756  Line *line = lines->getRef (findLineIndex (event->yWidget));
757 
758  // Pointer within the break space?
759  if (event->yWidget >
760  (lineYOffsetWidget (line) + line->boxAscent + line->boxDescent)) {
761  // Choose this break.
762  wordIndex = line->lastWord;
763  charPos = core::SelectionState::END_OF_WORD;
764  } else if (event->xWidget < lineXOffsetWidget (line)) {
765  // Left of the first word in the line.
766  wordIndex = line->firstWord;
767  } else {
768  int nextWordStartX = lineXOffsetWidget (line);
769 
770  for (wordIndex = line->firstWord;
771  wordIndex <= line->lastWord;
772  wordIndex++) {
773  Word *word = words->getRef (wordIndex);
774  int wordStartX = nextWordStartX;
775 
776  nextWordStartX += word->size.width + word->effSpace;
777 
778  if (event->xWidget >= wordStartX &&
779  event->xWidget < nextWordStartX) {
780  // We have found the word.
781  int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
782 
783  if (event->xWidget >= nextWordStartX - word->effSpace) {
784  charPos = core::SelectionState::END_OF_WORD;
785  if (wordIndex < line->lastWord &&
786  (words->getRef(wordIndex + 1)->content.type !=
787  core::Content::BREAK) &&
788  (event->yWidget <=
789  yWidgetBase + word->spaceStyle->font->descent) &&
790  (event->yWidget >
791  yWidgetBase - word->spaceStyle->font->ascent)) {
792  link = word->spaceStyle->x_link;
793  }
794  } else {
795  if (event->yWidget <= yWidgetBase + word->size.descent &&
796  event->yWidget > yWidgetBase - word->size.ascent) {
797  link = word->style->x_link;
798  }
799  if (word->content.type == core::Content::TEXT) {
800  int glyphX = wordStartX;
801  int isStartWord = word->flags & Word::WORD_START;
802  int isEndWord = word->flags & Word::WORD_END;
803 
804  while (1) {
805  int nextCharPos =
806  layout->nextGlyph (word->content.text, charPos);
807  // TODO The width of a text is not the sum
808  // of the widths of the glyphs, because of
809  // ligatures, kerning etc., so textWidth
810  // should be applied to the text from 0 to
811  // nextCharPos. (Or not? See comment below.)
812 
813  int glyphWidth =
814  textWidth (word->content.text, charPos,
815  nextCharPos - charPos, word->style,
816  isStartWord && charPos == 0,
817  isEndWord &&
818  word->content.text[nextCharPos] == 0);
819  if (event->xWidget > glyphX + glyphWidth) {
820  glyphX += glyphWidth;
821  charPos = nextCharPos;
822  continue;
823  } else if (event->xWidget >= glyphX + glyphWidth/2){
824  // On the right half of a character;
825  // now just look for combining chars
826  charPos = nextCharPos;
827  while (word->content.text[charPos]) {
828  nextCharPos =
829  layout->nextGlyph (word->content.text,
830  charPos);
831  if (textWidth (word->content.text, charPos,
832  nextCharPos - charPos,
833  word->style,
834  isStartWord && charPos == 0,
835  isEndWord &&
836  word->content.text[nextCharPos]
837  == 0))
838  break;
839  charPos = nextCharPos;
840  }
841  }
842  break;
843  }
844  } else {
845  // Depends on whether the pointer is within the left or
846  // right half of the (non-text) word.
847  if (event->xWidget >= (wordStartX + nextWordStartX) /2)
848  charPos = core::SelectionState::END_OF_WORD;
849  }
850  }
851  break;
852  }
853  }
854  if (wordIndex > line->lastWord) {
855  // No word found in this line (i.e. we are on the right side),
856  // take the last of this line.
857  wordIndex = line->lastWord;
858  charPos = core::SelectionState::END_OF_WORD;
859  }
860  }
861  }
862  }
863  it = new TextblockIterator (this, core::Content::SELECTION_CONTENT,
864  wordIndex);
865  r = selectionHandleEvent (eventType, it, charPos, link, event);
866  it->unref ();
867  return r;
868 }
869 
870 void Textblock::removeChild (Widget *child)
871 {
873 }
874 
875 core::Iterator *Textblock::iterator (core::Content::Type mask, bool atEnd)
876 {
877  return new TextblockIterator (this, mask, atEnd);
878 }
879 
880 
885 void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size)
886 {
887  core::Requisition requisition;
888  int availWidth, availAscent, availDescent;
889  core::style::Style *wstyle = widget->getStyle();
890 
891  /* We ignore line1_offset[_eff]. */
892  availWidth = this->availWidth - getStyle()->boxDiffWidth () - innerPadding;
893  availAscent = this->availAscent - getStyle()->boxDiffHeight ();
894  availDescent = this->availDescent;
895 
896  if (widget->usesHints ()) {
897  widget->setWidth (availWidth);
898  widget->setAscent (availAscent);
899  widget->setDescent (availDescent);
900  widget->sizeRequest (size);
901  } else {
902  if (wstyle->width == core::style::LENGTH_AUTO ||
903  wstyle->height == core::style::LENGTH_AUTO)
904  widget->sizeRequest (&requisition);
905 
906  if (wstyle->width == core::style::LENGTH_AUTO)
907  size->width = requisition.width;
908  else if (core::style::isAbsLength (wstyle->width))
909  /* Fixed lengths are only applied to the content, so we have to
910  * add padding, border and margin. */
911  size->width = core::style::absLengthVal (wstyle->width)
912  + wstyle->boxDiffWidth ();
913  else
914  size->width =
915  core::style::multiplyWithPerLength (availWidth, wstyle->width);
916 
917  if (wstyle->height == core::style::LENGTH_AUTO) {
918  size->ascent = requisition.ascent;
919  size->descent = requisition.descent;
920  } else if (core::style::isAbsLength (wstyle->height)) {
921  /* Fixed lengths are only applied to the content, so we have to
922  * add padding, border and margin. */
923  size->ascent = core::style::absLengthVal (wstyle->height)
924  + wstyle->boxDiffHeight ();
925  size->descent = 0;
926  } else {
927  size->ascent =
928  core::style::multiplyWithPerLength (wstyle->height, availAscent);
929  size->descent =
930  core::style::multiplyWithPerLength (wstyle->height, availDescent);
931  }
932  }
933 
934  /* ascent and descent in words do not contain margins. */
935  size->ascent -= wstyle->margin.top;
936  size->descent -= wstyle->margin.bottom;
937 }
938 
939 /*
940  * Draw the decorations on a word.
941  */
942 void Textblock::decorateText(core::View *view, core::style::Style *style,
944  int x, int yBase, int width)
945 {
946  int y, height;
947 
948  height = 1 + style->font->xHeight / 12;
950  y = yBase + style->font->descent / 3;
951  view->drawRectangle (style->color, shading, true, x, y, width, height);
952  }
954  y = yBase - style->font->ascent;
955  view->drawRectangle (style->color, shading, true, x, y, width, height);
956  }
958  y = yBase + (style->font->descent - style->font->ascent) / 2 +
959  style->font->descent / 4;
960  view->drawRectangle (style->color, shading, true, x, y, width, height);
961  }
962 }
963 
964 /*
965  * Draw a string of text
966  *
967  * Arguments: ... "isStart" and "isEnd" are true, when the text
968  * start/end represents the start/end of a "real" text word (before
969  * hyphenation). This has an effect on text transformation. ("isEnd"
970  * is not used yet, but here for symmetry.)
971  */
972 void Textblock::drawText(core::View *view, core::style::Style *style,
973  core::style::Color::Shading shading, int x, int y,
974  const char *text, int start, int len, bool isStart,
975  bool isEnd)
976 {
977  if (len > 0) {
978  char *str = NULL;
979 
980  switch (style->textTransform) {
982  default:
983  break;
985  str = layout->textToUpper(text + start, len);
986  break;
988  str = layout->textToLower(text + start, len);
989  break;
991  // If "isStart" is false, the first letter of "text" is
992  // not the first letter of the "real" text word, so no
993  // transformation is necessary.
994  if (isStart) {
995  /* \bug No way to know about non-ASCII punctuation. */
996  bool initial_seen = false;
997 
998  for (int i = 0; i < start; i++)
999  if (!ispunct(text[i]))
1000  initial_seen = true;
1001  if (initial_seen)
1002  break;
1003 
1004  int after = 0;
1005  text += start;
1006  while (ispunct(text[after]))
1007  after++;
1008  if (text[after])
1009  after = layout->nextGlyph(text, after);
1010  if (after > len)
1011  after = len;
1012 
1013  char *initial = layout->textToUpper(text, after);
1014  int newlen = strlen(initial) + len-after;
1015  str = (char *)malloc(newlen + 1);
1016  strcpy(str, initial);
1017  strncpy(str + strlen(str), text+after, len-after);
1018  str[newlen] = '\0';
1019  free(initial);
1020  }
1021  break;
1022  }
1023 
1024  view->drawText(style->font, style->color, shading, x, y,
1025  str ? str : text + start, str ? strlen(str) : len);
1026  if (str)
1027  free(str);
1028  }
1029 }
1030 
1039 void Textblock::drawWord (Line *line, int wordIndex1, int wordIndex2,
1040  core::View *view, core::Rectangle *area,
1041  int xWidget, int yWidgetBase)
1042 {
1043  core::style::Style *style = words->getRef(wordIndex1)->style;
1044  bool drawHyphen = wordIndex2 == line->lastWord
1045  && (words->getRef(wordIndex2)->flags & Word::DIV_CHAR_AT_EOL);
1046 
1047  if (style->hasBackground ()) {
1048  int w = 0;
1049  for (int i = wordIndex1; i <= wordIndex2; i++)
1050  w += words->getRef(i)->size.width;
1051  w += words->getRef(wordIndex2)->hyphenWidth;
1052  drawBox (view, style, area, xWidget, yWidgetBase - line->boxAscent,
1053  w, line->boxAscent + line->boxDescent, false);
1054  }
1055 
1056  if (wordIndex1 == wordIndex2 && !drawHyphen) {
1057  // Simple case, where copying in one buffer is not needed.
1058  Word *word = words->getRef (wordIndex1);
1059  drawWord0 (wordIndex1, wordIndex2, word->content.text, word->size.width,
1060  false, style, view, area, xWidget, yWidgetBase);
1061  } else {
1062  // Concatenate all words in a new buffer.
1063  int l = 0, totalWidth = 0;
1064  for (int i = wordIndex1; i <= wordIndex2; i++) {
1065  Word *w = words->getRef (i);
1066  l += strlen (w->content.text);
1067  totalWidth += w->size.width;
1068  }
1069 
1070  char text[l + (drawHyphen ? strlen (hyphenDrawChar) : 0) + 1];
1071  int p = 0;
1072  for (int i = wordIndex1; i <= wordIndex2; i++) {
1073  const char * t = words->getRef(i)->content.text;
1074  strcpy (text + p, t);
1075  p += strlen (t);
1076  }
1077 
1078  if(drawHyphen) {
1079  for (int i = 0; hyphenDrawChar[i]; i++)
1080  text[p++] = hyphenDrawChar[i];
1081  text[p++] = 0;
1082  }
1083 
1084  drawWord0 (wordIndex1, wordIndex2, text, totalWidth, drawHyphen,
1085  style, view, area, xWidget, yWidgetBase);
1086  }
1087 }
1088 
1092 void Textblock::drawWord0 (int wordIndex1, int wordIndex2,
1093  const char *text, int totalWidth, bool drawHyphen,
1094  core::style::Style *style, core::View *view,
1095  core::Rectangle *area, int xWidget, int yWidgetBase)
1096 {
1097  int xWorld = allocation.x + xWidget;
1098  int yWorldBase;
1099 
1100  /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */
1101  if (style->valign == core::style::VALIGN_SUB)
1102  yWidgetBase += style->font->ascent / 3;
1103  else if (style->valign == core::style::VALIGN_SUPER) {
1104  yWidgetBase -= style->font->ascent / 2;
1105  }
1106  yWorldBase = yWidgetBase + allocation.y;
1107 
1108  bool isStartTotal = words->getRef(wordIndex1)->flags & Word::WORD_START;
1109  bool isEndTotal = words->getRef(wordIndex2)->flags & Word::WORD_START;
1110  drawText (view, style, core::style::Color::SHADING_NORMAL, xWorld,
1111  yWorldBase, text, 0, strlen (text), isStartTotal, isEndTotal);
1112 
1113  if (style->textDecoration)
1114  decorateText(view, style, core::style::Color::SHADING_NORMAL, xWorld,
1115  yWorldBase, totalWidth);
1116 
1117  for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
1118  if (wordIndex1 <= hlEnd[layer].index &&
1119  wordIndex2 >= hlStart[layer].index) {
1120  const int wordLen = strlen (text);
1121  int xStart, width;
1122  int firstCharIdx;
1123  int lastCharIdx;
1124 
1125  if (hlStart[layer].index < wordIndex1)
1126  firstCharIdx = 0;
1127  else {
1128  firstCharIdx =
1129  misc::min (hlStart[layer].nChar,
1130  (int)strlen (words->getRef(hlStart[layer].index)
1131  ->content.text));
1132  for (int i = wordIndex1; i < hlStart[layer].index; i++)
1133  // It can be assumed that all words from wordIndex1 to
1134  // wordIndex2 have content type TEXT.
1135  firstCharIdx += strlen (words->getRef(i)->content.text);
1136  }
1137 
1138  if (hlEnd[layer].index > wordIndex2)
1139  lastCharIdx = wordLen;
1140  else {
1141  lastCharIdx =
1142  misc::min (hlEnd[layer].nChar,
1143  (int)strlen (words->getRef(hlEnd[layer].index)
1144  ->content.text));
1145  for (int i = wordIndex1; i < hlEnd[layer].index; i++)
1146  // It can be assumed that all words from wordIndex1 to
1147  // wordIndex2 have content type TEXT.
1148  lastCharIdx += strlen (words->getRef(i)->content.text);
1149  }
1150 
1151  xStart = xWorld;
1152  if (firstCharIdx)
1153  xStart += textWidth (text, 0, firstCharIdx, style,
1154  isStartTotal,
1155  isEndTotal && text[firstCharIdx] == 0);
1156  // With a hyphen, the width is a bit longer than totalWidth,
1157  // and so, the optimization to use totalWidth is not correct.
1158  if (!drawHyphen && firstCharIdx == 0 && lastCharIdx == wordLen)
1159  width = totalWidth;
1160  else
1161  width = textWidth (text, firstCharIdx,
1162  lastCharIdx - firstCharIdx, style,
1163  isStartTotal && firstCharIdx == 0,
1164  isEndTotal && text[lastCharIdx] == 0);
1165  if (width > 0) {
1166  /* Highlight text */
1167  core::style::Color *wordBgColor;
1168 
1169  if (!(wordBgColor = style->backgroundColor))
1170  wordBgColor = getBgColor();
1171 
1172  /* Draw background for highlighted text. */
1173  view->drawRectangle (
1174  wordBgColor, core::style::Color::SHADING_INVERSE, true, xStart,
1175  yWorldBase - style->font->ascent, width,
1176  style->font->ascent + style->font->descent);
1177 
1178  /* Highlight the text. */
1179  drawText (view, style, core::style::Color::SHADING_INVERSE, xStart,
1180  yWorldBase, text, firstCharIdx,
1181  lastCharIdx - firstCharIdx,
1182  isStartTotal && firstCharIdx == 0,
1183  isEndTotal && lastCharIdx == wordLen);
1184 
1185  if (style->textDecoration)
1186  decorateText(view, style, core::style::Color::SHADING_INVERSE,
1187  xStart, yWorldBase, width);
1188  }
1189  }
1190  }
1191 }
1192 
1193 /*
1194  * Draw a space.
1195  */
1196 void Textblock::drawSpace(int wordIndex, core::View *view,
1197  core::Rectangle *area, int xWidget, int yWidgetBase)
1198 {
1199  Word *word = words->getRef(wordIndex);
1200  int xWorld = allocation.x + xWidget;
1201  int yWorldBase;
1202  core::style::Style *style = word->spaceStyle;
1203  bool highlight = false;
1204 
1205  /* Adjust the space baseline if it is <SUP>-ed or <SUB>-ed */
1206  if (style->valign == core::style::VALIGN_SUB)
1207  yWidgetBase += style->font->ascent / 3;
1208  else if (style->valign == core::style::VALIGN_SUPER) {
1209  yWidgetBase -= style->font->ascent / 2;
1210  }
1211  yWorldBase = allocation.y + yWidgetBase;
1212 
1213  for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
1214  if (hlStart[layer].index <= wordIndex &&
1215  hlEnd[layer].index > wordIndex) {
1216  highlight = true;
1217  break;
1218  }
1219  }
1220  if (highlight) {
1221  core::style::Color *spaceBgColor;
1222 
1223  if (!(spaceBgColor = style->backgroundColor))
1224  spaceBgColor = getBgColor();
1225 
1226  view->drawRectangle (
1227  spaceBgColor, core::style::Color::SHADING_INVERSE, true, xWorld,
1228  yWorldBase - style->font->ascent, word->effSpace,
1229  style->font->ascent + style->font->descent);
1230  }
1231  if (style->textDecoration) {
1232  core::style::Color::Shading shading = highlight ?
1233  core::style::Color::SHADING_INVERSE :
1234  core::style::Color::SHADING_NORMAL;
1235 
1236  decorateText(view, style, shading, xWorld, yWorldBase, word->effSpace);
1237  }
1238 }
1239 
1240 /*
1241  * Paint a line
1242  * - x and y are toplevel dw coordinates (Question: what Dw? Changed. Test!)
1243  * - area is used always (ev. set it to event->area)
1244  * - event is only used when is_expose
1245  */
1246 void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
1247 {
1248  int xWidget = lineXOffsetWidget(line);
1249  int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
1250 
1251  for (int wordIndex = line->firstWord;
1252  wordIndex <= line->lastWord && xWidget < area->x + area->width;
1253  wordIndex++) {
1254  Word *word = words->getRef(wordIndex);
1255  int wordSize = word->size.width;
1256 
1257  if (xWidget + wordSize + word->hyphenWidth + word->effSpace >= area->x) {
1258  if (word->content.type == core::Content::TEXT ||
1259  word->content.type == core::Content::WIDGET) {
1260 
1261  if (word->size.width > 0) {
1262  if (word->content.type == core::Content::WIDGET) {
1263  core::Widget *child = word->content.widget;
1264  core::Rectangle childArea;
1265 
1266  if (child->intersects (area, &childArea))
1267  child->draw (view, &childArea);
1268  } else {
1269  int wordIndex2 = wordIndex;
1270  while (wordIndex2 < line->lastWord &&
1271  (words->getRef(wordIndex2)->flags
1272  & Word::DRAW_AS_ONE_TEXT) &&
1273  word->style == words->getRef(wordIndex2 + 1)->style)
1274  wordIndex2++;
1275 
1276  drawWord(line, wordIndex, wordIndex2, view, area,
1277  xWidget, yWidgetBase);
1278  wordSize = 0;
1279  for (int i = wordIndex; i <= wordIndex2; i++)
1280  wordSize += words->getRef(i)->size.width;
1281 
1282  wordIndex = wordIndex2;
1283  word = words->getRef(wordIndex);
1284  }
1285  }
1286 
1287  if (word->effSpace > 0 && wordIndex < line->lastWord &&
1288  words->getRef(wordIndex + 1)->content.type !=
1289  core::Content::BREAK) {
1290  if (word->spaceStyle->hasBackground ())
1291  drawBox (view, word->spaceStyle, area,
1292  xWidget + wordSize,
1293  yWidgetBase - line->boxAscent, word->effSpace,
1294  line->boxAscent + line->boxDescent, false);
1295  drawSpace(wordIndex, view, area, xWidget + wordSize,
1296  yWidgetBase);
1297  }
1298 
1299  }
1300  }
1301  xWidget += wordSize + word->effSpace;
1302  }
1303 }
1304 
1308 int Textblock::findLineIndex (int y)
1309 {
1310  int maxIndex = lines->size () - 1;
1311  int step, index, low = 0;
1312 
1313  step = (lines->size() + 1) >> 1;
1314  while ( step > 1 ) {
1315  index = low + step;
1316  if (index <= maxIndex &&
1317  lineYOffsetWidgetI (index) <= y)
1318  low = index;
1319  step = (step + 1) >> 1;
1320  }
1321 
1322  if (low < maxIndex && lineYOffsetWidgetI (low + 1) <= y)
1323  low++;
1324 
1325  /*
1326  * This new routine returns the line number between (top) and
1327  * (top + size.ascent + size.descent + breakSpace): the space
1328  * _below_ the line is considered part of the line. Old routine
1329  * returned line number between (top - previous_line->breakSpace)
1330  * and (top + size.ascent + size.descent): the space _above_ the
1331  * line was considered part of the line. This is important for
1332  * Dw_page_find_link() --EG
1333  * That function has now been inlined into Dw_page_motion_notify() --JV
1334  */
1335  return low;
1336 }
1337 
1341 int Textblock::findLineOfWord (int wordIndex)
1342 {
1343  int high = lines->size () - 1, index, low = 0;
1344 
1345  // TODO regard also not-yet-existing lines?
1346  if (wordIndex < 0 || wordIndex >= words->size ())
1347  return -1;
1348 
1349  while (true) {
1350  index = (low + high) / 2;
1351  if (wordIndex >= lines->getRef(index)->firstWord) {
1352  if (wordIndex <= lines->getRef(index)->lastWord)
1353  return index;
1354  else
1355  low = index + 1;
1356  } else
1357  high = index - 1;
1358  }
1359 }
1360 
1364 int Textblock::findParagraphOfWord (int wordIndex)
1365 {
1366  int high = paragraphs->size () - 1, index, low = 0;
1367 
1368  if (wordIndex < 0 || wordIndex >= words->size () ||
1369  // It may be that the paragraphs list is incomplete. But look
1370  // also at fillParagraphs, where this method is called.
1371  (paragraphs->size () > 0 &&
1372  wordIndex > paragraphs->getLastRef()->lastWord))
1373  return -1;
1374 
1375  while (true) {
1376  index = (low + high) / 2;
1377  if (wordIndex >= paragraphs->getRef(index)->firstWord) {
1378  if (wordIndex <= paragraphs->getRef(index)->lastWord)
1379  return index;
1380  else
1381  low = index + 1;
1382  } else
1383  high = index - 1;
1384  }
1385 }
1386 
1390 Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace)
1391 {
1392  int lineIndex, wordIndex;
1393  int xCursor, lastXCursor, yWidgetBase;
1394  Line *line;
1395  Word *word;
1396 
1397  *inSpace = false;
1398 
1399  if ((lineIndex = findLineIndex (y)) >= lines->size ())
1400  return NULL;
1401  line = lines->getRef (lineIndex);
1402  yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
1403  if (yWidgetBase + line->boxDescent <= y)
1404  return NULL;
1405 
1406  xCursor = lineXOffsetWidget (line);
1407  for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
1408  word = words->getRef (wordIndex);
1409  lastXCursor = xCursor;
1410  xCursor += word->size.width + word->effSpace;
1411  if (lastXCursor <= x && xCursor > x) {
1412  if (x >= xCursor - word->effSpace) {
1413  if (wordIndex < line->lastWord &&
1414  (words->getRef(wordIndex + 1)->content.type !=
1415  core::Content::BREAK) &&
1416  y > yWidgetBase - word->spaceStyle->font->ascent &&
1417  y <= yWidgetBase + word->spaceStyle->font->descent) {
1418  *inSpace = true;
1419  return word;
1420  }
1421  } else {
1422  if (y > yWidgetBase - word->size.ascent &&
1423  y <= yWidgetBase + word->size.descent)
1424  return word;
1425  }
1426  break;
1427  }
1428  }
1429 
1430  return NULL;
1431 }
1432 
1433 void Textblock::draw (core::View *view, core::Rectangle *area)
1434 {
1435  PRINTF ("DRAW: %d, %d, %d x %d\n",
1436  area->x, area->y, area->width, area->height);
1437 
1438  int lineIndex;
1439  Line *line;
1440 
1441  drawWidgetBox (view, area, false);
1442 
1443  lineIndex = findLineIndex (area->y);
1444 
1445  for (; lineIndex < lines->size (); lineIndex++) {
1446  line = lines->getRef (lineIndex);
1447  if (lineYOffsetWidget (line) >= area->y + area->height)
1448  break;
1449 
1450  drawLine (line, view, area);
1451  }
1452 }
1453 
1457 Textblock::Word *Textblock::addWord (int width, int ascent, int descent,
1458  short flags, core::style::Style *style)
1459 {
1460  words->increase ();
1461  int wordNo = words->size () - 1;
1462  initWord (wordNo);
1463  fillWord (wordNo, width, ascent, descent, flags, style);
1464  return words->getRef (wordNo);;
1465 }
1466 
1470 void Textblock::initWord (int wordNo)
1471 {
1472  Word *word = words->getRef (wordNo);
1473 
1474  word->style = word->spaceStyle = NULL;
1475  word->wordImgRenderer = NULL;
1476  word->spaceImgRenderer = NULL;
1477 }
1478 
1479 void Textblock::removeWordImgRenderer (int wordNo)
1480 {
1481  Word *word = words->getRef (wordNo);
1482 
1483  if (word->style && word->wordImgRenderer) {
1485  (word->wordImgRenderer);
1486  delete word->wordImgRenderer;
1487  word->wordImgRenderer = NULL;
1488  }
1489 }
1490 
1491 void Textblock::setWordImgRenderer (int wordNo)
1492 {
1493  Word *word = words->getRef (wordNo);
1494 
1495  if (word->style->backgroundImage) {
1496  word->wordImgRenderer = new WordImgRenderer (this, wordNo);
1498  (word->wordImgRenderer);
1499  } else
1500  word->wordImgRenderer = NULL;
1501 }
1502 
1503 void Textblock::removeSpaceImgRenderer (int wordNo)
1504 {
1505  Word *word = words->getRef (wordNo);
1506 
1507  if (word->spaceStyle && word->spaceImgRenderer) {
1509  (word->spaceImgRenderer);
1510  delete word->spaceImgRenderer;
1511  word->spaceImgRenderer = NULL;
1512  }
1513 }
1514 
1515 void Textblock::setSpaceImgRenderer (int wordNo)
1516 {
1517  Word *word = words->getRef (wordNo);
1518 
1519  if (word->spaceStyle->backgroundImage) {
1520  word->spaceImgRenderer = new SpaceImgRenderer (this, wordNo);
1522  (word->spaceImgRenderer);
1523  } else
1524  word->spaceImgRenderer = NULL;
1525 }
1526 
1527 void Textblock::fillWord (int wordNo, int width, int ascent, int descent,
1528  short flags, core::style::Style *style)
1529 {
1530  Word *word = words->getRef (wordNo);
1531 
1532  word->size.width = width;
1533  word->size.ascent = ascent;
1534  word->size.descent = descent;
1535  word->origSpace = word->effSpace = 0;
1536  word->hyphenWidth = 0;
1537  word->badnessAndPenalty.setPenalty (PENALTY_PROHIBIT_BREAK);
1538  word->content.space = false;
1539  word->flags = flags;
1540 
1541  removeWordImgRenderer (wordNo);
1542  removeSpaceImgRenderer (wordNo);
1543 
1544  word->style = style;
1545  word->spaceStyle = style;
1546 
1547  setWordImgRenderer (wordNo);
1548  setSpaceImgRenderer (wordNo);
1549 
1550  style->ref ();
1551  style->ref ();
1552 }
1553 
1554 /*
1555  * Get the width of a string of text.
1556  *
1557  * For "isStart" and "isEnd" see drawText.
1558  */
1559 int Textblock::textWidth(const char *text, int start, int len,
1560  core::style::Style *style, bool isStart, bool isEnd)
1561 {
1562  int ret = 0;
1563 
1564  if (len > 0) {
1565  char *str = NULL;
1566 
1567  switch (style->textTransform) {
1569  default:
1570  ret = layout->textWidth(style->font, text+start, len);
1571  break;
1573  str = layout->textToUpper(text+start, len);
1574  ret = layout->textWidth(style->font, str, strlen(str));
1575  break;
1577  str = layout->textToLower(text+start, len);
1578  ret = layout->textWidth(style->font, str, strlen(str));
1579  break;
1581  if (isStart) {
1582  /* \bug No way to know about non-ASCII punctuation. */
1583  bool initial_seen = false;
1584 
1585  for (int i = 0; i < start; i++)
1586  if (!ispunct(text[i]))
1587  initial_seen = true;
1588  if (initial_seen) {
1589  ret = layout->textWidth(style->font, text+start, len);
1590  } else {
1591  int after = 0;
1592 
1593  text += start;
1594  while (ispunct(text[after]))
1595  after++;
1596  if (text[after])
1597  after = layout->nextGlyph(text, after);
1598  if (after > len)
1599  after = len;
1600  str = layout->textToUpper(text, after);
1601  ret = layout->textWidth(style->font, str, strlen(str)) +
1602  layout->textWidth(style->font, text+after, len-after);
1603  }
1604  } else
1605  ret = layout->textWidth(style->font, text+start, len);
1606  break;
1607  }
1608 
1609  if (str)
1610  free(str);
1611  }
1612 
1613  return ret;
1614 }
1615 
1621 void Textblock::calcTextSize (const char *text, size_t len,
1622  core::style::Style *style,
1623  core::Requisition *size, bool isStart, bool isEnd)
1624 {
1625  size->width = textWidth (text, 0, len, style, isStart, isEnd);
1626  size->ascent = style->font->ascent;
1627  size->descent = style->font->descent;
1628 
1629  /*
1630  * For 'normal' line height, just use ascent and descent from font.
1631  * For absolute/percentage, line height is relative to font size, which
1632  * is (irritatingly) smaller than ascent+descent.
1633  */
1634  if (style->lineHeight != core::style::LENGTH_AUTO) {
1635  int height, leading;
1636  float factor = style->font->size;
1637 
1638  factor /= (style->font->ascent + style->font->descent);
1639 
1640  size->ascent = lout::misc::roundInt(size->ascent * factor);
1641  size->descent = lout::misc::roundInt(size->descent * factor);
1642 
1643  /* TODO: The containing block's line-height property gives a minimum
1644  * height for the line boxes. (Even when it's set to 'normal', i.e.,
1645  * AUTO? Apparently.) Once all block elements make Textblocks or
1646  * something, this can be handled.
1647  */
1648  if (core::style::isAbsLength (style->lineHeight))
1649  height = core::style::absLengthVal(style->lineHeight);
1650  else
1651  height =
1653  style->lineHeight);
1654  leading = height - style->font->size;
1655 
1656  size->ascent += leading / 2;
1657  size->descent += leading - (leading / 2);
1658  }
1659 
1660  /* In case of a sub or super script we increase the word's height and
1661  * potentially the line's height.
1662  */
1663  if (style->valign == core::style::VALIGN_SUB) {
1664  int requiredDescent = style->font->descent + style->font->ascent / 3;
1665  size->descent = misc::max (size->descent, requiredDescent);
1666  } else if (style->valign == core::style::VALIGN_SUPER) {
1667  int requiredAscent = style->font->ascent + style->font->ascent / 2;
1668  size->ascent = misc::max (size->ascent, requiredAscent);
1669  }
1670 }
1671 
1676 void Textblock::addText (const char *text, size_t len,
1677  core::style::Style *style)
1678 {
1679  PRINTF ("[%p] ADD_TEXT (%d characters)\n", this, (int)len);
1680 
1681  // Count dividing characters.
1682  int numParts = 1;
1683 
1684  for (const char *s = text; s; s = nextUtf8Char (s, text + len - s)) {
1685  int foundDiv = -1;
1686  for (int j = 0; foundDiv == -1 && j < NUM_DIV_CHARS; j++) {
1687  int lDiv = strlen (divChars[j].s);
1688  if (s <= text + len - lDiv) {
1689  if (memcmp (s, divChars[j].s, lDiv * sizeof (char)) == 0)
1690  foundDiv = j;
1691  }
1692  }
1693 
1694  if (foundDiv != -1) {
1695  if (divChars[foundDiv].penaltyIndexLeft != -1)
1696  numParts ++;
1697  if (divChars[foundDiv].penaltyIndexRight != -1)
1698  numParts ++;
1699  }
1700  }
1701 
1702  if (numParts == 1) {
1703  // Simple (and common) case: no dividing characters. May still
1704  // be hyphenated automatically.
1705  core::Requisition size;
1706  calcTextSize (text, len, style, &size, true, true);
1707  addText0 (text, len,
1708  Word::CAN_BE_HYPHENATED | Word::WORD_START | Word::WORD_END,
1709  style, &size);
1710 
1711  //printf ("[%p] %d: added simple word: ", this, words->size() - 1);
1712  //printWordWithFlags (words->getLastRef());
1713  //printf ("\n");
1714  } else {
1715  PRINTF ("HYPHENATION: '");
1716  for (size_t i = 0; i < len; i++)
1717  PUTCHAR(text[i]);
1718  PRINTF ("', with %d parts\n", numParts);
1719 
1720  // Store hyphen positions.
1721  int n = 0, totalLenCharRemoved = 0;
1722  int partPenaltyIndex[numParts - 1];
1723  int partStart[numParts], partEnd[numParts];
1724  bool charRemoved[numParts - 1], canBeHyphenated[numParts + 1];
1725  bool permDivChar[numParts - 1], unbreakableForMinWidth[numParts - 1];
1726  canBeHyphenated[0] = canBeHyphenated[numParts] = true;
1727  partStart[0] = 0;
1728  partEnd[numParts - 1] = len;
1729 
1730  for (const char *s = text; s; s = nextUtf8Char (s, text + len - s)) {
1731  int foundDiv = -1;
1732  for (int j = 0; foundDiv == -1 && j < NUM_DIV_CHARS; j++) {
1733  int lDiv = strlen (divChars[j].s);
1734  if (s <= text + len - lDiv) {
1735  if (memcmp (s, divChars[j].s, lDiv * sizeof (char)) == 0)
1736  foundDiv = j;
1737  }
1738  }
1739 
1740  if (foundDiv != -1) {
1741  int lDiv = strlen (divChars[foundDiv].s);
1742 
1743  if (divChars[foundDiv].charRemoved) {
1744  assert (divChars[foundDiv].penaltyIndexLeft != -1);
1745  assert (divChars[foundDiv].penaltyIndexRight == -1);
1746 
1747  partPenaltyIndex[n] = divChars[foundDiv].penaltyIndexLeft;
1748  charRemoved[n] = true;
1749  permDivChar[n] = false;
1750  unbreakableForMinWidth[n] =
1751  divChars[foundDiv].unbreakableForMinWidth;
1752  canBeHyphenated[n + 1] = divChars[foundDiv].canBeHyphenated;
1753  partEnd[n] = s - text;
1754  partStart[n + 1] = s - text + lDiv;
1755  n++;
1756  totalLenCharRemoved += lDiv;
1757  } else {
1758  assert (divChars[foundDiv].penaltyIndexLeft != -1 ||
1759  divChars[foundDiv].penaltyIndexRight != -1);
1760 
1761  if (divChars[foundDiv].penaltyIndexLeft != -1) {
1762  partPenaltyIndex[n] = divChars[foundDiv].penaltyIndexLeft;
1763  charRemoved[n] = false;
1764  permDivChar[n] = false;
1765  unbreakableForMinWidth[n] =
1766  divChars[foundDiv].unbreakableForMinWidth;
1767  canBeHyphenated[n + 1] = divChars[foundDiv].canBeHyphenated;
1768  partEnd[n] = s - text;
1769  partStart[n + 1] = s - text;
1770  n++;
1771  }
1772 
1773  if (divChars[foundDiv].penaltyIndexRight != -1) {
1774  partPenaltyIndex[n] = divChars[foundDiv].penaltyIndexRight;
1775  charRemoved[n] = false;
1776  permDivChar[n] = true;
1777  unbreakableForMinWidth[n] =
1778  divChars[foundDiv].unbreakableForMinWidth;
1779  canBeHyphenated[n + 1] = divChars[foundDiv].canBeHyphenated;
1780  partEnd[n] = s - text + lDiv;
1781  partStart[n + 1] = s - text + lDiv;
1782  n++;
1783  }
1784  }
1785  }
1786  }
1787 
1788  // Get text without removed characters, e. g. hyphens.
1789  const char *textWithoutHyphens;
1790  char textWithoutHyphensBuf[len - totalLenCharRemoved];
1791  int *breakPosWithoutHyphens, breakPosWithoutHyphensBuf[numParts - 1];
1792 
1793  if (totalLenCharRemoved == 0) {
1794  // No removed characters: take original arrays.
1795  textWithoutHyphens = text;
1796  // Ends are also break positions, except the last end, which
1797  // is superfluous, but does not harm (since arrays in C/C++
1798  // does not have an implicit length).
1799  breakPosWithoutHyphens = partEnd;
1800  } else {
1801  // Copy into special buffers.
1802  textWithoutHyphens = textWithoutHyphensBuf;
1803  breakPosWithoutHyphens = breakPosWithoutHyphensBuf;
1804 
1805  int n = 0;
1806  for (int i = 0; i < numParts; i++) {
1807  memmove (textWithoutHyphensBuf + n, text + partStart[i],
1808  partEnd[i] - partStart[i]);
1809  n += partEnd[i] - partStart[i];
1810  if (i < numParts - 1)
1811  breakPosWithoutHyphensBuf[i] = n;
1812  }
1813  }
1814 
1815  PRINTF("H... without hyphens: '");
1816  for (size_t i = 0; i < len - totalLenCharRemoved; i++)
1817  PUTCHAR(textWithoutHyphens[i]);
1818  PRINTF("'\n");
1819 
1820  core::Requisition wordSize[numParts];
1821  calcTextSizes (textWithoutHyphens, len - totalLenCharRemoved, style,
1822  numParts - 1, breakPosWithoutHyphens, wordSize);
1823 
1824  // Finished!
1825  for (int i = 0; i < numParts; i++) {
1826  short flags = 0;
1827 
1828  // If this parts adjoins at least one division characters,
1829  // for which canBeHyphenated is set to false (this is the
1830  // case for soft hyphens), do not hyphenate.
1831  if (canBeHyphenated[i] && canBeHyphenated[i + 1])
1832  flags |= Word::CAN_BE_HYPHENATED;
1833 
1834  if(i < numParts - 1) {
1835  if (charRemoved[i])
1836  flags |= Word::DIV_CHAR_AT_EOL;
1837 
1838  if (permDivChar[i])
1839  flags |= Word::PERM_DIV_CHAR;
1840  if (unbreakableForMinWidth[i])
1841  flags |= Word::UNBREAKABLE_FOR_MIN_WIDTH;
1842 
1843  flags |= Word::DRAW_AS_ONE_TEXT;
1844  }
1845 
1846  if (i == 0)
1847  flags |= Word::WORD_START;
1848  if (i == numParts - 1)
1849  flags |= Word::WORD_END;
1850 
1851  addText0 (text + partStart[i], partEnd[i] - partStart[i],
1852  flags, style, &wordSize[i]);
1853 
1854  //printf ("[%p] %d: added word part: ", this, words->size() - 1);
1855  //printWordWithFlags (words->getLastRef());
1856  //printf ("\n");
1857 
1858  //PRINTF("H... [%d] '", i);
1859  //for (int j = partStart[i]; j < partEnd[i]; j++)
1860  // PUTCHAR(text[j]);
1861  //PRINTF("' added\n");
1862 
1863  if(i < numParts - 1) {
1864  Word *word = words->getLastRef();
1865 
1866  setBreakOption (word, style, penalties[partPenaltyIndex[i]][0],
1867  penalties[partPenaltyIndex[i]][1], false);
1868 
1869  if (charRemoved[i])
1870  // Currently, only unconditional hyphens (UTF-8:
1871  // "\xe2\x80\x90") can be used. See also drawWord, last
1872  // section "if (drawHyphen)".
1873  // Could be extended by adding respective members to
1874  // DivChar and Word.
1875  word->hyphenWidth =
1876  layout->textWidth (word->style->font, hyphenDrawChar,
1877  strlen (hyphenDrawChar));
1878 
1879  accumulateWordData (words->size() - 1);
1880  correctLastWordExtremes ();
1881  }
1882  }
1883  }
1884 }
1885 
1886 void Textblock::calcTextSizes (const char *text, size_t textLen,
1887  core::style::Style *style,
1888  int numBreaks, int *breakPos,
1889  core::Requisition *wordSize)
1890 {
1891  // The size of the last part is calculated in a simple way.
1892  int lastStart = breakPos[numBreaks - 1];
1893  calcTextSize (text + lastStart, textLen - lastStart, style,
1894  &wordSize[numBreaks], true, true);
1895 
1896  PRINTF("H... [%d] '", numBreaks);
1897  for (size_t i = 0; i < textLen - lastStart; i++)
1898  PUTCHAR(text[i + lastStart]);
1899  PRINTF("' -> %d\n", wordSize[numBreaks].width);
1900 
1901  // The rest is more complicated. See dw-line-breaking, section
1902  // "Hyphens".
1903  for (int i = numBreaks - 1; i >= 0; i--) {
1904  int start = (i == 0) ? 0 : breakPos[i - 1];
1905  calcTextSize (text + start, textLen - start, style, &wordSize[i],
1906  i == 0, i == numBreaks - 1);
1907 
1908  PRINTF("H... [%d] '", i);
1909  for (size_t j = 0; j < textLen - start; j++)
1910  PUTCHAR(text[j + start]);
1911  PRINTF("' -> %d\n", wordSize[i].width);
1912 
1913  for (int j = i + 1; j < numBreaks + 1; j++) {
1914  wordSize[i].width -= wordSize[j].width;
1915  PRINTF("H... - %d = %d\n", wordSize[j].width, wordSize[i].width);
1916  }
1917  }
1918 }
1919 
1923 void Textblock::addText0 (const char *text, size_t len, short flags,
1924  core::style::Style *style, core::Requisition *size)
1925 {
1926  //printf("[%p] addText0 ('", this);
1927  //for (size_t i = 0; i < len; i++)
1928  // putchar(text[i]);
1929  //printf("', ");
1930  //printWordFlags (flags);
1931  //printf (", ...)\n");
1932 
1933  Word *word = addWord (size->width, size->ascent, size->descent,
1934  flags, style);
1935  DBG_OBJ_ASSOC_CHILD (style);
1936  word->content.type = core::Content::TEXT;
1937  word->content.text = layout->textZone->strndup(text, len);
1938 
1939  processWord (words->size () - 1);
1940 }
1941 
1945 void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
1946 {
1947  Word *word;
1948  core::Requisition size;
1949 
1950  /* We first assign -1 as parent_ref, since the call of widget->size_request
1951  * will otherwise let this Textblock be rewrapped from the beginning.
1952  * (parent_ref is actually undefined, but likely has the value 0.) At the,
1953  * end of this function, the correct value is assigned. */
1954  widget->parentRef = -1;
1955 
1956  PRINTF ("%p becomes child of %p\n", widget, this);
1957 
1958  widget->setParent (this);
1959  widget->setStyle (style);
1960 
1961  calcWidgetSize (widget, &size);
1962  word = addWord (size.width, size.ascent, size.descent, 0, style);
1963 
1964  word->content.type = core::Content::WIDGET;
1965  word->content.widget = widget;
1966 
1967  //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1,
1968  // word->content.widget);
1969 
1970  processWord (words->size () - 1);
1971  //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
1972  // word->content.widget->parent_ref);
1973 
1974  //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
1975  // "Assigning parent_ref = %d to added word %d, "
1976  // "in page with %d word(s)\n",
1977  // lines->size () - 1, words->size() - 1, words->size());
1978 }
1979 
1987 bool Textblock::addAnchor (const char *name, core::style::Style *style)
1988 {
1989  char *copy;
1990  int y;
1991 
1992  // Since an anchor does not take any space, it is safe to call
1993  // addAnchor already here.
1994  if (wasAllocated ()) {
1995  if (lines->size () == 0)
1996  y = allocation.y;
1997  else
1998  y = allocation.y + lineYOffsetWidgetI (lines->size () - 1);
1999  copy = Widget::addAnchor (name, y);
2000  } else
2001  copy = Widget::addAnchor (name);
2002 
2003  if (copy == NULL)
2008  return false;
2009  else {
2010  Anchor *anchor;
2011 
2012  anchors->increase();
2013  anchor = anchors->getRef(anchors->size() - 1);
2014  anchor->name = copy;
2015  anchor->wordIndex = words->size();
2016  return true;
2017  }
2018 }
2019 
2020 
2024 void Textblock::addSpace (core::style::Style *style)
2025 {
2026  int wordIndex = words->size () - 1;
2027  if (wordIndex >= 0) {
2028  fillSpace (wordIndex, style);
2029  accumulateWordData (wordIndex);
2030  correctLastWordExtremes ();
2031  }
2032 }
2033 
2040 void Textblock::addBreakOption (core::style::Style *style, bool forceBreak)
2041 {
2042  int wordIndex = words->size () - 1;
2043  if (wordIndex >= 0) {
2044  setBreakOption (words->getRef(wordIndex), style, 0, 0, forceBreak);
2045  // Call of accumulateWordData() is not needed here.
2046  correctLastWordExtremes ();
2047  }
2048 }
2049 
2050 void Textblock::fillSpace (int wordNo, core::style::Style *style)
2051 {
2052  // Old comment:
2053  //
2054  // According to
2055  // http://www.w3.org/TR/CSS2/text.html#white-space-model: "line
2056  // breaking opportunities are determined based on the text
2057  // prior to the white space collapsing steps".
2058  //
2059  // So we call addBreakOption () for each Textblock::addSpace ()
2060  // call. This is important e.g. to be able to break between
2061  // foo and bar in: <span style="white-space:nowrap">foo </span>
2062  // bar
2063  //
2064  // TODO: Re-evaluate again.
2065 
2066  Word *word = words->getRef (wordNo);
2067 
2068  // TODO: This line does not work: addBreakOption (word, style);
2069 
2070  // Do not override a previously set break penalty.
2071  if (!word->content.space) {
2072  setBreakOption (word, style, 0, 0, false);
2073 
2074  word->content.space = true;
2075  word->effSpace = word->origSpace = style->font->spaceWidth +
2076  style->wordSpacing;
2077 
2078  //DBG_OBJ_ARRSET_NUM (this, "words.%d.origSpace", wordIndex,
2079  // word->origSpace);
2080  //DBG_OBJ_ARRSET_NUM (this, "words.%d.effSpace", wordIndex,
2081  // word->effSpace);
2082  //DBG_OBJ_ARRSET_NUM (this, "words.%d.content.space", wordIndex,
2083  // word->content.space);
2084 
2085 
2086  removeSpaceImgRenderer (wordNo);
2087 
2088  word->spaceStyle->unref ();
2089  word->spaceStyle = style;
2090  style->ref ();
2091 
2092  setSpaceImgRenderer (wordNo);
2093  }
2094 }
2095 
2101 void Textblock::setBreakOption (Word *word, core::style::Style *style,
2102  int breakPenalty1, int breakPenalty2,
2103  bool forceBreak)
2104 {
2105  // TODO: lineMustBeBroken should be independent of the penalty
2106  // index? Otherwise, examine the last line.
2107  if (!word->badnessAndPenalty.lineMustBeBroken(0)) {
2108  if (forceBreak || isBreakAllowed (word))
2109  word->badnessAndPenalty.setPenalties (breakPenalty1, breakPenalty2);
2110  else
2111  word->badnessAndPenalty.setPenalty (PENALTY_PROHIBIT_BREAK);
2112  }
2113 }
2114 
2115 bool Textblock::isBreakAllowed (Word *word)
2116 {
2117  switch (word->style->whiteSpace) {
2121  return true;
2122 
2125  return false;
2126  }
2127 
2128  // compiler happiness
2130  return false;
2131 }
2132 
2133 
2137 void Textblock::addParbreak (int space, core::style::Style *style)
2138 {
2139  Word *word;
2140 
2141  /* A break may not be the first word of a page, or directly after
2142  the bullet/number (which is the first word) in a list item. (See
2143  also comment in Dw_page_size_request.) */
2144  if (words->size () == 0 ||
2145  (hasListitemValue && words->size () == 1)) {
2146  /* This is a bit hackish: If a break is added as the
2147  first/second word of a page, and the parent widget is also a
2148  Textblock, and there is a break before -- this is the case when
2149  a widget is used as a text box (lists, blockquotes, list
2150  items etc) -- then we simply adjust the break before, in a
2151  way that the space is in any case visible. */
2152  Widget *widget;
2153 
2154  /* Find the widget where to adjust the breakSpace. */
2155  for (widget = this;
2156  widget->getParent() &&
2157  widget->getParent()->instanceOf (Textblock::CLASS_ID);
2158  widget = widget->getParent ()) {
2159  Textblock *textblock2 = (Textblock*)widget->getParent ();
2160  int index = textblock2->hasListitemValue ? 1 : 0;
2161  bool isfirst = (textblock2->words->getRef(index)->content.type
2162  == core::Content::WIDGET
2163  && textblock2->words->getRef(index)->content.widget
2164  == widget);
2165  if (!isfirst) {
2166  /* The page we searched for has been found. */
2167  Word *word2;
2168  int lineno = widget->parentRef;
2169 
2170  if (lineno > 0 &&
2171  (word2 =
2172  textblock2->words->getRef(textblock2->lines
2173  ->getRef(lineno - 1)->firstWord)) &&
2174  word2->content.type == core::Content::BREAK) {
2175  if (word2->content.breakSpace < space) {
2176  word2->content.breakSpace = space;
2177  textblock2->queueResize (lineno, false);
2178  textblock2->mustQueueResize = false;
2179  }
2180  }
2181  return;
2182  }
2183  /* Otherwise continue to examine parents. */
2184  }
2185  /* Return in any case. */
2186  return;
2187  }
2188 
2189  /* Another break before? */
2190  if ((word = words->getRef(words->size () - 1)) &&
2191  word->content.type == core::Content::BREAK) {
2192  Line *lastLine = lines->getRef (lines->size () - 1);
2193 
2194  word->content.breakSpace =
2195  misc::max (word->content.breakSpace, space);
2196  lastLine->breakSpace =
2197  misc::max (word->content.breakSpace,
2198  lastLine->marginDescent - lastLine->boxDescent,
2199  lastLine->breakSpace);
2200  return;
2201  }
2202 
2203  word = addWord (0, 0, 0, 0, style);
2204  DBG_OBJ_ASSOC_CHILD (style);
2205  word->content.type = core::Content::BREAK;
2206  word->badnessAndPenalty.setPenalty (PENALTY_FORCE_BREAK);
2207  word->content.breakSpace = space;
2208  processWord (words->size () - 1);
2209 }
2210 
2211 /*
2212  * Cause a line break.
2213  */
2214 void Textblock::addLinebreak (core::style::Style *style)
2215 {
2216  Word *word;
2217 
2218  if (words->size () == 0 ||
2219  words->getRef(words->size () - 1)->content.type == core::Content::BREAK)
2220  // An <BR> in an empty line gets the height of the current font
2221  // (why would someone else place it here?), ...
2222  word =
2223  addWord (0, style->font->ascent, style->font->descent, 0, style);
2224  else
2225  // ... otherwise, it has no size (and does not enlarge the line).
2226  word = addWord (0, 0, 0, 0, style);
2227 
2228  DBG_OBJ_ASSOC_CHILD (style);
2229 
2230  word->content.type = core::Content::BREAK;
2231  word->badnessAndPenalty.setPenalty (PENALTY_FORCE_BREAK);
2232  word->content.breakSpace = 0;
2233  processWord (words->size () - 1);
2234 }
2235 
2236 
2243 core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
2244 {
2245  int lineIndex, wordIndex;
2246  Line *line;
2247 
2248  if (x < allocation.x ||
2249  y < allocation.y ||
2250  x > allocation.x + allocation.width ||
2251  y > allocation.y + getHeight ()) {
2252  return NULL;
2253  }
2254 
2255  lineIndex = findLineIndex (y - allocation.y);
2256 
2257  if (lineIndex < 0 || lineIndex >= lines->size ()) {
2258  return this;
2259  }
2260 
2261  line = lines->getRef (lineIndex);
2262 
2263  for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
2264  Word *word = words->getRef (wordIndex);
2265 
2266  if (word->content.type == core::Content::WIDGET) {
2267  core::Widget * childAtPoint;
2268  childAtPoint = word->content.widget->getWidgetAtPoint (x, y,
2269  level + 1);
2270  if (childAtPoint) {
2271  return childAtPoint;
2272  }
2273  }
2274  }
2275 
2276  return this;
2277 }
2278 
2279 
2284 void Textblock::handOverBreak (core::style::Style *style)
2285 {
2286  if (lines->size() > 0) {
2287  Widget *parent;
2288  Line *lastLine = lines->getRef (lines->size () - 1);
2289 
2290  if (lastLine->breakSpace != 0 && (parent = getParent()) &&
2291  parent->instanceOf (Textblock::CLASS_ID)) {
2292  Textblock *textblock2 = (Textblock*) parent;
2293  textblock2->addParbreak(lastLine->breakSpace, style);
2294  }
2295  }
2296 }
2297 
2298 /*
2299  * Any words added by addWord() are not immediately (queued to
2300  * be) drawn, instead, this function must be called. This saves some
2301  * calls to queueResize().
2302  *
2303  */
2304 void Textblock::flush ()
2305 {
2306  PRINTF ("[%p] FLUSH => %s (parentRef = %d)\n",
2307  this, mustQueueResize ? "true" : "false", parentRef);
2308 
2309  if (mustQueueResize) {
2310  queueResize (-1, true);
2311  mustQueueResize = false;
2312  }
2313 }
2314 
2315 
2316 // next: Dw_page_find_word
2317 
2318 void Textblock::changeLinkColor (int link, int newColor)
2319 {
2320  for (int lineIndex = 0; lineIndex < lines->size(); lineIndex++) {
2321  bool changed = false;
2322  Line *line = lines->getRef (lineIndex);
2323  int wordIdx;
2324 
2325  for (wordIdx = line->firstWord; wordIdx <= line->lastWord; wordIdx++){
2326  Word *word = words->getRef(wordIdx);
2327 
2328  if (word->style->x_link == link) {
2329  core::style::StyleAttrs styleAttrs;
2330 
2331  switch (word->content.type) {
2332  case core::Content::TEXT:
2333  { core::style::Style *old_style = word->style;
2334  styleAttrs = *old_style;
2335  styleAttrs.color = core::style::Color::create (layout,
2336  newColor);
2337  word->style = core::style::Style::create (&styleAttrs);
2338  old_style->unref();
2339  old_style = word->spaceStyle;
2340  styleAttrs = *old_style;
2341  styleAttrs.color = core::style::Color::create (layout,
2342  newColor);
2343  word->spaceStyle = core::style::Style::create(&styleAttrs);
2344  old_style->unref();
2345  break;
2346  }
2347  case core::Content::WIDGET:
2348  { core::Widget *widget = word->content.widget;
2349  styleAttrs = *widget->getStyle();
2350  styleAttrs.color = core::style::Color::create (layout,
2351  newColor);
2352  styleAttrs.setBorderColor(
2353  core::style::Color::create (layout, newColor));
2354  widget->setStyle(core::style::Style::create (&styleAttrs));
2355  break;
2356  }
2357  default:
2358  break;
2359  }
2360  changed = true;
2361  }
2362  }
2363  if (changed)
2364  queueDrawArea (0, lineYOffsetWidget(line), allocation.width,
2365  line->boxAscent + line->boxDescent);
2366  }
2367 }
2368 
2369 void Textblock::changeWordStyle (int from, int to, core::style::Style *style,
2370  bool includeFirstSpace, bool includeLastSpace)
2371 {
2372 }
2373 
2374 void Textblock::queueDrawRange (int index1, int index2)
2375 {
2376  int from = misc::min (index1, index2);
2377  int to = misc::max (index1, index2);
2378 
2379  from = misc::min (from, words->size () - 1);
2380  from = misc::max (from, 0);
2381  to = misc::min (to, words->size () - 1);
2382  to = misc::max (to, 0);
2383 
2384  int line1idx = findLineOfWord (from);
2385  int line2idx = findLineOfWord (to);
2386 
2387  if (line1idx >= 0 && line2idx >= 0) {
2388  Line *line1 = lines->getRef (line1idx),
2389  *line2 = lines->getRef (line2idx);
2390  int y = lineYOffsetWidget (line1) + line1->boxAscent -
2391  line1->contentAscent;
2392  int h = lineYOffsetWidget (line2) + line2->boxAscent +
2393  line2->contentDescent - y;
2394 
2395  queueDrawArea (0, y, allocation.width, h);
2396  }
2397 }
2398 
2399 } // namespace dw
dw::core::style::VALIGN_SUPER
Definition: style.hh:260
dw::core::style::TEXT_DECORATION_UNDERLINE
Definition: style.hh:337
dw::core::style::Tooltip
Definition: style.hh:615
DBG_OBJ_DELETE
#define DBG_OBJ_DELETE()
Definition: debug.hh:176
dw::Textblock::Line::lastWord
int lastWord
Definition: textblock.hh:309
dw::core::style::StyleAttrs::textTransform
TextTransform textTransform
Definition: style.hh:505
dw::Textblock::mustQueueResize
bool mustQueueResize
Definition: textblock.hh:459
dw::Textblock::Line::maxLineWidth
int maxLineWidth
Definition: textblock.hh:325
dw::core::style::StyleImage::putExternalImgRenderer
void putExternalImgRenderer(ImgRenderer *ir)
Add an additional ImgRenderer, especially used for drawing.
Definition: style.hh:848
dw::core::Widget::draw
virtual void draw(View *view, Rectangle *area)=0
dw::Textblock::hasListitemValue
bool hasListitemValue
Definition: textblock.hh:433
dw::core::style::TEXT_TRANSFORM_NONE
Definition: style.hh:266
dw::core::MousePositionEvent::yWidget
int yWidget
Definition: events.hh:51
dw::core::Allocation::y
int y
Definition: types.hh:166
dw::core::style::isPerLength
bool isPerLength(Length l)
Returns true if l is a percentage.
Definition: style.hh:406
dw::core::Allocation::x
int x
Definition: types.hh:165
dw::core::style::absLengthVal
int absLengthVal(Length l)
Returns the value of a length in pixels, as an integer.
Definition: style.hh:412
dw::core::Extremes::maxWidth
int maxWidth
Definition: types.hh:182
dw::core::style::Style::ref
void ref()
Definition: style.hh:598
dw::Textblock::Paragraph
Definition: textblock.hh:278
DBG_OBJ_CREATE
#define DBG_OBJ_CREATE(klass)
Definition: debug.hh:175
dw::Textblock::addParbreak
void addParbreak(int space, core::style::Style *style)
Definition: textblock.cc:2137
dw::core::Requisition
Definition: types.hh:172
dw::core::style::StyleAttrs::height
Length height
Definition: style.hh:508
dw::core::style::TEXT_DECORATION_OVERLINE
Definition: style.hh:338
dw::Textblock
A Widget for rendering text blocks, i.e. paragraphs or sequences of paragraphs.
Definition: textblock.hh:149
dw::core::style::WHITE_SPACE_NORMAL
Definition: style.hh:344
dw::Textblock::BadnessAndPenalty::setPenalties
void setPenalties(int penalty1, int penalty2)
Definition: textblock_linebreaking.cc:128
dw::core::SelectionState::EventType
EventType
Definition: selection.hh:220
dw::core::MousePositionEvent
Base class for all mouse events related to a specific position.
Definition: events.hh:48
dw::core::style::WHITE_SPACE_PRE_LINE
Definition: style.hh:348
dw::core::EventCrossing
Represents a enter or leave notify event.
Definition: events.hh:74
dw::core::style::StyleAttrs::width
Length width
Definition: style.hh:508
dw::core::EventButton
Represents a button press or release event.
Definition: events.hh:57
dw::core::style::LENGTH_AUTO
Represents "auto" lengths.
Definition: style.hh:454
dw::core::Requisition::descent
int descent
Definition: types.hh:176
dw::core::style::StyleAttrs::margin
Box margin
Definition: style.hh:510
dw::core::style::Color
Definition: style.hh:709
dw::core::Extremes::minWidth
int minWidth
Definition: types.hh:181
dw::core::style::StyleAttrs::backgroundColor
Color * backgroundColor
Definition: style.hh:495
dw::Textblock::Anchor::wordIndex
int wordIndex
Definition: textblock.hh:406
dw::core::Widget::setAscent
virtual void setAscent(int ascent)
Definition: widget.cc:637
dw::core::style::TEXT_TRANSFORM_UPPERCASE
Definition: style.hh:268
dw::core::style::StyleAttrs::cursor
Cursor cursor
Definition: style.hh:519
dw::core::Content::Type
Type
Definition: types.hh:187
dw::core::View::drawText
virtual void drawText(style::Font *font, style::Color *color, style::Color::Shading shading, int x, int y, const char *text, int len)=0
dw::Textblock::Word::origSpace
short origSpace
Definition: textblock.hh:361
dw::core::style::StyleAttrs::lineHeight
Length lineHeight
Definition: style.hh:508
dw::core::Iterator
Iterators are used to iterate through the contents of a widget.
Definition: iterator.hh:19
dw::core::style::StyleAttrs::whiteSpace
WhiteSpace whiteSpace
Definition: style.hh:516
lout::misc::assertNotReached
void assertNotReached()
Definition: misc.hh:35
dw::core::Widget::setWidth
virtual void setWidth(int width)
Definition: widget.cc:633
dw::Textblock::Line::firstWord
int firstWord
Definition: textblock.hh:308
dw::Textblock::DivChar
Definition: textblock.hh:228
dw::core::Rectangle::x
int x
Definition: types.hh:72
dw::core::Content::space
bool space
Definition: types.hh:202
dw::core::Widget::parentRef
int parentRef
This value is defined by the parent widget, and used for incremential resizing.
Definition: widget.hh:143
dw::core::Widget::setParent
void setParent(Widget *parent)
Definition: widget.cc:144
dw::core::Allocation
Represents the allocation, i.e. actual position and size of a dw::core::Widget.
Definition: types.hh:163
dw::Textblock::Paragraph::maxParMax
int maxParMax
Definition: textblock.hh:302
dw::core::Content::text
const char * text
Definition: types.hh:204
dw::core::Widget::getParent
Widget * getParent()
Definition: widget.hh:302
dw::core::Widget::getStyle
style::Style * getStyle()
Definition: widget.hh:268
dw::core::style::multiplyWithPerLength
int multiplyWithPerLength(int x, Length l)
Multiply an int with a percentage length, returning int.
Definition: style.hh:433
dw::core::View::drawRectangle
virtual void drawRectangle(style::Color *color, style::Color::Shading shading, bool filled, int x, int y, int width, int height)=0
dw::Textblock::Word
Definition: textblock.hh:328
lout::unicode
Stuff dealing with Unicode characters: UTF-8, character classes etc.
Definition: unicode.cc:8
dw::core::Widget::sizeAllocate
void sizeAllocate(Allocation *allocation)
Wrapper for Widget::sizeAllocateImpl, calls the latter only when needed.
Definition: widget.cc:249
dw::Textblock::Word::badnessAndPenalty
BadnessAndPenalty badnessAndPenalty
Definition: textblock.hh:385
dw::Textblock::Line::contentAscent
int contentAscent
Definition: textblock.hh:313
dw::core::style::Font::spaceWidth
int spaceWidth
Definition: style.hh:674
dw::core::style::TEXT_TRANSFORM_LOWERCASE
Definition: style.hh:269
dw::Textblock::BadnessAndPenalty::lineMustBeBroken
bool lineMustBeBroken(int penaltyIndex)
Definition: textblock_linebreaking.cc:168
lout::misc::max
T max(T a, T b)
Definition: misc.hh:20
DBG_OBJ_ASSOC_CHILD
#define DBG_OBJ_ASSOC_CHILD(child)
Definition: debug.hh:179
lout::misc::min
T min(T a, T b)
Definition: misc.hh:19
dw::core::style::Color::Shading
Shading
Definition: style.hh:725
PUTCHAR
#define PUTCHAR(ch)
Definition: textblock.hh:12
dw::core::style::Font::ascent
int ascent
Definition: style.hh:673
dw::core::style::multiplyWithPerLengthRounded
int multiplyWithPerLengthRounded(int x, Length l)
Like multiplyWithPerLength, but rounds to nearest integer instead of down.
Definition: style.hh:443
textblock.hh
dw::core::style::StyleAttrs::setBorderColor
void setBorderColor(Color *val)
Definition: style.hh:534
dw::Textblock::Line::boxDescent
int boxDescent
Definition: textblock.hh:313
dw::core::Allocation::descent
int descent
Definition: types.hh:169
dw::core::style::StyleAttrs::boxDiffHeight
int boxDiffHeight()
Definition: style.hh:558
dw::Textblock::Word::style
core::style::Style * style
Definition: textblock.hh:388
dw::core::Widget::hasContents
bool hasContents()
Definition: widget.hh:263
dw::Textblock::TextblockIterator
Definition: textblock.hh:409
dw::Textblock::Paragraph::maxParMin
int maxParMin
Definition: textblock.hh:300
dw::Textblock::Line::marginDescent
int marginDescent
Definition: textblock.hh:318
dw::Textblock::Word::content
core::Content content
Definition: textblock.hh:371
dw::Textblock::Word::spaceStyle
core::style::Style * spaceStyle
Definition: textblock.hh:389
dw::core::Rectangle::height
int height
Definition: types.hh:75
dw::core::EventMotion
Represents a mouse motion event.
Definition: events.hh:67
dw::core::Requisition::ascent
int ascent
Definition: types.hh:175
lout
Definition: container.cc:26
dw::core::style::isAbsLength
bool isAbsLength(Length l)
Returns true if l is an absolute length.
Definition: style.hh:403
dw::core::Widget::intersects
bool intersects(Rectangle *area, Rectangle *intersection)
Calculates the intersection of widget->allocation and area, returned in intersection (in widget coord...
Definition: widget.cc:123
dw::Textblock::lines
lout::misc::SimpleVector< Line > * lines
Definition: textblock.hh:484
dw::Textblock::Line
Definition: textblock.hh:306
hoverTooltip
static dw::core::style::Tooltip * hoverTooltip
Definition: textblock.cc:35
dw::core::MousePositionEvent::xWidget
int xWidget
Definition: events.hh:51
dw::core::Requisition::width
int width
Definition: types.hh:174
dw::core::style::StyleAttrs::hasBackground
bool hasBackground()
Definition: style.hh:560
dw::Textblock::Word::flags
short flags
Definition: textblock.hh:370
dw::core::MousePositionEvent::yCanvas
int yCanvas
Definition: events.hh:51
dw::core::Widget::getWidgetAtPoint
virtual Widget * getWidgetAtPoint(int x, int y, int level)
Search recursively through widget.
Definition: widget.cc:552
dw::core::style::Style::unref
void unref()
Definition: style.hh:599
dw::core::Rectangle
dw::core::Shape implemtation for simple rectangles.
Definition: types.hh:69
dw::Textblock::Word::effSpace
short effSpace
Definition: textblock.hh:362
dw::core::Widget::getAllocation
Allocation * getAllocation()
Definition: widget.hh:270
dw::core::Extremes
Definition: types.hh:179
dw::core::HIGHLIGHT_NUM_LAYERS
Definition: types.hh:46
dw::core::View
An interface to encapsulate platform dependent drawing.
Definition: view.hh:16
dw::core::style::VALIGN_SUB
Definition: style.hh:259
dw::core::style::Tooltip::onLeave
virtual void onLeave()
Definition: style.hh:630
dw::core::style::TEXT_DECORATION_LINE_THROUGH
Definition: style.hh:339
dw::core::Content::breakSpace
int breakSpace
Definition: types.hh:206
dw::core::style::Font::descent
int descent
Definition: style.hh:673
dw::core::style::Box::top
int top
Definition: style.hh:467
dw::core::style::StyleAttrs::x_tooltip
Tooltip * x_tooltip
Definition: style.hh:523
dw::core::style::StyleAttrs::textDecoration
int textDecoration
Definition: style.hh:493
dw::core::Widget::sizeRequest
void sizeRequest(Requisition *requisition)
This method is a wrapper for Widget::sizeRequestImpl(); it calls the latter only when needed.
Definition: widget.cc:214
dw::core::Widget::getExtremes
void getExtremes(Extremes *extremes)
Wrapper for Widget::getExtremesImpl().
Definition: widget.cc:232
dw::core::Content::widget
Widget * widget
Definition: types.hh:205
dw::Textblock::Word::size
core::Requisition size
Definition: textblock.hh:359
dw::Textblock::words
lout::misc::NotSoSimpleVector< Word > * words
Definition: textblock.hh:487
dw::Textblock::SpaceImgRenderer
Definition: textblock.hh:266
dw::core::style::Font::xHeight
int xHeight
Definition: style.hh:675
dw::core::style::StyleAttrs::font
Font * font
Definition: style.hh:492
lout::misc::NotSoSimpleVector
Container similar to lout::misc::SimpleVector, but some cases of insertion optimized (used for hyphen...
Definition: misc.hh:262
dw::core::style::StyleAttrs::wordSpacing
int wordSpacing
Definition: style.hh:507
dw::core::style::WHITE_SPACE_NOWRAP
Definition: style.hh:346
lout::unicode::nextUtf8Char
const char * nextUtf8Char(const char *s)
Definition: unicode.cc:90
dw::Textblock::Anchor::name
char * name
Definition: textblock.hh:405
dw::Textblock::Line::boxAscent
int boxAscent
Definition: textblock.hh:313
dw::core::style::StyleAttrs::boxDiffWidth
int boxDiffWidth()
Definition: style.hh:549
dw::core::Allocation::ascent
int ascent
Definition: types.hh:168
dw::core::style::StyleAttrs::backgroundImage
StyleImage * backgroundImage
Definition: style.hh:496
_MSG
#define _MSG(...)
Definition: bookmarks.c:44
dw::core::style::Tooltip::onEnter
virtual void onEnter()
Definition: style.hh:629
dw::core::style::TEXT_TRANSFORM_CAPITALIZE
Definition: style.hh:267
dw::core::Widget::setDescent
virtual void setDescent(int descent)
Definition: widget.cc:641
dw::Textblock::Line::top
int top
Definition: textblock.hh:313
dw::core::style::StyleAttrs::color
Color * color
Definition: style.hh:495
dw::core::style::Tooltip::onMotion
virtual void onMotion()
Definition: style.hh:631
dw::core::style::StyleAttrs::x_link
int x_link
Definition: style.hh:521
dw::core::Widget::wasAllocated
bool wasAllocated()
Definition: widget.hh:261
dw::core::style::StyleAttrs
Definition: style.hh:489
dw::Textblock::Word::wordImgRenderer
WordImgRenderer * wordImgRenderer
Definition: textblock.hh:394
dw::core::style::FontAttrs::size
int size
Definition: style.hh:642
dw::core::Widget::usesHints
bool usesHints()
Definition: widget.hh:262
dw::Textblock::Line::breakSpace
int breakSpace
Definition: textblock.hh:313
dw::Textblock::Word::spaceImgRenderer
SpaceImgRenderer * spaceImgRenderer
Definition: textblock.hh:395
PRINTF
#define PRINTF(fmt,...)
Definition: textblock.hh:11
lout::misc::roundInt
int roundInt(double d)
Definition: misc.hh:41
dw::core::style::Box::bottom
int bottom
Definition: style.hh:467
dw::core::Iterator::unref
virtual void unref()
Delete the iterator.
Definition: iterator.cc:64
dw::core::Widget::setStyle
virtual void setStyle(style::Style *style)
Change the style of a widget.
Definition: widget.cc:325
dw::Textblock::Word::hyphenWidth
short hyphenWidth
Definition: textblock.hh:364
dw::core::Widget
The base class of all dillo widgets.
Definition: widget.hh:23
dw::core::Widget::queueResize
void queueResize(int ref, bool extremesChanged)
This method should be called, when a widget changes its size.
Definition: widget.cc:171
dw
Dw is in this namespace, or sub namespaces of this one.
Definition: alignedtextblock.cc:26
dw::core::Content::type
short type
Definition: types.hh:201
dw::core::style::StyleImage::removeExternalImgRenderer
void removeExternalImgRenderer(ImgRenderer *ir)
Remove a previously added additional ImgRenderer.
Definition: style.hh:854
dw::Textblock::WordImgRenderer
Implementation used for words.
Definition: textblock.hh:243
dw::core::MouseEvent::state
ButtonState state
Definition: events.hh:42
dw::core::BUTTON1_MASK
Definition: events.hh:20
dw::core::Rectangle::width
int width
Definition: types.hh:74
lout::misc::SimpleVector
Simple (simpler than container::untyped::Vector and container::typed::Vector) template based vector.
Definition: misc.hh:71
dw::Textblock::BadnessAndPenalty::setPenalty
void setPenalty(int penalty)
Definition: textblock.hh:211
dw::core::style::Style
Definition: style.hh:571
dw::core::Rectangle::y
int y
Definition: types.hh:73
dw::core::style::StyleAttrs::valign
VAlignType valign
Definition: style.hh:503
dw::core::Allocation::width
int width
Definition: types.hh:167
dw::Textblock::Anchor
Definition: textblock.hh:403
dw::core::style::WHITE_SPACE_PRE_WRAP
Definition: style.hh:347
dw::core::style::WHITE_SPACE_PRE
Definition: style.hh:345