Dillo v3.2.0-151-g90488cbf
Loading...
Searching...
No Matches
textblock.cc
Go to the documentation of this file.
1/*
2 * Dillo Widget
3 *
4 * Copyright 2005-2007, 2012-2014 Sebastian Geerken <sgeerken@dillo.org>
5 * Copyright 2023-2024 Rodrigo Arias Mallo <rodarima@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22#include "textblock.hh"
23#include "../lout/msg.h"
24#include "../lout/misc.hh"
25#include "../lout/unicode.hh"
26#include "../lout/debug.hh"
27#include "dlib/dlib.h" /* dIspunct */
28
29#include <stdio.h>
30#include <math.h> // remove again?
31#include <limits.h>
32
33/*
34 * Local variables
35 */
36/* The tooltip under mouse pointer in current textblock. No ref. hold.
37 * (having one per view looks not worth the extra clutter). */
39
40
41using namespace lout;
42using namespace lout::misc;
43using namespace lout::unicode;
44
45namespace dw {
46
47int Textblock::CLASS_ID = -1;
48
50 int wordNo)
51{
52 //printf ("new WordImgRenderer %p\n", this);
53
54 this->textblock = textblock;
55 this->wordNo = wordNo;
56 dataSet = false;
57}
58
60{
61 //printf ("delete WordImgRenderer %p\n", this);
62}
63
64void Textblock::WordImgRenderer::setData (int xWordWidget, int lineNo)
65{
66 dataSet = true;
67 this->xWordWidget = xWordWidget;
68 this->lineNo = lineNo;
69}
70
72{
73 //print ();
74 //printf ("\n");
75
76 return dataSet && textblock->wasAllocated ()
77 && wordNo < textblock->words->size()
78 && lineNo < textblock->lines->size();
79}
80
81void Textblock::WordImgRenderer::getBgArea (int *x, int *y, int *width,
82 int *height)
83{
84 // TODO Subtract margin and border (padding box)?
85 Line *line = textblock->lines->getRef (lineNo);
86 *x = textblock->allocation.x + this->xWordWidget;
87 *y = textblock->lineYOffsetCanvas (line);
88 *width = textblock->words->getRef(wordNo)->size.width;
89 *height = line->borderAscent + line->borderDescent;
90}
91
92void Textblock::WordImgRenderer::getRefArea (int *xRef, int *yRef,
93 int *widthRef, int *heightRef)
94{
95 // See comment in Widget::drawBox about the reference area.
96 textblock->getPaddingArea (xRef, yRef, widthRef, heightRef);
97}
98
100{
101 return textblock->words->getRef(wordNo)->style;
102}
103
104void Textblock::WordImgRenderer::draw (int x, int y, int width, int height)
105{
106 textblock->queueDrawArea (x - textblock->allocation.x,
107 y - textblock->allocation.y, width, height);
108
109}
110
111void Textblock::SpaceImgRenderer::getBgArea (int *x, int *y, int *width,
112 int *height)
113{
114 WordImgRenderer::getBgArea (x, y, width, height);
115 *x += *width;
116 *width = textblock->words->getRef(wordNo)->effSpace;
117}
118
120{
121 return textblock->words->getRef(wordNo)->spaceStyle;
122}
123
124// ----------------------------------------------------------------------
125
127 // soft hyphen (U+00AD)
128 { "\xc2\xad", true, false, true, PENALTY_HYPHEN, -1 },
129 // simple hyphen-minus: same penalties like automatic or soft hyphens
130 { "-", false, true, true, -1, PENALTY_HYPHEN },
131 // (unconditional) hyphen (U+2010): handled exactly like minus-hyphen.
132 { "\xe2\x80\x90", false, true, true, -1, PENALTY_HYPHEN },
133 // em dash (U+2014): breaks on both sides are allowed (but see below).
134 { "\xe2\x80\x94", false, true, false,
136};
137
138// Standard values are defined here. The values are already multiplied
139// with 100.
140//
141// Some examples (details are described in doc/dw-line-breaking.doc):
142//
143// 0 = Perfect line; as penalty used for normal spaces.
144
145// 1 (100 here) = A justified line with spaces having 150% or 67% of
146// the ideal space width has this as badness.
147//
148// 8 (800 here) = A justified line with spaces twice as wide as
149// ideally has this as badness.
150//
151// The second value is used when the line before ends with a hyphen,
152// dash etc.
153
155 // Penalties for all hyphens.
156 { 100, 800 },
157 // Penalties for a break point *left* of an em-dash: rather large,
158 // so that a break on the *right* side is preferred.
159 { 800, 800 },
160 // Penalties for a break point *right* of an em-dash: like hyphens.
161 { 100, 800 }
162};
163
165
174const char *Textblock::hyphenDrawChar = "-";
175
176void Textblock::setPenaltyHyphen (int penaltyHyphen)
177{
178 penalties[PENALTY_HYPHEN][0] = penaltyHyphen;
179}
180
181void Textblock::setPenaltyHyphen2 (int penaltyHyphen2)
182{
183 penalties[PENALTY_HYPHEN][1] = penaltyHyphen2;
184}
185
186void Textblock::setPenaltyEmDashLeft (int penaltyLeftEmDash)
187{
188 penalties[PENALTY_EM_DASH_LEFT][0] = penaltyLeftEmDash;
189 penalties[PENALTY_EM_DASH_LEFT][1] = penaltyLeftEmDash;
190}
191
192void Textblock::setPenaltyEmDashRight (int penaltyRightEmDash)
193{
194 penalties[PENALTY_EM_DASH_RIGHT][0] = penaltyRightEmDash;
195}
196
197void Textblock::setPenaltyEmDashRight2 (int penaltyRightEmDash2)
198{
199 penalties[PENALTY_EM_DASH_RIGHT][1] = penaltyRightEmDash2;
200}
201
206
208{
209 DBG_OBJ_CREATE ("dw::Textblock");
210 registerName ("dw::Textblock", &CLASS_ID);
211 setButtonSensitive(true);
212
213 hasListitemValue = false;
215 line1Offset = 0;
217 mustQueueResize = false;
218 DBG_OBJ_SET_BOOL ("mustQueueResize", mustQueueResize);
219 redrawY = 0;
220 DBG_OBJ_SET_NUM ("redrawY", redrawY);
221 lastWordDrawn = -1;
222 DBG_OBJ_SET_NUM ("lastWordDrawn", lastWordDrawn);
223
225
226 /*
227 * The initial sizes of lines and words should not be
228 * too high, since this will waste much memory with tables
229 * containing many small cells. The few more calls to realloc
230 * should not decrease the speed considerably.
231 * (Current setting is for minimal memory usage. An interesting fact
232 * is that high values decrease speed due to memory handling overhead!)
233 * TODO: Some tests would be useful.
234 */
235 paragraphs = new misc::SimpleVector <Paragraph> (1);
236 lines = new misc::SimpleVector <Line> (1);
238 words = new misc::NotSoSimpleVector <Word> (1);
239 anchors = new misc::SimpleVector <Anchor> (1);
240
243
244 DBG_OBJ_SET_NUM ("lines.size", lines->size ());
245 DBG_OBJ_SET_NUM ("words.size", words->size ());
246 DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines);
247 DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs);
248 DBG_OBJ_SET_NUM ("wrapRefLinesFCX", wrapRefLinesFCX);
249 DBG_OBJ_SET_NUM ("wrapRefLinesFCY", wrapRefLinesFCY);
250
251 hoverLink = -1;
252
253 // -1 means undefined.
254 lineBreakWidth = -1;
255 DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth);
256
257 this->limitTextWidth = limitTextWidth;
258 this->treatAsInline = treatAsInline;
259
260 for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
261 /* hlStart[layer].index > hlEnd[layer].index means no highlighting */
262 hlStart[layer].index = 1;
263 hlStart[layer].nChar = 0;
264 hlEnd[layer].index = 0;
265 hlEnd[layer].nChar = 0;
266
267 DBG_OBJ_ARRATTRSET_NUM ("hlStart", layer, "index", hlStart[layer].index);
268 DBG_OBJ_ARRATTRSET_NUM ("hlStart", layer, "nChar", hlStart[layer].nChar);
269 DBG_OBJ_ARRATTRSET_NUM ("hlEnd", layer, "index", hlEnd[layer].index);
270 DBG_OBJ_ARRATTRSET_NUM ("hlEnd", layer, "nChar", hlEnd[layer].nChar);
271 }
272
274
275 initNewLine ();
276}
277
279{
280 /* make sure not to call a free'd tooltip (very fast overkill) */
281 hoverTooltip = NULL;
282
283 for (int i = 0; i < words->size(); i++)
284 cleanupWord (i);
285
286 for (int i = 0; i < anchors->size(); i++) {
287 Anchor *anchor = anchors->getRef (i);
288 /* This also frees the names (see removeAnchor() and related). */
289 removeAnchor(anchor->name);
290 }
291
292 delete paragraphs;
293 delete lines;
294 delete words;
295 delete anchors;
296
297 /* Make sure we don't own widgets anymore. Necessary before call of
298 parent class destructor. (???) */
299 words = NULL;
300
302}
303
310 Widget **references, int *x, int *y)
311{
312 DBG_OBJ_ENTER ("resize", 0, "sizeRequestImpl", "%d, ...", numPos);
313
314 sizeRequestParams.fill (numPos, references, x, y);
315
316 // We have to rewrap the whole textblock, if (i) the available width (which
317 // is the line break width) has changed, or (ii) if the position within the
318 // float container, and so possibly borders relative to this textblock, have
319 // changed.
320 //
321 // (The latter is a simplification: an over-correct implementation would test
322 // all OOFMs on whether affectsLeftBorder() or affectsRightBorder() returns
323 // true. Also, this may be optimized by distinguishing between floats
324 // generated by this textblock (which would not make rewrapping necessary)
325 // and floats generated by other textblocks (which would).)
326
327 int newLineBreakWidth = getAvailWidth (true);
328 int newFCX, newFCY;
329 bool fcDefined = findSizeRequestReference (OOFM_FLOATS, &newFCX, &newFCY);
330
331 if (newLineBreakWidth != lineBreakWidth ||
332 (fcDefined && (newFCX != wrapRefLinesFCX ||
333 newFCY != wrapRefLinesFCY))) {
334 lineBreakWidth = newLineBreakWidth;
335 wrapRefLines = 0;
336 DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth);
337 DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines);
338
339 if (!fcDefined) {
340 wrapRefLinesFCX = newFCX;
341 wrapRefLinesFCY = newFCY;
342 DBG_OBJ_SET_NUM ("wrapRefLinesFCX", wrapRefLinesFCX);
343 DBG_OBJ_SET_NUM ("wrapRefLinesFCY", wrapRefLinesFCY);
344 }
345 }
346
347 rewrap ();
349
350 if (lines->size () > 0) {
351 Line *firstLine = lines->getRef(0), *lastLine = lines->getLastRef ();
352
353 // Note: the breakSpace of the last line is ignored, so breaks
354 // at the end of a textblock are not visible.
355
357 lastLine->maxLineWidth + leftInnerPadding + boxDiffWidth ();
358
359 // Also regard collapsing of this widget top margin and the top
360 // margin of the first line box:
362 getStyle()->borderWidth.top,
363 getStyle()->margin.top
364 + extraSpace.top,
365 firstLine->borderAscent,
366 firstLine->marginAscent);
367
368 // And here, regard collapsing of this widget bottom margin and the
369 // bottom margin of the last line box:
371 // (BTW, this line:
372 lastLine->top - firstLine->borderAscent + lastLine->borderAscent +
373 // ... is 0 for a block with one line, so special handling
374 // for this case is not necessary.)
376 getStyle()->borderWidth.bottom,
377 getStyle()->margin.bottom + extraSpace.bottom,
378 lastLine->borderDescent, lastLine->marginDescent);
379 } else {
383 }
384
385 if (usesMaxGeneratorWidth ()) {
386 DBG_OBJ_MSGF ("resize", 1,
387 "before considering lineBreakWidth (= %d): %d * (%d + %d)",
392 } else
393 DBG_OBJ_MSG ("resize", 1, "lineBreakWidth needs no consideration");
394
395 DBG_OBJ_MSGF ("resize", 1, "before correction: %d * (%d + %d)",
397
399 false);
400
401 // Dealing with parts out of flow, which may overlap the borders of
402 // the text block. Base lines are ignored here: they do not play a
403 // role (currently) and caring about them (for the future) would
404 // cause too much problems.
405
406 // Notice that the order is not typical: correctRequisition should
407 // be the last call. However, calling correctRequisition after
408 // outOfFlowMgr->getSize may result again in a size which is too
409 // small for floats, so triggering again (and again) the resize
410 // idle function resulting in CPU hogging. See also
411 // getExtremesImpl.
412 //
413 // Is this really what we want? An alternative could be that
414 // OutOfFlowMgr::getSize honours CSS attributes an corrected sizes.
415
417
418 DBG_OBJ_MSGF ("resize", 1, "final: %d * (%d + %d)",
420 DBG_OBJ_LEAVE ();
421}
422
427
432
433int Textblock::calcVerticalBorder (int widgetPadding, int widgetBorder,
434 int widgetMargin, int lineBorderTotal,
435 int lineMarginTotal)
436{
437 DBG_OBJ_ENTER ("resize", 0, "calcVerticalBorder", "%d, %d, %d, %d, %d",
438 widgetPadding, widgetBorder, widgetMargin, lineBorderTotal,
439 lineMarginTotal);
440
441 int result;
442
443 if (widgetPadding == 0 && widgetBorder == 0) {
444 if (lineMarginTotal - lineBorderTotal >= widgetMargin)
445 result = lineMarginTotal;
446 else
447 result = widgetMargin + lineBorderTotal;
448 } else
449 result = lineMarginTotal + widgetPadding + widgetBorder + widgetMargin;
450
451 DBG_OBJ_LEAVE_VAL ("%d", result);
452 return result;
453}
454
467
469{
470 DBG_OBJ_ENTER0 ("resize", 0, "getExtremesSimpl");
471
473
474 if (paragraphs->size () == 0) {
475 /* empty page */
476 extremes->minWidth = 0;
478 extremes->maxWidth = 0;
481 } else {
482 Paragraph *lastPar = paragraphs->getLastRef ();
483 extremes->minWidth = lastPar->maxParMin;
485 extremes->maxWidth = lastPar->maxParMax;
488
489 DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMin = %d (%d)",
490 paragraphs->size () - 1, lastPar->maxParMin,
491 lastPar->maxParMinIntrinsic);
492 DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMax = %d (%d)",
493 paragraphs->size () - 1, lastPar->maxParMax,
494 lastPar->maxParMaxIntrinsic);
495 }
496
497 DBG_OBJ_MSGF ("resize", 0, "after considering paragraphs: %d (%d) / %d (%d)",
500
501 int diff = leftInnerPadding + boxDiffWidth ();
502 extremes->minWidth += diff;
504 extremes->maxWidth += diff;
506 extremes->adjustmentWidth += diff;
507
508 DBG_OBJ_MSGF ("resize", 0, "after adding diff: %d (%d) / %d (%d)",
511
512 // For the order, see similar reasoning in sizeRequestImpl.
513
515
516 DBG_OBJ_MSGF ("resize", 0, "after correction: %d (%d) / %d (%d)",
519
521
522 DBG_OBJ_MSGF ("resize", 0,
523 "finally, after considering OOFM: %d (%d) / %d (%d)",
526 DBG_OBJ_LEAVE ();
527}
528
533
538
540{
541 OOFAwareWidget::notifySetAsTopLevel ();
542
544 DBG_OBJ_SET_NUM ("numSizeReferences", numSizeReferences);
545}
546
548{
549 OOFAwareWidget::notifySetParent ();
550
552 for (int i = 0; i < NUM_OOFM; i++) {
553 if (oofContainer[i] != this) {
554 // avoid duplicates
555 bool found = false;
556 for (int j = 0; !found && j < numSizeReferences; j++)
557 if (oofContainer[i] == oofContainer[j])
558 found = true;
559
560 if (!found)
562 }
563 }
564
565 DBG_OBJ_SET_NUM ("numSizeReferences", numSizeReferences);
566 for (int i = 0; i < numSizeReferences; i++)
567 DBG_OBJ_ARRSET_PTR ("sizeReferences", i, sizeReferences[i]);
568}
569
571{
572 DBG_OBJ_ENTER ("resize", 0, "sizeAllocateImpl", "%d, %d; %d * (%d + %d)",
575
577
579
580 int lineIndex, wordIndex;
581 Line *line;
582 Word *word;
583 int xCursor;
584 core::Allocation childAllocation;
585 core::Allocation *oldChildAllocation;
586
587 if (allocation->x != this->allocation.x ||
588 allocation->y != this->allocation.y ||
589 allocation->width != this->allocation.width) {
590 redrawY = 0;
591 DBG_OBJ_SET_NUM ("redrawY", redrawY);
592 }
593
595
596 for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) {
597 DBG_OBJ_MSGF ("resize", 1, "line %d", lineIndex);
599
600 // Especially for floats, allocation->width may be different
601 // from the line break width, so that for centered and right
602 // text, the offsets have to be recalculated again. However, if
603 // the allocation width is greater than the line break width,
604 // due to wide unbreakable lines (large image etc.), use the
605 // original line break width.
606 //
607 // TODO: test case?
608
610
611 line = lines->getRef (lineIndex);
612 xCursor = line->textOffset;
613
614 DBG_OBJ_MSGF ("resize", 1, "xCursor = %d (initially)", xCursor);
615
616 for (wordIndex = line->firstWord; wordIndex <= line->lastWord;
617 wordIndex++) {
618 word = words->getRef (wordIndex);
619
620 if (wordIndex == lastWordDrawn + 1) {
622 DBG_OBJ_SET_NUM ("redrawY", redrawY);
623 }
624
626 DBG_OBJ_MSGF ("resize", 1,
627 "allocating widget in flow: line %d, word %d",
628 lineIndex, wordIndex);
629
630 // TODO For word->flags & Word::TOPLEFT_OF_LINE, make
631 // allocation consistent with calcSizeOfWidgetInFlow():
632
633 childAllocation.x = xCursor + allocation->x;
634
636 /* align=top:
637 childAllocation.y = line->top + allocation->y;
638 */
639 /* align=bottom (base line) */
640 /* Commented lines break the n2 and n3 test cases at
641 * https://dillo-browser.github.io/old/test/img/ */
642 childAllocation.y = lineYOffsetCanvas (line, allocation)
643 + (line->borderAscent - word->size.ascent);
644
645 childAllocation.width = word->size.width;
646 childAllocation.ascent = word->size.ascent;
647 childAllocation.descent = word->size.descent;
648
649 oldChildAllocation = word->content.widget->getAllocation();
650
651 if (childAllocation.x != oldChildAllocation->x ||
652 childAllocation.y != oldChildAllocation->y ||
653 childAllocation.width != oldChildAllocation->width) {
654 /* The child widget has changed its position or its width
655 * so we need to redraw from this line onwards.
656 */
657 redrawY =
659 DBG_OBJ_SET_NUM ("redrawY", redrawY);
660 if (word->content.widget->wasAllocated ()) {
662 oldChildAllocation->y - this->allocation.y);
663 DBG_OBJ_SET_NUM ("redrawY", redrawY);
664 }
665
666 } else if (childAllocation.ascent + childAllocation.descent !=
667 oldChildAllocation->ascent + oldChildAllocation->descent) {
668 /* The child widget has changed its height. We need to redraw
669 * from where it changed.
670 * It's important not to draw from the line base, because the
671 * child might be a table covering the whole page so we would
672 * end up redrawing the whole screen over and over.
673 * The drawing of the child content is left to the child itself.
674 * However this optimization is only possible if the widget is
675 * the only word in the line apart from an optional BREAK.
676 * Otherwise the height change of the widget could change the
677 * position of other words in the line, requiring a
678 * redraw of the complete line.
679 */
680 if (line->lastWord == line->firstWord ||
681 (line->lastWord == line->firstWord + 1 &&
682 words->getRef (line->lastWord)->content.type ==
684
685 int childChangedY =
686 misc::min(childAllocation.y - allocation->y +
687 childAllocation.ascent + childAllocation.descent,
688 oldChildAllocation->y - this->allocation.y +
689 oldChildAllocation->ascent +
690 oldChildAllocation->descent);
691
692 redrawY = misc::min (redrawY, childChangedY);
693 DBG_OBJ_SET_NUM ("redrawY", redrawY);
694 } else {
695 redrawY =
697 DBG_OBJ_SET_NUM ("redrawY", redrawY);
698 }
699 }
700 word->content.widget->sizeAllocate (&childAllocation);
701 }
702
703 xCursor += (word->size.width + word->effSpace);
704 DBG_OBJ_MSGF ("resize", 1, "xCursor = %d (after word %d)",
705 xCursor, wordIndex);
706 DBG_MSG_WORD ("resize", 1, "<i>that is:</i> ", wordIndex, "");
707 }
708
710 }
711
713
715
716 for (int i = 0; i < anchors->size(); i++) {
717 Anchor *anchor = anchors->getRef(i);
718 int y;
719
720 if (anchor->wordIndex >= words->size() ||
721 // Also regard not-yet-existing lines.
722 lines->size () <= 0 ||
723 anchor->wordIndex > lines->getLastRef()->lastWord) {
725 } else {
726 Line *line = lines->getRef(findLineOfWord (anchor->wordIndex));
727 y = lineYOffsetCanvas (line, allocation);
728 }
729 changeAnchor (anchor->name, y);
730 }
731
732 DBG_OBJ_LEAVE ();
733}
734
735void Textblock::calcExtraSpaceImpl (int numPos, Widget **references, int *x,
736 int *y)
737{
738 DBG_OBJ_ENTER0 ("resize", 0, "Textblock::calcExtraSpaceImpl");
739
740 sizeRequestParams.fill (numPos, references, x, y);
741
742 OOFAwareWidget::calcExtraSpaceImpl (numPos, references, x, y);
743
744 int clearPosition = 0;
745 for (int i = 0; i < NUM_OOFM; i++)
746 if (searchOutOfFlowMgr (i) && findSizeRequestReference (i, NULL, NULL))
747 clearPosition =
748 misc::max (clearPosition,
749 searchOutOfFlowMgr(i)->getClearPosition (this));
750
751 extraSpace.top = misc::max (extraSpace.top, clearPosition);
752
753 DBG_OBJ_LEAVE ();
754}
755
756int Textblock::getAvailWidthOfChild (Widget *child, bool forceValue)
757{
758 DBG_OBJ_ENTER ("resize", 0, "Textblock::getAvailWidthOfChild", "%p, %s",
759 child, forceValue ? "true" : "false");
760
761 int width;
762
763 if (isWidgetOOF (child) && getWidgetOutOfFlowMgr(child) &&
765 width =
766 getWidgetOutOfFlowMgr(child)->getAvailWidthOfChild (child,forceValue);
767 else {
768 if (child->getStyle()->width == core::style::LENGTH_AUTO) {
769 // No width specified: similar to standard implementation (see
770 // there), but "leftInnerPadding" has to be considered, too.
771 DBG_OBJ_MSG ("resize", 1, "no specification");
772 if (forceValue) {
773 width = misc::max (getAvailWidth (true) - boxDiffWidth ()
775 0);
776
777 if (width != -1) {
778 /* Clamp to min-width and max-width if given, taking into
779 * account leftInnerPadding. */
780 int maxWidth = child->calcWidth (child->getStyle()->maxWidth,
781 -1, this, -1, false);
782 if (maxWidth != -1 && width > maxWidth - leftInnerPadding)
783 width = maxWidth - leftInnerPadding;
784
785 int minWidth = child->calcWidth (child->getStyle()->minWidth,
786 -1, this, -1, false);
787 if (minWidth != -1 && width < minWidth - leftInnerPadding)
788 width = minWidth - leftInnerPadding;
789 }
790
791 } else {
792 width = -1;
793 }
794 } else
795 width = Widget::getAvailWidthOfChild (child, forceValue);
796
797 if (forceValue && this == child->getContainer () &&
803 }
804 }
805
806 DBG_OBJ_LEAVE_VAL ("%d", width);
807 return width;
808}
809
811{
812 if (isWidgetOOF(child) && getWidgetOutOfFlowMgr(child) &&
813 getWidgetOutOfFlowMgr(child)->dealingWithSizeOfChild (child))
814 return getWidgetOutOfFlowMgr(child)->getAvailHeightOfChild (child,
815 forceValue);
816 else
817 return Widget::getAvailHeightOfChild (child, forceValue);
818}
819
821{
822 DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
823
824 for (int i = 0; i < words->size (); i++) {
825 Word *word = words->getRef (i);
828 }
829
831
832 DBG_OBJ_LEAVE ();
833}
834
836{
837 DBG_OBJ_ENTER ("resize", 0,
838 "Textblock/affectsSizeChangeContainerChild", "%p", child);
839
840 // See Textblock::getAvailWidthOfChild() and Textblock::oofSizeChanged():
841 // Extremes changes affect the size of the child, too:
842 bool ret;
843 if (!usesMaxGeneratorWidth () &&
845 ret = true;
846 else
847 ret = Widget::affectsSizeChangeContainerChild (child);
848
849 DBG_OBJ_LEAVE_VAL ("%s", boolToStr(ret));
850 return ret;
851}
852
854{
855 return true;
856}
857
859{
860 DBG_OBJ_ENTER0 ("draw", 0, "resizeDrawImpl");
861
863 if (lines->size () > 0) {
864 Line *lastLine = lines->getRef (lines->size () - 1);
865 /* Remember the last word that has been drawn so we can ensure to
866 * draw any new added words (see sizeAllocateImpl()).
867 */
868 lastWordDrawn = lastLine->lastWord;
869 DBG_OBJ_SET_NUM ("lastWordDrawn", lastWordDrawn);
870 }
871
872 redrawY = getHeight ();
873 DBG_OBJ_SET_NUM ("redrawY", redrawY);
874
875 DBG_OBJ_LEAVE ();
876}
877
879{
880 DBG_OBJ_ENTER ("resize", 0, "markSizeChange", "%d", ref);
881
882 if (isParentRefOOF (ref))
885 else {
886 /* By the way: ref == -1 may have two different causes: (i) flush()
887 calls "queueResize (-1, true)", when no rewrapping is necessary;
888 and (ii) a word may have parentRef == -1 , when it is not yet
889 added to a line. In the latter case, nothing has to be done
890 now, but addLine(...) will do everything necessary. */
891 if (ref != -1) {
892 if (wrapRefLines == -1)
894 else
897 }
898
899 DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines);
900
901 // It seems that sometimes (even without floats) the lines
902 // structure is changed, so that wrapRefLines may refers to a
903 // line which does not exist anymore. Should be examined
904 // again. Until then, setting wrapRefLines to the same value is
905 // a workaround.
906 markExtremesChange (ref);
907 }
908
909 DBG_OBJ_LEAVE ();
910}
911
913{
914 DBG_OBJ_ENTER ("resize", 1, "markExtremesChange", "%d", ref);
915
916 if (isParentRefOOF (ref))
919 else {
920 /* By the way: ref == -1 may have two different causes: (i) flush()
921 calls "queueResize (-1, true)", when no rewrapping is necessary;
922 and (ii) a word may have parentRef == -1 , when it is not yet
923 added to a line. In the latter case, nothing has to be done
924 now, but addLine(...) will do everything necessary. */
925 if (ref != -1) {
926 if (wrapRefParagraphs == -1)
928 else
931 }
932
933 DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs);
934 }
935
936 DBG_OBJ_LEAVE ();
937}
938
940{
941 return true;
942}
943
948
953
954/*
955 * Handle motion inside the widget
956 * (special care is necessary when switching from another widget,
957 * because hoverLink and hoverTooltip are meaningless then).
958 */
960{
961 if (event->state & core::BUTTON1_MASK)
963 else {
964 bool inSpace;
965 int linkOld = hoverLink;
967 const Word *word = findWord (event->xWidget, event->yWidget, &inSpace);
968
969 // cursor from word or widget style
970 if (word == NULL) {
971 setCursor (getStyle()->cursor);
972 hoverLink = -1;
973 hoverTooltip = NULL;
974 } else {
975 core::style::Style *style = inSpace ? word->spaceStyle : word->style;
979 }
980
981 // Show/hide tooltip
982 if (tooltipOld != hoverTooltip) {
983 if (tooltipOld)
984 tooltipOld->onLeave ();
985 if (hoverTooltip)
987 } else if (hoverTooltip)
989
990 _MSG("MN tb=%p tooltipOld=%p hoverTooltip=%p\n",
991 this, tooltipOld, hoverTooltip);
992 if (hoverLink != linkOld) {
993 /* LinkEnter with hoverLink == -1 is the same as LinkLeave */
994 return layout->emitLinkEnter (this, hoverLink, -1, -1, -1);
995 } else {
996 return hoverLink != -1;
997 }
998 }
999}
1000
1002{
1003 _MSG(" tb=%p, ENTER NotifyImpl hoverTooltip=%p\n", this, hoverTooltip);
1004 /* reset hoverLink so linkEnter is detected */
1005 hoverLink = -2;
1006}
1007
1009{
1010 _MSG(" tb=%p, LEAVE NotifyImpl: hoverTooltip=%p\n", this, hoverTooltip);
1011
1012 /* leaving the viewport can't be handled by motionNotifyImpl() */
1013 if (hoverLink >= 0)
1014 layout->emitLinkEnter (this, -1, -1, -1, -1);
1015
1016 if (hoverTooltip) {
1018 hoverTooltip = NULL;
1019 }
1020}
1021
1027{
1028 DBG_OBJ_ENTER0 ("events", 0, "sendSelectionEvent");
1029
1030 core::Iterator *it;
1031 int wordIndex;
1032 int charPos = 0, link = -1;
1033 bool r;
1034
1035 if (words->size () == 0) {
1036 wordIndex = -1;
1037 } else {
1038 Line *lastLine = lines->getRef (lines->size () - 1);
1039 int yFirst = lineYOffsetCanvas (0);
1040 int yLast = lineYOffsetCanvas (lastLine) + lastLine->borderAscent +
1041 lastLine->borderDescent;
1042 if (event->yCanvas < yFirst) {
1043 // Above the first line: take the first word.
1044 wordIndex = 0;
1045 } else if (event->yCanvas >= yLast) {
1046 // Below the last line: take the last word.
1047 wordIndex = words->size () - 1;
1049 } else {
1050 Line *line =
1051 lines->getRef (findLineIndexWhenAllocated (event->yWidget));
1052
1053 // Pointer within the break space?
1054 if (event->yWidget > (lineYOffsetWidget (line) +
1055 line->borderAscent + line->borderDescent)) {
1056 // Choose this break.
1057 wordIndex = line->lastWord;
1059 } else if (event->xWidget < line->textOffset) {
1060 // Left of the first word in the line.
1061 wordIndex = line->firstWord;
1062 } else {
1063 int nextWordStartX = line->textOffset;
1064
1065 for (wordIndex = line->firstWord;
1066 wordIndex <= line->lastWord;
1067 wordIndex++) {
1068 Word *word = words->getRef (wordIndex);
1069 int wordStartX = nextWordStartX;
1070
1071 nextWordStartX += word->size.width + word->effSpace;
1072
1073 if (event->xWidget >= wordStartX &&
1074 event->xWidget < nextWordStartX) {
1075 // We have found the word.
1076 int yWidgetBase =
1077 lineYOffsetWidget (line) + line->borderAscent;
1078
1079 if (event->xWidget >= nextWordStartX - word->effSpace) {
1081 if (wordIndex < line->lastWord &&
1082 (words->getRef(wordIndex + 1)->content.type !=
1084 (event->yWidget <=
1085 yWidgetBase + word->spaceStyle->font->descent) &&
1086 (event->yWidget >
1087 yWidgetBase - word->spaceStyle->font->ascent)) {
1088 link = word->spaceStyle->x_link;
1089 }
1090 } else {
1091 if (event->yWidget <= yWidgetBase + word->size.descent &&
1092 event->yWidget > yWidgetBase - word->size.ascent) {
1093 link = word->style->x_link;
1094 }
1095 if (word->content.type == core::Content::TEXT) {
1096 int glyphX = wordStartX;
1097 int isStartWord = word->flags & Word::WORD_START;
1098 int isEndWord = word->flags & Word::WORD_END;
1099
1100 while (1) {
1101 int nextCharPos =
1102 layout->nextGlyph (word->content.text, charPos);
1103 // TODO The width of a text is not the sum
1104 // of the widths of the glyphs, because of
1105 // ligatures, kerning etc., so textWidth
1106 // should be applied to the text from 0 to
1107 // nextCharPos. (Or not? See comment below.)
1108
1109 int glyphWidth =
1110 textWidth (word->content.text, charPos,
1111 nextCharPos - charPos, word->style,
1112 isStartWord && charPos == 0,
1113 isEndWord &&
1114 word->content.text[nextCharPos] == 0);
1115 if (event->xWidget > glyphX + glyphWidth) {
1116 glyphX += glyphWidth;
1117 charPos = nextCharPos;
1118 continue;
1119 } else if (event->xWidget >= glyphX + glyphWidth/2){
1120 // On the right half of a character;
1121 // now just look for combining chars
1122 charPos = nextCharPos;
1123 while (word->content.text[charPos]) {
1124 nextCharPos =
1125 layout->nextGlyph (word->content.text,
1126 charPos);
1127 if (textWidth (word->content.text, charPos,
1128 nextCharPos - charPos,
1129 word->style,
1130 isStartWord && charPos == 0,
1131 isEndWord &&
1132 word->content.text[nextCharPos]
1133 == 0))
1134 break;
1135 charPos = nextCharPos;
1136 }
1137 }
1138 break;
1139 }
1140 } else {
1141 // Depends on whether the pointer is within the left or
1142 // right half of the (non-text) word.
1143 if (event->xWidget >= (wordStartX + nextWordStartX) /2)
1145 }
1146 }
1147 break;
1148 }
1149 }
1150 if (wordIndex > line->lastWord) {
1151 // No word found in this line (i.e. we are on the right side),
1152 // take the last of this line.
1153 wordIndex = line->lastWord;
1155 }
1156 }
1157 }
1158 }
1159
1160 DBG_OBJ_MSGF ("events", 1, "wordIndex = %d", wordIndex);
1161 DBG_MSG_WORD ("events", 1, "<i>this is:</i> ", wordIndex, "");
1162
1164 (this, core::Content::maskForSelection (true), wordIndex);
1165 r = selectionHandleEvent (eventType, it, charPos, link, event);
1166 it->unref ();
1167
1168 DBG_OBJ_LEAVE_VAL ("%s", boolToStr(r));
1169 return r;
1170}
1171
1173{
1175}
1176
1178{
1179 return new TextblockIterator (this, mask, atEnd);
1180}
1181
1182/*
1183 * Draw the decorations on a word.
1184 */
1187 int x, int yBase, int width)
1188{
1189 int y, height;
1190
1191 height = 1 + style->font->xHeight / 12;
1193 y = yBase + style->font->descent / 3;
1194 view->drawRectangle (style->color, shading, true, x, y, width, height);
1195 }
1197 y = yBase - style->font->ascent;
1198 view->drawRectangle (style->color, shading, true, x, y, width, height);
1199 }
1201 y = yBase + (style->font->descent - style->font->ascent) / 2 +
1202 style->font->descent / 4;
1203 view->drawRectangle (style->color, shading, true, x, y, width, height);
1204 }
1205}
1206
1207/*
1208 * Draw a string of text
1209 *
1210 * Arguments: ... "isStart" and "isEnd" are true, when the text
1211 * start/end represents the start/end of a "real" text word (before
1212 * hyphenation). This has an effect on text transformation. ("isEnd"
1213 * is not used yet, but here for symmetry.)
1214 */
1216 core::style::Color::Shading shading, int x, int y,
1217 const char *text, int start, int len, bool isStart,
1218 bool isEnd)
1219{
1220 if (len > 0) {
1221 char *str = NULL;
1222
1223 switch (style->textTransform) {
1225 default:
1226 break;
1228 str = layout->textToUpper(text + start, len);
1229 break;
1231 str = layout->textToLower(text + start, len);
1232 break;
1234 // If "isStart" is false, the first letter of "text" is
1235 // not the first letter of the "real" text word, so no
1236 // transformation is necessary.
1237 if (isStart) {
1238 /* \bug No way to know about non-ASCII punctuation. */
1239 bool initial_seen = false;
1240
1241 for (int i = 0; i < start; i++)
1242 if (!dIspunct(text[i]))
1243 initial_seen = true;
1244 if (initial_seen)
1245 break;
1246
1247 int after = 0;
1248 text += start;
1249 while (dIspunct(text[after]))
1250 after++;
1251 if (text[after])
1252 after = layout->nextGlyph(text, after);
1253 if (after > len)
1254 after = len;
1255
1256 char *initial = layout->textToUpper(text, after);
1257 int newlen = strlen(initial) + len-after;
1258 str = (char *)malloc(newlen + 1);
1259 strcpy(str, initial);
1260 strncpy(str + strlen(str), text+after, len-after);
1261 str[newlen] = '\0';
1262 free(initial);
1263 }
1264 break;
1265 }
1266
1267 view->drawText(style->font, style->color, shading, x, y,
1268 str ? str : text + start, str ? strlen(str) : len);
1269 if (str)
1270 free(str);
1271 }
1272}
1273
1282void Textblock::drawWord (Line *line, int wordIndex1, int wordIndex2,
1283 core::View *view, core::Rectangle *area,
1284 int xWidget, int yWidgetBase)
1285{
1286 core::style::Style *style = words->getRef(wordIndex1)->style;
1287 bool drawHyphen = wordIndex2 == line->lastWord
1288 && (words->getRef(wordIndex2)->flags & Word::DIV_CHAR_AT_EOL);
1289
1290 if (style->hasBackground ()) {
1291 int w = 0;
1292 for (int i = wordIndex1; i <= wordIndex2; i++)
1293 w += words->getRef(i)->size.width;
1294 w += words->getRef(wordIndex2)->hyphenWidth;
1295 drawBox (view, style, area, xWidget, yWidgetBase - line->borderAscent,
1296 w, line->borderAscent + line->borderDescent, false);
1297 }
1298
1299 if (wordIndex1 == wordIndex2 && !drawHyphen) {
1300 // Simple case, where copying in one buffer is not needed.
1301 Word *word = words->getRef (wordIndex1);
1302 drawWord0 (wordIndex1, wordIndex2, word->content.text, word->size.width,
1303 false, style, view, area, xWidget, yWidgetBase);
1304 } else {
1305 // Concatenate all words in a new buffer.
1306 int l = 0, totalWidth = 0;
1307 for (int i = wordIndex1; i <= wordIndex2; i++) {
1308 Word *w = words->getRef (i);
1309 l += strlen (w->content.text);
1310 totalWidth += w->size.width;
1311 }
1312
1313 char *text = new char[l + (drawHyphen ? strlen (hyphenDrawChar) : 0) + 1];
1314 int p = 0;
1315 for (int i = wordIndex1; i <= wordIndex2; i++) {
1316 const char * t = words->getRef(i)->content.text;
1317 strcpy (text + p, t);
1318 p += strlen (t);
1319 }
1320
1321 if(drawHyphen) {
1322 for (int i = 0; hyphenDrawChar[i]; i++)
1323 text[p++] = hyphenDrawChar[i];
1324 text[p++] = 0;
1325 }
1326
1327 drawWord0 (wordIndex1, wordIndex2, text, totalWidth, drawHyphen,
1328 style, view, area, xWidget, yWidgetBase);
1329
1330 delete[] text;
1331 }
1332}
1333
1337void Textblock::drawWord0 (int wordIndex1, int wordIndex2,
1338 const char *text, int totalWidth, bool drawHyphen,
1340 core::Rectangle *area, int xWidget, int yWidgetBase)
1341{
1342 int xWorld = allocation.x + xWidget;
1343 int yWorldBase;
1344
1345 /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */
1347 yWidgetBase += style->font->ascent / 3;
1348 else if (style->valign == core::style::VALIGN_SUPER) {
1349 yWidgetBase -= style->font->ascent / 2;
1350 }
1351 yWorldBase = yWidgetBase + allocation.y;
1352
1353 bool isStartTotal = words->getRef(wordIndex1)->flags & Word::WORD_START;
1354 bool isEndTotal = words->getRef(wordIndex2)->flags & Word::WORD_START;
1356 yWorldBase, text, 0, strlen (text), isStartTotal, isEndTotal);
1357
1358 if (style->textDecoration)
1360 yWorldBase, totalWidth);
1361
1362 for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
1363 if (wordIndex1 <= hlEnd[layer].index &&
1364 wordIndex2 >= hlStart[layer].index) {
1365 const int wordLen = strlen (text);
1366 int xStart, width;
1367 int firstCharIdx;
1368 int lastCharIdx;
1369
1370 if (hlStart[layer].index < wordIndex1)
1371 firstCharIdx = 0;
1372 else {
1373 firstCharIdx =
1374 misc::min (hlStart[layer].nChar,
1375 (int)strlen (words->getRef(hlStart[layer].index)
1376 ->content.text));
1377 for (int i = wordIndex1; i < hlStart[layer].index; i++)
1378 // It can be assumed that all words from wordIndex1 to
1379 // wordIndex2 have content type TEXT.
1380 firstCharIdx += strlen (words->getRef(i)->content.text);
1381 }
1382
1383 if (hlEnd[layer].index > wordIndex2)
1384 lastCharIdx = wordLen;
1385 else {
1386 lastCharIdx =
1387 misc::min (hlEnd[layer].nChar,
1388 (int)strlen (words->getRef(hlEnd[layer].index)
1389 ->content.text));
1390 for (int i = wordIndex1; i < hlEnd[layer].index; i++)
1391 // It can be assumed that all words from wordIndex1 to
1392 // wordIndex2 have content type TEXT.
1393 lastCharIdx += strlen (words->getRef(i)->content.text);
1394 }
1395
1396 xStart = xWorld;
1397 if (firstCharIdx)
1398 xStart += textWidth (text, 0, firstCharIdx, style,
1399 isStartTotal,
1400 isEndTotal && text[firstCharIdx] == 0);
1401 // With a hyphen, the width is a bit longer than totalWidth,
1402 // and so, the optimization to use totalWidth is not correct.
1403 if (!drawHyphen && firstCharIdx == 0 && lastCharIdx == wordLen)
1404 width = totalWidth;
1405 else
1406 width = textWidth (text, firstCharIdx,
1407 lastCharIdx - firstCharIdx, style,
1408 isStartTotal && firstCharIdx == 0,
1409 isEndTotal && text[lastCharIdx] == 0);
1410 if (width > 0) {
1411 /* Highlight text */
1412 core::style::Color *wordBgColor;
1413
1414 if (!(wordBgColor = style->backgroundColor))
1415 wordBgColor = getBgColor();
1416
1417 /* Draw background for highlighted text. */
1418 view->drawRectangle (
1419 wordBgColor, core::style::Color::SHADING_INVERSE, true, xStart,
1420 yWorldBase - style->font->ascent, width,
1422
1423 /* Highlight the text. */
1425 yWorldBase, text, firstCharIdx,
1426 lastCharIdx - firstCharIdx,
1427 isStartTotal && firstCharIdx == 0,
1428 isEndTotal && lastCharIdx == wordLen);
1429
1430 if (style->textDecoration)
1432 xStart, yWorldBase, width);
1433 }
1434 }
1435 }
1436}
1437
1438/*
1439 * Draw a space.
1440 */
1441void Textblock::drawSpace(int wordIndex, core::View *view,
1442 core::Rectangle *area, int xWidget, int yWidgetBase)
1443{
1444 Word *word = words->getRef(wordIndex);
1445 int xWorld = allocation.x + xWidget;
1446 int yWorldBase;
1448 bool highlight = false;
1449
1450 /* Adjust the space baseline if it is <SUP>-ed or <SUB>-ed */
1452 yWidgetBase += style->font->ascent / 3;
1453 else if (style->valign == core::style::VALIGN_SUPER) {
1454 yWidgetBase -= style->font->ascent / 2;
1455 }
1456 yWorldBase = allocation.y + yWidgetBase;
1457
1458 for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
1459 if (hlStart[layer].index <= wordIndex &&
1460 hlEnd[layer].index > wordIndex) {
1461 highlight = true;
1462 break;
1463 }
1464 }
1465 if (highlight) {
1466 core::style::Color *spaceBgColor;
1467
1468 if (!(spaceBgColor = style->backgroundColor))
1469 spaceBgColor = getBgColor();
1470
1471 view->drawRectangle (
1472 spaceBgColor, core::style::Color::SHADING_INVERSE, true, xWorld,
1473 yWorldBase - style->font->ascent, word->effSpace,
1475 }
1476 if (style->textDecoration) {
1477 core::style::Color::Shading shading = highlight ?
1480
1481 decorateText(view, style, shading, xWorld, yWorldBase, word->effSpace);
1482 }
1483}
1484
1485/*
1486 * Paint a line
1487 * - x and y are toplevel dw coordinates (Question: what Dw? Changed. Test!)
1488 * - area is used always (ev. set it to event->area)
1489 * - event is only used when is_expose
1490 */
1492 core::DrawingContext *context)
1493{
1494 DBG_OBJ_ENTER ("draw", 0, "drawLine", "..., [%d, %d, %d * %d]",
1495 area->x, area->y, area->width, area->height);
1496
1497 int xWidget = line->textOffset;
1498 int yWidgetBase = lineYOffsetWidget (line) + line->borderAscent;
1499
1500 DBG_OBJ_MSGF ("draw", 1, "line from %d to %d (%d words), at (%d, %d)",
1501 line->firstWord, line->lastWord, words->size (),
1502 xWidget, yWidgetBase);
1503 DBG_MSG_WORD ("draw", 1, "<i>line starts with: </i>", line->firstWord, "");
1504 DBG_MSG_WORD ("draw", 1, "<i>line ends with: </i>", line->lastWord, "");
1505
1507
1508 for (int wordIndex = line->firstWord;
1509 wordIndex <= line->lastWord && xWidget < area->x + area->width;
1510 wordIndex++) {
1511 DBG_MSG_WORD ("draw", 2, "<i>drawing: </i>", wordIndex, "");
1512
1513 Word *word = words->getRef (wordIndex);
1514 int wordSize = word->size.width;
1515
1516 if (xWidget + wordSize + word->hyphenWidth + word->effSpace >= area->x) {
1517 if (word->content.type == core::Content::TEXT ||
1519
1520 if (word->size.width > 0) {
1522 core::Widget *child = word->content.widget;
1523 core::Rectangle childArea;
1525 (child) &&
1526 child->intersects (this, area, &childArea))
1527 child->draw (view, &childArea, context);
1528 } else {
1529 int wordIndex2 = wordIndex;
1530 while (wordIndex2 < line->lastWord &&
1531 (words->getRef(wordIndex2)->flags
1533 word->style == words->getRef(wordIndex2 + 1)->style)
1534 wordIndex2++;
1535
1536 drawWord(line, wordIndex, wordIndex2, view, area,
1537 xWidget, yWidgetBase);
1538 wordSize = 0;
1539 for (int i = wordIndex; i <= wordIndex2; i++)
1540 wordSize += words->getRef(i)->size.width;
1541
1542 wordIndex = wordIndex2;
1543 word = words->getRef(wordIndex);
1544 }
1545 }
1546
1547 if (word->effSpace > 0 && wordIndex < line->lastWord &&
1548 words->getRef(wordIndex + 1)->content.type !=
1550 if (word->spaceStyle->hasBackground ())
1551 drawBox (view, word->spaceStyle, area,
1552 xWidget + wordSize,
1553 yWidgetBase - line->borderAscent, word->effSpace,
1554 line->borderAscent + line->borderDescent, false);
1555 drawSpace (wordIndex, view, area, xWidget + wordSize,
1556 yWidgetBase);
1557 }
1558
1559 }
1560 }
1561 xWidget += wordSize + word->effSpace;
1562 }
1563
1564 DBG_OBJ_MSG_END ();
1565
1566 DBG_OBJ_LEAVE ();
1567}
1568
1578
1580{
1581 if (lines->size() == 0)
1582 return -1;
1583 else
1584 return
1586 getStyle()->borderWidth.top,
1587 getStyle()->margin.top
1588 + extraSpace.top,
1589 lines->getRef(0)->borderAscent,
1590 lines->getRef(0)->marginAscent));
1591}
1592
1594{
1595 assert (wasAllocated ());
1596 return findLineIndex (y, allocation.ascent);
1597}
1598
1599int Textblock::findLineIndex (int y, int ascent)
1600{
1601 DBG_OBJ_ENTER ("events", 0, "findLineIndex", "%d, %d", y, ascent);
1602
1603 core::Allocation alloc;
1604 alloc.ascent = ascent; // More is not needed.
1605
1606 int maxIndex = lines->size () - 1;
1607 int step, index, low = 0;
1608
1609 step = (lines->size() + 1) >> 1;
1610 while ( step > 1 ) {
1611 index = low + step;
1612 if (index <= maxIndex && lineYOffsetWidget (index, &alloc) <= y)
1613 low = index;
1614 step = (step + 1) >> 1;
1615 }
1616
1617 if (low < maxIndex && lineYOffsetWidget (low + 1, &alloc) <= y)
1618 low++;
1619
1620 /*
1621 * This new routine returns the line number between (top) and
1622 * (top + size.ascent + size.descent + breakSpace): the space
1623 * _below_ the line is considered part of the line. Old routine
1624 * returned line number between (top - previous_line->breakSpace)
1625 * and (top + size.ascent + size.descent): the space _above_ the
1626 * line was considered part of the line. This is important for
1627 * Dw_page_find_link() --EG
1628 * That function has now been inlined into Dw_page_motion_notify() --JV
1629 */
1630
1631 DBG_OBJ_LEAVE_VAL ("%d", low);
1632 return low;
1633}
1634
1638int Textblock::findLineOfWord (int wordIndex)
1639{
1640 if (wordIndex < 0 || wordIndex >= words->size () ||
1641 // Also regard not-yet-existing lines.
1642 lines->size () <= 0 || wordIndex > lines->getLastRef()->lastWord)
1643 return -1;
1644
1645 int high = lines->size () - 1, index, low = 0;
1646
1647 while (true) {
1648 index = (low + high) / 2;
1649 if (wordIndex >= lines->getRef(index)->firstWord) {
1650 if (wordIndex <= lines->getRef(index)->lastWord)
1651 return index;
1652 else
1653 low = index + 1;
1654 } else
1655 high = index - 1;
1656 }
1657}
1658
1663{
1664 int high = paragraphs->size () - 1, index, low = 0;
1665
1666 if (wordIndex < 0 || wordIndex >= words->size () ||
1667 // It may be that the paragraphs list is incomplete. But look
1668 // also at fillParagraphs, where this method is called.
1669 (paragraphs->size () == 0 ||
1670 wordIndex > paragraphs->getLastRef()->lastWord))
1671 return -1;
1672
1673 while (true) {
1674 index = (low + high) / 2;
1675 if (wordIndex >= paragraphs->getRef(index)->firstWord) {
1676 if (wordIndex <= paragraphs->getRef(index)->lastWord)
1677 return index;
1678 else
1679 low = index + 1;
1680 } else
1681 high = index - 1;
1682 }
1683}
1684
1688Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace)
1689{
1690 int lineIndex, wordIndex;
1691 int xCursor, lastXCursor, yWidgetBase;
1692 Line *line;
1693 Word *word;
1694
1695 *inSpace = false;
1696
1697 if ((lineIndex = findLineIndexWhenAllocated (y)) >= lines->size ())
1698 return NULL;
1699 line = lines->getRef (lineIndex);
1700 yWidgetBase = lineYOffsetWidget (line) + line->borderAscent;
1701 if (yWidgetBase + line->borderDescent <= y)
1702 return NULL;
1703
1704 xCursor = line->textOffset;
1705 for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
1706 word = words->getRef (wordIndex);
1707 lastXCursor = xCursor;
1708 xCursor += word->size.width + word->effSpace;
1709 if (lastXCursor <= x && xCursor > x) {
1710 if (x >= xCursor - word->effSpace) {
1711 if (wordIndex < line->lastWord &&
1712 (words->getRef(wordIndex + 1)->content.type !=
1714 y > yWidgetBase - word->spaceStyle->font->ascent &&
1715 y <= yWidgetBase + word->spaceStyle->font->descent) {
1716 *inSpace = true;
1717 return word;
1718 }
1719 } else {
1720 if (y > yWidgetBase - word->size.ascent &&
1721 y <= yWidgetBase + word->size.descent)
1722 return word;
1723 }
1724 break;
1725 }
1726 }
1727
1728 return NULL;
1729}
1730
1732 int level, core::DrawingContext *context)
1733{
1734 DBG_OBJ_ENTER ("draw", 0, "Textblock::drawLevel", "(%d, %d, %d * %d), %s",
1735 area->x, area->y, area->width, area->height,
1736 stackingLevelText (level));
1737
1738 switch (level) {
1739 case SL_IN_FLOW:
1740 for (int lineIndex = findLineIndexWhenAllocated (area->y);
1741 lineIndex < lines->size (); lineIndex++) {
1742 Line *line = lines->getRef (lineIndex);
1743 if (lineYOffsetWidget (line) >= area->y + area->height)
1744 break;
1745
1746 DBG_OBJ_MSGF ("draw", 0, "line %d (of %d)", lineIndex, lines->size ());
1747 drawLine (line, view, area, context);
1748 }
1749 break;
1750
1751 case SL_OOF_REF:
1752 // TODO Inefficient. Perhaps store OOF references in separate
1753 // (much smaller!) list.
1754 for (int oofmIndex = 0; oofmIndex < NUM_OOFM; oofmIndex++) {
1755 for (int wordIndex = 0; wordIndex < words->size (); wordIndex++) {
1756 Word *word = words->getRef (wordIndex);
1759 == oofmIndex &&
1761 word->content.widget->drawInterruption (view, area, context);
1762 }
1763 }
1764 break;
1765
1766 default:
1767 OOFAwareWidget::drawLevel (view, area, level, context);
1768 break;
1769 }
1770
1771 DBG_OBJ_LEAVE ();
1772}
1773
1777Textblock::Word *Textblock::addWord (int width, int ascent, int descent,
1779{
1780 DBG_OBJ_ENTER ("construct.word", 0, "addWord", "%d * (%d + %d), %d, %p",
1781 width, ascent, descent, flags, style);
1782
1783 if (lineBreakWidth == -1) {
1785 DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth);
1786 }
1787
1788 words->increase ();
1789 DBG_OBJ_SET_NUM ("words.size", words->size ());
1790 int wordNo = words->size () - 1;
1791 initWord (wordNo);
1792 fillWord (wordNo, width, ascent, descent, flags, style);
1793
1794 DBG_OBJ_LEAVE ();
1795 return words->getRef (wordNo);
1796}
1797
1801void Textblock::initWord (int wordNo)
1802{
1803 Word *word = words->getRef (wordNo);
1804
1805 word->style = word->spaceStyle = NULL;
1806 word->wordImgRenderer = NULL;
1807 word->spaceImgRenderer = NULL;
1808}
1809
1810void Textblock::cleanupWord (int wordNo)
1811{
1812 Word *word = words->getRef (wordNo);
1813
1815 delete word->content.widget;
1817 delete word->content.widgetReference;
1820 removeWordImgRenderer (wordNo);
1821 removeSpaceImgRenderer (wordNo);
1822
1823 word->style->unref ();
1824 word->spaceStyle->unref ();
1825}
1826
1828{
1829 Word *word = words->getRef (wordNo);
1830
1831 if (word->style && word->wordImgRenderer) {
1833 (word->wordImgRenderer);
1834 delete word->wordImgRenderer;
1835 word->wordImgRenderer = NULL;
1836 }
1837}
1838
1840{
1841 Word *word = words->getRef (wordNo);
1842
1843 if (word->style->backgroundImage) {
1844 word->wordImgRenderer = new WordImgRenderer (this, wordNo);
1846 (word->wordImgRenderer);
1847 } else
1848 word->wordImgRenderer = NULL;
1849}
1850
1852{
1853 Word *word = words->getRef (wordNo);
1854
1855 if (word->spaceStyle && word->spaceImgRenderer) {
1857 (word->spaceImgRenderer);
1858 delete word->spaceImgRenderer;
1859 word->spaceImgRenderer = NULL;
1860 }
1861}
1862
1864{
1865 Word *word = words->getRef (wordNo);
1866
1867 if (word->spaceStyle->backgroundImage) {
1868 word->spaceImgRenderer = new SpaceImgRenderer (this, wordNo);
1870 (word->spaceImgRenderer);
1871 } else
1872 word->spaceImgRenderer = NULL;
1873}
1874
1875void Textblock::fillWord (int wordNo, int width, int ascent, int descent,
1877{
1878 Word *word = words->getRef (wordNo);
1879
1880 word->size.width = width;
1881 word->size.ascent = ascent;
1882 word->size.descent = descent;
1883 DBG_SET_WORD_SIZE (wordNo);
1884 word->origSpace = word->effSpace = 0;
1885 word->hyphenWidth = 0;
1887 word->content.space = false;
1888 word->flags = flags;
1889
1890 removeWordImgRenderer (wordNo);
1891 removeSpaceImgRenderer (wordNo);
1892
1893 word->style = style;
1894 word->spaceStyle = style;
1895
1896 setWordImgRenderer (wordNo);
1897 setSpaceImgRenderer (wordNo);
1898
1899 style->ref ();
1900 style->ref ();
1901}
1902
1903/*
1904 * Get the width of a string of text.
1905 *
1906 * For "isStart" and "isEnd" see drawText.
1907 */
1908int Textblock::textWidth(const char *text, int start, int len,
1909 core::style::Style *style, bool isStart, bool isEnd)
1910{
1911 int ret = 0;
1912
1913 if (len > 0) {
1914 char *str = NULL;
1915
1916 switch (style->textTransform) {
1918 default:
1919 ret = layout->textWidth(style->font, text+start, len);
1920 break;
1922 str = layout->textToUpper(text+start, len);
1923 ret = layout->textWidth(style->font, str, strlen(str));
1924 break;
1926 str = layout->textToLower(text+start, len);
1927 ret = layout->textWidth(style->font, str, strlen(str));
1928 break;
1930 if (isStart) {
1931 /* \bug No way to know about non-ASCII punctuation. */
1932 bool initial_seen = false;
1933
1934 for (int i = 0; i < start; i++)
1935 if (!dIspunct(text[i]))
1936 initial_seen = true;
1937 if (initial_seen) {
1938 ret = layout->textWidth(style->font, text+start, len);
1939 } else {
1940 int after = 0;
1941
1942 text += start;
1943 while (dIspunct(text[after]))
1944 after++;
1945 if (text[after])
1946 after = layout->nextGlyph(text, after);
1947 if (after > len)
1948 after = len;
1949 str = layout->textToUpper(text, after);
1950 ret = layout->textWidth(style->font, str, strlen(str)) +
1951 layout->textWidth(style->font, text+after, len-after);
1952 }
1953 } else
1954 ret = layout->textWidth(style->font, text+start, len);
1955 break;
1956 }
1957
1958 if (str)
1959 free(str);
1960 }
1961
1962 return ret;
1963}
1964
1970void Textblock::calcTextSize (const char *text, size_t len,
1972 core::Requisition *size, bool isStart, bool isEnd)
1973{
1974 size->width = textWidth (text, 0, len, style, isStart, isEnd);
1975 size->ascent = style->font->ascent;
1976 size->descent = style->font->descent;
1977
1978 /*
1979 * For 'normal' line height, just use ascent and descent from font.
1980 * For absolute/percentage, line height is relative to font size, which
1981 * is (irritatingly) smaller than ascent+descent.
1982 */
1984 int height, leading;
1985 float factor = style->font->size;
1986
1987 factor /= (style->font->ascent + style->font->descent);
1988
1989 size->ascent = lout::misc::roundInt(size->ascent * factor);
1990 size->descent = lout::misc::roundInt(size->descent * factor);
1991
1992 /* TODO: The containing block's line-height property gives a minimum
1993 * height for the line boxes. (Even when it's set to 'normal', i.e.,
1994 * AUTO? Apparently.) Once all block elements make Textblocks or
1995 * something, this can be handled.
1996 */
1999 else
2000 height =
2002 style->lineHeight);
2003 leading = height - style->font->size;
2004
2005 size->ascent += leading / 2;
2006 size->descent += leading - (leading / 2);
2007 }
2008
2009 /* In case of a sub or super script we increase the word's height and
2010 * potentially the line's height.
2011 */
2013 int requiredDescent = style->font->descent + style->font->ascent / 3;
2014 size->descent = misc::max (size->descent, requiredDescent);
2015 } else if (style->valign == core::style::VALIGN_SUPER) {
2016 int requiredAscent = style->font->ascent + style->font->ascent / 2;
2017 size->ascent = misc::max (size->ascent, requiredAscent);
2018 }
2019}
2020
2025void Textblock::addText (const char *text, size_t len,
2027{
2028 DBG_OBJ_ENTER ("construct.word", 0, "addText", "..., %d, %p",
2029 (int)len, style);
2030
2031 // Count dividing characters.
2032 int numParts = 1;
2033
2034 for (const char *s = text; s; s = nextUtf8Char (s, text + len - s)) {
2035 int foundDiv = -1;
2036 for (int j = 0; foundDiv == -1 && j < NUM_DIV_CHARS; j++) {
2037 int lDiv = strlen (divChars[j].s);
2038 if (s <= text + len - lDiv) {
2039 if (memcmp (s, divChars[j].s, lDiv * sizeof (char)) == 0)
2040 foundDiv = j;
2041 }
2042 }
2043
2044 if (foundDiv != -1) {
2045 if (divChars[foundDiv].penaltyIndexLeft != -1)
2046 numParts ++;
2047 if (divChars[foundDiv].penaltyIndexRight != -1)
2048 numParts ++;
2049 }
2050 }
2051
2052 if (numParts == 1) {
2053 // Simple (and common) case: no dividing characters. May still
2054 // be hyphenated automatically.
2055 core::Requisition size;
2056 calcTextSize (text, len, style, &size, true, true);
2057 addText0 (text, len,
2059 style, &size);
2060
2061 //printf ("[%p] %d: added simple word: ", this, words->size() - 1);
2062 //printWordWithFlags (words->getLastRef());
2063 //printf ("\n");
2064 } else {
2065 PRINTF ("HYPHENATION: '");
2066 for (size_t i = 0; i < len; i++)
2067 PUTCHAR(text[i]);
2068 PRINTF ("', with %d parts\n", numParts);
2069
2070 // Store hyphen positions.
2071 int n = 0, totalLenCharRemoved = 0;
2072 int *partPenaltyIndex = new int[numParts - 1];
2073 int *partStart = new int[numParts];
2074 int *partEnd = new int[numParts];
2075 bool *charRemoved = new bool[numParts - 1];
2076 bool *canBeHyphenated = new bool[numParts + 1];
2077 bool *permDivChar = new bool[numParts - 1];
2078 bool *unbreakableForMinWidth = new bool[numParts - 1];
2079 canBeHyphenated[0] = canBeHyphenated[numParts] = true;
2080 partStart[0] = 0;
2081 partEnd[numParts - 1] = len;
2082
2083 for (const char *s = text; s; s = nextUtf8Char (s, text + len - s)) {
2084 int foundDiv = -1;
2085 for (int j = 0; foundDiv == -1 && j < NUM_DIV_CHARS; j++) {
2086 int lDiv = strlen (divChars[j].s);
2087 if (s <= text + len - lDiv) {
2088 if (memcmp (s, divChars[j].s, lDiv * sizeof (char)) == 0)
2089 foundDiv = j;
2090 }
2091 }
2092
2093 if (foundDiv != -1) {
2094 int lDiv = strlen (divChars[foundDiv].s);
2095
2096 if (divChars[foundDiv].charRemoved) {
2097 assert (divChars[foundDiv].penaltyIndexLeft != -1);
2098 assert (divChars[foundDiv].penaltyIndexRight == -1);
2099
2100 partPenaltyIndex[n] = divChars[foundDiv].penaltyIndexLeft;
2101 charRemoved[n] = true;
2102 permDivChar[n] = false;
2103 unbreakableForMinWidth[n] =
2105 canBeHyphenated[n + 1] = divChars[foundDiv].canBeHyphenated;
2106 partEnd[n] = s - text;
2107 partStart[n + 1] = s - text + lDiv;
2108 n++;
2109 totalLenCharRemoved += lDiv;
2110 } else {
2111 assert (divChars[foundDiv].penaltyIndexLeft != -1 ||
2112 divChars[foundDiv].penaltyIndexRight != -1);
2113
2114 if (divChars[foundDiv].penaltyIndexLeft != -1) {
2115 partPenaltyIndex[n] = divChars[foundDiv].penaltyIndexLeft;
2116 charRemoved[n] = false;
2117 permDivChar[n] = false;
2118 unbreakableForMinWidth[n] =
2120 canBeHyphenated[n + 1] = divChars[foundDiv].canBeHyphenated;
2121 partEnd[n] = s - text;
2122 partStart[n + 1] = s - text;
2123 n++;
2124 }
2125
2126 if (divChars[foundDiv].penaltyIndexRight != -1) {
2127 partPenaltyIndex[n] = divChars[foundDiv].penaltyIndexRight;
2128 charRemoved[n] = false;
2129 permDivChar[n] = true;
2130 unbreakableForMinWidth[n] =
2132 canBeHyphenated[n + 1] = divChars[foundDiv].canBeHyphenated;
2133 partEnd[n] = s - text + lDiv;
2134 partStart[n + 1] = s - text + lDiv;
2135 n++;
2136 }
2137 }
2138 }
2139 }
2140
2141 // Get text without removed characters, e. g. hyphens.
2142 const char *textWithoutHyphens;
2143 char *textWithoutHyphensBuf = new char[len - totalLenCharRemoved];
2144 int *breakPosWithoutHyphens;
2145 int *breakPosWithoutHyphensBuf = new int[numParts - 1];
2146
2147 if (totalLenCharRemoved == 0) {
2148 // No removed characters: take original arrays.
2149 textWithoutHyphens = text;
2150 // Ends are also break positions, except the last end, which
2151 // is superfluous, but does not harm (since arrays in C/C++
2152 // does not have an implicit length).
2153 breakPosWithoutHyphens = partEnd;
2154 } else {
2155 // Copy into special buffers.
2156 textWithoutHyphens = textWithoutHyphensBuf;
2157 breakPosWithoutHyphens = breakPosWithoutHyphensBuf;
2158
2159 int n = 0;
2160 for (int i = 0; i < numParts; i++) {
2161 memmove (textWithoutHyphensBuf + n, text + partStart[i],
2162 partEnd[i] - partStart[i]);
2163 n += partEnd[i] - partStart[i];
2164 if (i < numParts - 1)
2165 breakPosWithoutHyphensBuf[i] = n;
2166 }
2167 }
2168
2169 PRINTF("H... without hyphens: '");
2170 for (size_t i = 0; i < len - totalLenCharRemoved; i++)
2171 PUTCHAR(textWithoutHyphens[i]);
2172 PRINTF("'\n");
2173
2174 core::Requisition *wordSize = new core::Requisition[numParts];
2175 calcTextSizes (textWithoutHyphens, len - totalLenCharRemoved, style,
2176 numParts - 1, breakPosWithoutHyphens, wordSize);
2177
2178 // Finished!
2179 for (int i = 0; i < numParts; i++) {
2180 short flags = 0;
2181
2182 // If this parts adjoins at least one division characters,
2183 // for which canBeHyphenated is set to false (this is the
2184 // case for soft hyphens), do not hyphenate.
2185 if (canBeHyphenated[i] && canBeHyphenated[i + 1])
2187
2188 if(i < numParts - 1) {
2189 if (charRemoved[i])
2191
2192 if (permDivChar[i])
2194 if (unbreakableForMinWidth[i])
2196
2198 }
2199
2200 if (i == 0)
2202 if (i == numParts - 1)
2204
2205 addText0 (text + partStart[i], partEnd[i] - partStart[i],
2206 flags, style, &wordSize[i]);
2207
2208 //printf ("[%p] %d: added word part: ", this, words->size() - 1);
2209 //printWordWithFlags (words->getLastRef());
2210 //printf ("\n");
2211
2212 //PRINTF("H... [%d] '", i);
2213 //for (int j = partStart[i]; j < partEnd[i]; j++)
2214 // PUTCHAR(text[j]);
2215 //PRINTF("' added\n");
2216
2217 if(i < numParts - 1) {
2218 Word *word = words->getLastRef();
2219
2220 setBreakOption (word, style, penalties[partPenaltyIndex[i]][0],
2221 penalties[partPenaltyIndex[i]][1], false);
2222 DBG_SET_WORD (words->size () - 1);
2223
2224 if (charRemoved[i])
2225 // Currently, only unconditional hyphens (UTF-8:
2226 // "\xe2\x80\x90") can be used. See also drawWord, last
2227 // section "if (drawHyphen)".
2228 // Could be extended by adding respective members to
2229 // DivChar and Word.
2230 word->hyphenWidth =
2232 strlen (hyphenDrawChar));
2233
2234 accumulateWordData (words->size() - 1);
2236 }
2237 }
2238
2239 delete[] partPenaltyIndex;
2240 delete[] partStart;
2241 delete[] partEnd;
2242 delete[] charRemoved;
2243 delete[] canBeHyphenated;
2244 delete[] permDivChar;
2245 delete[] unbreakableForMinWidth;
2246 delete[] textWithoutHyphensBuf;
2247 delete[] breakPosWithoutHyphensBuf;
2248 delete[] wordSize;
2249 }
2250
2251 DBG_OBJ_LEAVE ();
2252}
2253
2254void Textblock::calcTextSizes (const char *text, size_t textLen,
2256 int numBreaks, int *breakPos,
2257 core::Requisition *wordSize)
2258{
2259 // The size of the last part is calculated in a simple way.
2260 int lastStart = breakPos[numBreaks - 1];
2261 calcTextSize (text + lastStart, textLen - lastStart, style,
2262 &wordSize[numBreaks], true, true);
2263
2264 PRINTF("H... [%d] '", numBreaks);
2265 for (size_t i = 0; i < textLen - lastStart; i++)
2266 PUTCHAR(text[i + lastStart]);
2267 PRINTF("' -> %d\n", wordSize[numBreaks].width);
2268
2269 // The rest is more complicated. See dw-line-breaking, section
2270 // "Hyphens".
2271 for (int i = numBreaks - 1; i >= 0; i--) {
2272 int start = (i == 0) ? 0 : breakPos[i - 1];
2273 calcTextSize (text + start, textLen - start, style, &wordSize[i],
2274 i == 0, i == numBreaks - 1);
2275
2276 PRINTF("H... [%d] '", i);
2277 for (size_t j = 0; j < textLen - start; j++)
2278 PUTCHAR(text[j + start]);
2279 PRINTF("' -> %d\n", wordSize[i].width);
2280
2281 for (int j = i + 1; j < numBreaks + 1; j++) {
2282 wordSize[i].width -= wordSize[j].width;
2283 PRINTF("H... - %d = %d\n", wordSize[j].width, wordSize[i].width);
2284 }
2285 }
2286}
2287
2292bool Textblock::calcSizeOfWidgetInFlow (int wordIndex, Widget *widget,
2293 core::Requisition *size)
2294{
2295 DBG_OBJ_ENTER ("resize", 0, "calcSizeOfWidgetInFlow", "%d, %p, ...",
2296 wordIndex, widget);
2297
2298 bool result, firstWordOfLine;
2299
2300 if (hasListitemValue)
2301 // For list items, the word #0 at the beginning (bullet, number ...) has
2302 // to be considered;
2303 firstWordOfLine = wordIndex == 1 ||
2304 (wordIndex > 0 &&
2305 words->getRef(wordIndex - 1)->content.type == core::Content::BREAK);
2306 else
2307 firstWordOfLine = wordIndex == 0 ||
2308 words->getRef(wordIndex - 1)->content.type == core::Content::BREAK;
2309
2310 DBG_OBJ_MSGF ("resize", 1, "firstWordOfLine = %s",
2311 boolToStr (firstWordOfLine));
2312
2313 if (firstWordOfLine &&
2314 (widget->getStyle()->display == core::style::DISPLAY_BLOCK ||
2315 widget->getStyle()->display == core::style::DISPLAY_LIST_ITEM ||
2316 widget->getStyle()->display == core::style::DISPLAY_TABLE)) {
2317 // pass positions
2318 DBG_OBJ_MSG ("resize", 1, "pass position");
2319 int lastWord = lines->empty () ? -1 : lines->getLastRef()->lastWord;
2320 assert (wordIndex > lastWord);
2321
2322 // The position passed to sizeRequest must be equivalent to the position
2323 // passed later to sizeAllocate. This is more complicated for widgets
2324 // which are centered or aligned to the right: here, we have to know the
2325 // width to calculate the horizontal position. Both are calculated in an
2326 // iterative way; initially, the left border is used (like for other,
2327 // simpler alignments).
2328
2329 // Since the child widget will regard floats, we do not have to include
2330 // floats when calculating left and right border.
2331
2332 // TODO Actually line1OffsetEff should be used instead of line1Offset, but
2333 // it may not initialized here. Anyway, since ignoreLine1OffsetSometimes
2334 // is not used, line1OffsetEff is always equal to line1Offset.
2335
2336 int leftBorder = boxOffsetX () + leftInnerPadding
2337 + (lines->size () == 0 ? line1Offset /* ...Eff, actually */ : 0);
2338 int rightBorder = boxRestWidth ();
2339
2340 int lastMargin, yLine = yOffsetOfLineToBeCreated (&lastMargin);
2341 int yRel = yLine - min (lastMargin, widget->getStyle()->margin.top);
2342
2343 DBG_OBJ_MSGF ("resize", 1,
2344 "leftBorder = %d + %d + (%d == 0 ? %d : 0) = %d, "
2345 "rightBorder = %d, yRel = %d - min (%d, %d) = %d",
2346 boxOffsetX (), leftInnerPadding , lines->size (),
2347 line1OffsetEff, leftBorder, rightBorder, yLine, lastMargin,
2348 widget->getStyle()->margin.top, yRel);
2349
2350 core::SizeParams childParams;
2351 DBG_OBJ_ASSOC_CHILD (&childParams);
2352
2353 int oldXRel = leftBorder;
2354
2355 sizeRequestParams.forChild (this, widget, oldXRel, yRel, &childParams);
2356 widget->sizeRequest (size, childParams.getNumPos (),
2357 childParams.getReferences (), childParams.getX (),
2358 childParams.getY ());
2359
2361
2362 while (true) {
2364
2365 int xRel;
2366
2367 switch(widget->getStyle()->textAlign) {
2369 case core::style::TEXT_ALIGN_STRING: // see comment in alignLine()
2370 case core::style::TEXT_ALIGN_JUSTIFY: // equivalent for only one word
2371 default: // compiler happiness
2372 xRel = leftBorder;
2373 break;
2374
2376 xRel = lineBreakWidth - rightBorder - size->width;
2377 break;
2378
2380 xRel =
2381 (leftBorder + lineBreakWidth - rightBorder - size->width) / 2;
2382 break;
2383 }
2384
2385 // Cf. Textblock::calcTextOffset().
2386 if (xRel < leftBorder)
2387 xRel = leftBorder;
2388
2389 DBG_OBJ_MSGF ("resize", 2, "xRel = %d, oldXRel = %d", xRel, oldXRel);
2390
2391 // Stop when the value of xRel has not changed during the last
2392 // iteration.
2393 if (xRel == oldXRel)
2394 break;
2395
2396 sizeRequestParams.forChild (this, widget, xRel, yRel, &childParams);
2397 widget->sizeRequest (size, childParams.getNumPos (),
2398 childParams.getReferences (), childParams.getX (),
2399 childParams.getY ());
2400
2401 oldXRel = xRel;
2402
2403 DBG_OBJ_MSG_END ();
2404 }
2405
2406 DBG_OBJ_MSG_END ();
2407
2408 result = true;
2409 } else {
2410 // do not pass positions (inline elements etc)
2411 DBG_OBJ_MSG ("resize", 1, "do not pass position");
2412 widget->sizeRequest (size);
2413 result = false;
2414 }
2415
2416 DBG_OBJ_LEAVE_VAL ("%s", boolToStr (result));
2417 return result;
2418}
2419
2421 int *yRef)
2422{
2423 if (reference == this) {
2424 if (xRef)
2425 *xRef = 0;
2426 if (yRef)
2427 *yRef = 0;
2428 return true;
2429 } else
2430 return sizeRequestParams.findReference (reference, xRef, yRef);
2431}
2432
2436void Textblock::addText0 (const char *text, size_t len, short flags,
2438{
2439 DBG_OBJ_ENTER ("construct.word", 0, "addText0",
2440 "..., %d, %s:%s:%s:%s:%s:%s:%s:%s, %p, %d * (%d + %d)",
2441 (int)len,
2442 (flags & Word::CAN_BE_HYPHENATED) ? "h?" : "--",
2443 (flags & Word::DIV_CHAR_AT_EOL) ? "de" : "--",
2444 (flags & Word::PERM_DIV_CHAR) ? "dp" : "--",
2445 (flags & Word::DRAW_AS_ONE_TEXT) ? "t1" : "--",
2446 (flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) ? "um" : "--",
2447 (flags & Word::WORD_START) ? "st" : "--",
2448 (flags & Word::WORD_END) ? "en" : "--",
2449 (flags & Word::TOPLEFT_OF_LINE) ? "00" : "--",
2450 style, size->width, size->ascent, size->descent);
2451
2452 //printf("[%p] addText0 ('", this);
2453 //for (size_t i = 0; i < len; i++)
2454 // putchar(text[i]);
2455 //printf("', ");
2456 //printWordFlags (flags);
2457 //printf (", ...)\n");
2458
2459 Word *word = addWord (size->width, size->ascent, size->descent,
2460 flags, style);
2462 word->content.type = core::Content::TEXT;
2463 word->content.text = layout->textZone->strndup(text, len);
2464
2465 DBG_SET_WORD (words->size () - 1);
2466
2467 // The following debug message may be useful to identify the
2468 // different textblocks.
2469
2470 //if (words->size() == 1)
2471 // printf ("[%p] first word: '%s'\n", this, text);
2472
2473 processWord (words->size () - 1);
2474
2475 DBG_OBJ_LEAVE ();
2476}
2477
2482{
2483 DBG_OBJ_ENTER ("construct.word", 0, "addWidget", "%p, %p", widget, style);
2484
2485 /* We first assign -1 as parent_ref, since the call of widget->size_request
2486 * will otherwise let this Textblock be rewrapped from the beginning.
2487 * (parent_ref is actually undefined, but likely has the value 0.) At the,
2488 * end of this function, the correct value is assigned. */
2489 widget->parentRef = -1;
2490 DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef);
2491
2492 widget->setStyle (style);
2493
2495
2496 if (testWidgetOutOfFlow (widget)) {
2497 int oofmIndex = getOOFMIndex (widget);
2498 DBG_OBJ_MSGF ("construct.word", 1, "ouf of flow: oofmIndex = %d (%s)",
2499 oofmIndex, OOFM_NAME[oofmIndex]);
2500
2501 widget->setParent (oofContainer[oofmIndex]);
2502 widget->setGenerator (this);
2503
2504 oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex);
2505 int oofmSubRef = oofm->addWidgetOOF (widget, this, words->size ());
2506 widget->parentRef = makeParentRefOOF (oofmIndex, oofmSubRef);
2507
2508 DBG_OBJ_MSGF ("construct.word", 1, "oofmSubRef = %d => parentRef = %d",
2509 oofmSubRef, widget->parentRef);
2510
2511 core::Requisition size;
2512 oofm->calcWidgetRefSize (widget, &size);
2513 Word *word = addWord (size.width, size.ascent, size.descent, 0, style);
2515 word->content.widgetReference = new core::WidgetReference (widget);
2517
2518 // After a out-of-flow reference, breaking is allowed. (This avoids some
2519 // problems with breaking near float definitions.)
2520 setBreakOption (word, style, 0, 0, false);
2521 } else {
2522 DBG_OBJ_MSG ("construct.word", 1, "in flow");
2523
2524 widget->setParent (this);
2525
2526 // TODO Replace (perhaps) later "textblock" by "OOF aware widget".
2527 if (widget->instanceOf (Textblock::CLASS_ID)) {
2528 for (int i = 0; i < NUM_OOFM; i++)
2529 searchOutOfFlowMgr(i)->addWidgetInFlow ((Textblock*)widget, this,
2530 words->size ());
2531 }
2532
2533 core::Requisition size;
2534 short flags = calcSizeOfWidgetInFlow (words->size (), widget, &size) ?
2536 Word *word =
2537 addWord (size.width, size.ascent, size.descent, flags, style);
2539 word->content.widget = widget;
2540 }
2541
2542 DBG_SET_WORD (words->size () - 1);
2543
2544 processWord (words->size () - 1);
2545 //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref",
2546 // word->content.widget->parent_ref);
2547
2548 //DEBUG_MSG(DEBUG_REWRAP_LEVEL,
2549 // "Assigning parent_ref = %d to added word %d, "
2550 // "in page with %d word(s)\n",
2551 // lines->size () - 1, words->size() - 1, words->size());
2552
2553 DBG_OBJ_LEAVE ();
2554}
2555
2564{
2565 DBG_OBJ_ENTER ("construct.word", 0, "addAnchor", "\"%s\", %p", name, style);
2566
2567 char *copy;
2568 int y;
2569 bool result;
2570
2571 // Since an anchor does not take any space, it is safe to call
2572 // addAnchor already here.
2573 if (wasAllocated ()) {
2574 if (lines->size () == 0)
2575 y = allocation.y;
2576 else
2577 y = allocation.y + lineYOffsetWidget (lines->size () - 1);
2578 copy = Widget::addAnchor (name, y);
2579 } else
2580 copy = Widget::addAnchor (name);
2581
2582 if (copy == NULL)
2587 result = false;
2588 else {
2589 Anchor *anchor;
2590
2591 anchors->increase();
2592 anchor = anchors->getRef(anchors->size() - 1);
2593 anchor->name = copy;
2594 anchor->wordIndex = words->size();
2595 result = true;
2596 }
2597
2598 DBG_OBJ_LEAVE_VAL ("%s", boolToStr(result));
2599 return result;
2600}
2601
2602
2607{
2608 DBG_OBJ_ENTER ("construct.word", 0, "addSpace", "%p", style);
2609
2610 int wordIndex = words->size () - 1;
2611 if (wordIndex >= 0) {
2612 fillSpace (wordIndex, style);
2613 DBG_SET_WORD (wordIndex);
2614 accumulateWordData (wordIndex);
2616 }
2617
2618 DBG_OBJ_LEAVE ();
2619}
2620
2628{
2629 DBG_OBJ_ENTER ("construct.word", 0, "addBreakOption", "%p, %s",
2630 style, forceBreak ? "true" : "false");
2631
2632 int wordIndex = words->size () - 1;
2633 if (wordIndex >= 0) {
2634 setBreakOption (words->getRef(wordIndex), style, 0, 0, forceBreak);
2635 DBG_SET_WORD (wordIndex);
2636 // Call of accumulateWordData() is not needed here.
2638 }
2639
2640 DBG_OBJ_LEAVE ();
2641}
2642
2644{
2645 DBG_OBJ_ENTER ("construct.word", 0, "fillSpace", "%d, ...", wordNo);
2646
2647 DBG_OBJ_MSGF ("construct.word", 1, "style.white-space = %s",
2651 "nowrap"
2653 "pre-wrap"
2655 "pre-line" : "???");
2656
2657 // Old comment:
2658 //
2659 // According to
2660 // http://www.w3.org/TR/CSS2/text.html#white-space-model: "line
2661 // breaking opportunities are determined based on the text
2662 // prior to the white space collapsing steps".
2663 //
2664 // So we call addBreakOption () for each Textblock::addSpace ()
2665 // call. This is important e.g. to be able to break between
2666 // foo and bar in: <span style="white-space:nowrap">foo </span>
2667 // bar
2668 //
2669 // TODO: Re-evaluate again.
2670
2671 Word *word = words->getRef (wordNo);
2672
2673 // TODO: This line does not work: addBreakOption (word, style);
2674
2675 if (// Do not override a previously set break penalty:
2676 !word->content.space &&
2677 // OOF references are considered specially, and must not have a space:
2678 word->content.type != core::Content::WIDGET_OOF_REF) {
2679 setBreakOption (word, style, 0, 0, false);
2680
2681 word->content.space = true;
2682 word->origSpace = word->effSpace =
2684
2685 removeSpaceImgRenderer (wordNo);
2686
2687 word->spaceStyle->unref ();
2688 word->spaceStyle = style;
2689 style->ref ();
2690
2691 setSpaceImgRenderer (wordNo);
2692 }
2693
2694 DBG_OBJ_LEAVE ();
2695}
2696
2703 int breakPenalty1, int breakPenalty2,
2704 bool forceBreak)
2705{
2706 DBG_OBJ_ENTER ("construct.word", 0, "setBreakOption", "..., %d, %d, %s",
2707 breakPenalty1, breakPenalty2, forceBreak ? "true" : "false");
2708
2709 // TODO: lineMustBeBroken should be independent of the penalty
2710 // index? Otherwise, examine the last line.
2711 if (!word->badnessAndPenalty.lineMustBeBroken(0)) {
2712 if (forceBreak || isBreakAllowed (style))
2713 word->badnessAndPenalty.setPenalties (breakPenalty1, breakPenalty2);
2714 else
2716 }
2717
2718 DBG_OBJ_LEAVE ();
2719}
2720
2722{
2723 switch (style->whiteSpace) {
2727 return true;
2728
2731 return false;
2732
2733 default:
2734 // compiler happiness
2736 return false;
2737 }
2738}
2739
2740
2745{
2746 DBG_OBJ_ENTER ("construct.word", 0, "addParbreak", "%d, %p",
2747 space, style);
2748 DBG_OBJ_MSG ("construct.word", 0,
2749 "<i>No nesting! Stack trace may be incomplete.</i>");
2750 DBG_OBJ_LEAVE ();
2751
2752 Word *word;
2753
2754 /* A break may not be the first word of a page, or directly after
2755 the bullet/number (which is the first word) in a list item. (See
2756 also comment in sizeRequest.) */
2757 if (words->size () == 0 ||
2758 (hasListitemValue && words->size () == 1)) {
2759 /* This is a bit hackish: If a break is added as the
2760 first/second word of a page, and the parent widget is also a
2761 Textblock, and there is a break before -- this is the case when
2762 a widget is used as a text box (lists, blockquotes, list
2763 items etc) -- then we simply adjust the break before, in a
2764 way that the space is in any case visible. */
2765 /* Find the widget where to adjust the breakSpace. (Only
2766 consider normal flow, no floats etc.) */
2767 for (Widget *widget = this;
2768 widget->getParent() != NULL &&
2769 widget->getParent()->instanceOf (Textblock::CLASS_ID) &&
2770 !isWidgetOOF (widget);
2771 widget = widget->getParent ()) {
2772 Textblock *textblock2 = (Textblock*)widget->getParent ();
2773 int index = textblock2->hasListitemValue ? 1 : 0;
2774 bool isfirst = (textblock2->words->getRef(index)->content.type
2776 && textblock2->words->getRef(index)->content.widget
2777 == widget);
2778 if (!isfirst) {
2779 /* The text block we searched for has been found. */
2780 Word *word2;
2781 int lineno = getWidgetInFlowSubRef (widget);
2782
2783 if (lineno > 0 &&
2784 (word2 =
2785 textblock2->words->getRef(textblock2->lines
2786 ->getRef(lineno - 1)->firstWord)) &&
2787 word2->content.type == core::Content::BREAK) {
2788 if (word2->content.breakSpace < space) {
2789 word2->content.breakSpace = space;
2790 textblock2->queueResize (makeParentRefInFlow (lineno), false);
2791 textblock2->mustQueueResize = false;
2792 DBG_OBJ_SET_BOOL_O (textblock2, "mustQueueResize",
2793 textblock2->mustQueueResize);
2794 }
2795 }
2796 return;
2797 }
2798 /* Otherwise continue to examine parents. */
2799 }
2800
2801 /* Return in any case. */
2802 return;
2803 }
2804
2805 /* Another break before? */
2806 if ((word = words->getRef(words->size () - 1)) &&
2808 Line *lastLine = lines->getRef (lines->size () - 1);
2809
2810 word->content.breakSpace =
2811 misc::max (word->content.breakSpace, space);
2812 lastLine->breakSpace =
2814 lastLine->marginDescent - lastLine->borderDescent,
2815 lastLine->breakSpace);
2816 return;
2817 }
2818
2819 word = addWord (0, 0, 0, 0, style);
2823 word->content.breakSpace = space;
2824
2825 DBG_SET_WORD (words->size () - 1);
2826
2827 breakAdded ();
2828 processWord (words->size () - 1);
2829}
2830
2831/*
2832 * Cause a line break.
2833 */
2835{
2836 DBG_OBJ_ENTER ("construct.word", 0, "addLinebreak", "%p", style);
2837
2838 Word *word;
2839
2840 if (words->size () == 0 ||
2841 words->getRef(words->size () - 1)->content.type == core::Content::BREAK)
2842 // An <BR> in an empty line gets the height of the current font
2843 // (why would someone else place it here?), ...
2844 word =
2846 else
2847 // ... otherwise, it has no size (and does not enlarge the line).
2848 word = addWord (0, 0, 0, 0, style);
2849
2851
2854 word->content.breakSpace = 0;
2855
2856 DBG_SET_WORD (words->size () - 1);
2857
2858 breakAdded ();
2859 processWord (words->size () - 1);
2860
2861 DBG_OBJ_LEAVE ();
2862}
2863
2868{
2869 assert (words->size () >= 1);
2870 assert (words->getRef(words->size () - 1)->content.type
2872
2873 // Any space before is removed. It is not used; on the other hand,
2874 // this snippet (an example from a real-world debugging session)
2875 // would cause problems:
2876 //
2877 // <input style="width: 100%" .../>
2878 // <button ...>...</button>
2879 //
2880 // (Notice the space between <input> and <button>, and also that
2881 // the HTML parser will insert a BREAK between them.) The <input>
2882 // would be given the available width ("width: 100%"), but the
2883 // actual width (Word::totalWidth) would include the space, so that
2884 // the width of the line is larger than the available width.
2885
2886 if (words->size () >= 2)
2887 words->getRef(words->size () - 2)->origSpace =
2888 words->getRef(words->size () - 2)->effSpace = 0;
2889}
2890
2893 *context)
2894{
2895 DBG_OBJ_ENTER ("events", 0, "Textblock::getWidgetAtPointLevel", "%d, %d, %s",
2896 x, y, stackingLevelText (level));
2897
2898 Widget *widgetAtPoint = NULL;
2899
2900 switch (level) {
2901 case SL_IN_FLOW:
2902 {
2903 int lineIndex = findLineIndexWhenAllocated (y - allocation.y);
2904
2905 if (lineIndex >= 0 && lineIndex < lines->size ()) {
2906 Line *line = lines->getRef (lineIndex);
2907
2908 for (int wordIndex = line->lastWord;
2909 widgetAtPoint == NULL && wordIndex >= line->firstWord;
2910 wordIndex--) {
2911 Word *word = words->getRef (wordIndex);
2914 (word->content.widget))
2915 widgetAtPoint =
2916 word->content.widget->getWidgetAtPoint (x, y, context);
2917 }
2918 }
2919 }
2920 break;
2921
2922 case SL_OOF_REF:
2923 // TODO Inefficient. Perhaps store OOF references in separate
2924 // (much smaller!) list.
2925 for (int oofmIndex = NUM_OOFM; widgetAtPoint == NULL && oofmIndex >= 0;
2926 oofmIndex--) {
2927 for (int wordIndex = words->size () - 1;
2928 widgetAtPoint == NULL && wordIndex >= 0; wordIndex--) {
2929 Word *word = words->getRef (wordIndex);
2932 == oofmIndex &&
2934 ->widget))
2935 widgetAtPoint =
2937 ->getWidgetAtPointInterrupted (x, y, context);
2938 }
2939 }
2940 break;
2941
2942 default:
2943 widgetAtPoint =
2944 OOFAwareWidget::getWidgetAtPointLevel (x, y, level, context);
2945 break;
2946 }
2947
2948 DBG_OBJ_LEAVE_VAL ("%p", widgetAtPoint);
2949 return widgetAtPoint;
2950}
2951
2957{
2958 if (lines->size() > 0) {
2959 Widget *parent;
2960 Line *lastLine = lines->getRef (lines->size () - 1);
2961
2962 if (lastLine->breakSpace != 0 && (parent = getParent()) &&
2965 Textblock *textblock2 = (Textblock*) parent;
2966 textblock2->addParbreak(lastLine->breakSpace, style);
2967 }
2968 }
2969}
2970
2971/*
2972 * Any words added by addWord() are not immediately (queued to
2973 * be) drawn, instead, this function must be called. This saves some
2974 * calls to queueResize().
2975 *
2976 */
2978{
2979 DBG_OBJ_ENTER0 ("resize", 0, "flush");
2980
2981 if (mustQueueResize) {
2982 DBG_OBJ_MSG ("resize", 0, "mustQueueResize set");
2983
2984 queueResize (-1, true);
2985 mustQueueResize = false;
2986 DBG_OBJ_SET_BOOL ("mustQueueResize", mustQueueResize);
2987 }
2988
2989 DBG_OBJ_LEAVE ();
2990}
2991
2992
2993// next: Dw_page_find_word
2994
2995void Textblock::changeLinkColor (int link, int newColor)
2996{
2997 for (int lineIndex = 0; lineIndex < lines->size(); lineIndex++) {
2998 bool changed = false;
2999 Line *line = lines->getRef (lineIndex);
3000 int wordIdx;
3001
3002 for (wordIdx = line->firstWord; wordIdx <= line->lastWord; wordIdx++){
3003 Word *word = words->getRef(wordIdx);
3004
3005 if (word->style->x_link == link) {
3006 core::style::StyleAttrs styleAttrs;
3007
3008 switch (word->content.type) {
3010 { core::style::Style *old_style = word->style;
3011 styleAttrs = *old_style;
3013 newColor);
3014 word->style = core::style::Style::create (&styleAttrs);
3015 old_style->unref();
3016 old_style = word->spaceStyle;
3017 styleAttrs = *old_style;
3019 newColor);
3020 word->spaceStyle = core::style::Style::create(&styleAttrs);
3021 old_style->unref();
3022 break;
3023 }
3025 { core::Widget *widget = word->content.widget;
3026 styleAttrs = *widget->getStyle();
3028 newColor);
3029 styleAttrs.setBorderColor(
3031 widget->setStyle(core::style::Style::create (&styleAttrs));
3032 break;
3033 }
3034 default:
3035 break;
3036 }
3037 changed = true;
3038 }
3039 }
3040 if (changed)
3042 line->borderAscent + line->borderDescent);
3043 }
3044}
3045
3047 bool includeFirstSpace, bool includeLastSpace)
3048{
3049}
3050
3051void Textblock::queueDrawRange (int index1, int index2)
3052{
3053 DBG_OBJ_ENTER ("draw", 0, "queueDrawRange", "%d, %d", index1, index2);
3054
3055 int from = misc::min (index1, index2);
3056 int to = misc::max (index1, index2);
3057
3058 from = misc::min (from, words->size () - 1);
3059 from = misc::max (from, 0);
3060 to = misc::min (to, words->size () - 1);
3061 to = misc::max (to, 0);
3062
3063 int line1idx = findLineOfWord (from);
3064 int line2idx = findLineOfWord (to);
3065
3066 if (line1idx >= 0 && line2idx >= 0) {
3067 Line *line1 = lines->getRef (line1idx),
3068 *line2 = lines->getRef (line2idx);
3069 int y = lineYOffsetWidget (line1) + line1->borderAscent -
3070 line1->contentAscent;
3071 int h = lineYOffsetWidget (line2) + line2->borderAscent +
3072 line2->contentDescent - y;
3073
3074 queueDrawArea (0, y, allocation.width, h);
3075 }
3076
3077 DBG_OBJ_LEAVE ();
3078}
3079
3081{
3082 DBG_OBJ_ENTER ("resize", 0, "updateReference", "%d", ref);
3083
3084 // Only `queueResize` when there're words or float clearance
3085 // (float clearance may change `extraSpace.top`).
3086 if (words->size () > 0 || getStyle()->clear != core::style::CLEAR_NONE)
3087 queueResize (ref, false);
3088
3089 DBG_OBJ_LEAVE ();
3090}
3091
3092void Textblock::widgetRefSizeChanged (int externalIndex)
3093{
3094 int lineNo = findLineOfWord (externalIndex);
3095 if (lineNo >= 0 && lineNo < lines->size ())
3096 queueResize (makeParentRefInFlow (lineNo), true);
3097}
3098
3100{
3101 DBG_OBJ_ENTER ("resize", 0, "oofSizeChanged", "%s",
3102 extremesChanged ? "true" : "false");
3104
3105 // See Textblock::getAvailWidthOfChild(): Extremes changes may become also
3106 // relevant for the children, under certain conditions:
3109
3110 DBG_OBJ_LEAVE ();
3111}
3112
3113int Textblock::getGeneratorX (int oofmIndex)
3114{
3115 DBG_OBJ_ENTER ("resize", 0, "Textblock::getGeneratorX", "%d", oofmIndex);
3116
3117 int x, xRef;
3118 if (findSizeRequestReference (oofmIndex, &xRef, NULL))
3119 x = xRef;
3120 else {
3121 // Only called for floats, so this should not happen:
3123 x = 0;
3124 }
3125
3126 DBG_OBJ_LEAVE_VAL ("%d", x);
3127 return x;
3128}
3129
3130int Textblock::getGeneratorY (int oofmIndex)
3131{
3132 DBG_OBJ_ENTER ("resize", 0, "Textblock::getGeneratorY", "%d", oofmIndex);
3133
3134 int yRef, y;
3135 if (findSizeRequestReference (oofmIndex, NULL, &yRef))
3136 y = yRef;
3137 else {
3138 // Only called for floats, so this should not happen:
3140 y = 0;
3141 }
3142
3143 DBG_OBJ_LEAVE_VAL ("%d", y);
3144 return y;
3145}
3146
3148{
3149 DBG_OBJ_ENTER ("resize", 0, "Textblock::getGeneratorRest", "%d", oofmIndex);
3150
3151 int xRef, rest;
3153
3154 if (container != NULL && findSizeRequestReference (container, &xRef, NULL))
3155 rest = container->getGeneratorWidth () - (xRef + getGeneratorWidth ());
3156 else {
3157 // Only callend for floats, so this should not happen:
3159 rest = 0;
3160 }
3161
3162 DBG_OBJ_LEAVE_VAL ("%d", rest);
3163 return rest;
3164}
3165
3167{
3168 DBG_OBJ_ENTER0 ("resize", 0, "Textblock::getGeneratorWidth");
3169
3170 // Cf. sizeRequestImpl.
3171 if (usesMaxGeneratorWidth ()) {
3173 return lineBreakWidth;
3174 } else {
3175 // In some cases (especially when called from sizeRequest for an
3176 // ancestor), the value is not up to date, since content from children is
3177 // not yet added to lines. Moreover, this leads to inconsistencies between
3178 // this widget and ancestors (as in Textblock::getGeneratorRest). For this
3179 // reason, the children are examined recursively.
3180 //
3181 // Test case:
3182 //
3183 // <div style="float:left">
3184 // <div div style="float:right">float</div>
3185 // <div>abcdefghijkl mnopqrstuvwx</div>
3186 // </div>
3187
3188 int wChild = 0;
3189 int firstWordAfterLastLine =
3190 lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0;
3191 for (int i = firstWordAfterLastLine; i < words->size(); i++) {
3192 Word *word = words->getRef(i);
3193 int xRel;
3194 // We only examine instances of dw::Textblock, since they are relevant
3195 // for floats, for which this method is only called.
3198 Textblock *tbChild = (Textblock*)word->content.widget;
3199 if(tbChild->findSizeRequestReference(this, &xRel, NULL))
3200 wChild = max(wChild, xRel + tbChild->getGeneratorWidth());
3201 }
3202 }
3203
3204 DBG_OBJ_MSGF ("resize", 1, "wChild = %d", wChild);
3205
3206 int w0 = lines->size () > 0 ? lines->getLastRef()->maxLineWidth : 0;
3207 DBG_OBJ_MSGF ("resize", 1, "w0 = %d", w0);
3208 int wThis = min(w0 + leftInnerPadding + boxDiffWidth (), lineBreakWidth);
3209 DBG_OBJ_MSGF ("resize", 1, "wThis = min(%d + %d + %d, %d) = %d",
3211 wThis);
3212 int w = max(wThis, wChild);
3213 DBG_OBJ_LEAVE_VAL ("max(%d, %d) = %d", wThis, wChild, w);
3214 return w;
3215 }
3216}
3217
3219{
3220 DBG_OBJ_ENTER0 ("resize", 0, "Textblock::getMaxGeneratorWidth");
3222 return lineBreakWidth;
3223}
3224
3226{
3227 DBG_OBJ_ENTER0 ("resize", 0, "usesMaxGeneratorWidth");
3228
3229 bool result;
3230 if (treatAsInline) {
3231 DBG_OBJ_MSG ("resize", 1, "treatAsInline set");
3232 result = false;
3233 } else {
3234 bool toplevel = getParent () == NULL,
3236 vloat = testWidgetFloat (this),
3237 abspos = testWidgetAbsolutelyPositioned (this),
3238 fixpos = testWidgetFixedlyPositioned (this);
3239 DBG_OBJ_MSGF("resize", 1,
3240 "toplevel: %s, block: %s, float: %s, abspos: %s, fixpos: %s",
3241 boolToStr(toplevel), boolToStr(block), boolToStr(vloat),
3242 boolToStr(abspos), boolToStr(fixpos));
3243
3244 // In detail, this depends on what the respective OOFM does with the
3245 // child widget:
3246 result = toplevel || (block && !(vloat || abspos || fixpos));
3247 }
3248
3249 DBG_OBJ_LEAVE_VAL ("%s", boolToStr(result));
3250 return result;
3251}
3252
3254{
3255 return true;
3256}
3257
3259{
3260 return true;
3261}
3262
3267
3269{
3270 // Can also be used for a line not yet existing.
3271 int firstWord = lineNo == 0 ? 0 : lines->getRef(lineNo - 1)->lastWord + 1;
3272 int lastWord = lineNo < lines->size() ?
3273 lines->getRef(lineNo)->lastWord : words->size() - 1;
3274 return getWidgetRegardingBorderForLine (firstWord, lastWord);
3275}
3276
3278 int lastWord)
3279{
3280 DBG_OBJ_ENTER ("resize", 0, "getWidgetRegardingBorderForLine", "%d, %d",
3281 firstWord, lastWord);
3282 DBG_OBJ_MSGF ("resize", 1, "words.size = %d", words->size ());
3283
3284 RegardingBorder *widgetRegardingBorder = NULL;
3285
3286 if (firstWord < words->size ()) {
3287 // Any instance of a subclass of WidgetRegardingBorder is always
3288 // between two line breaks, and so the first word of the line.
3289 Word *word = words->getRef (firstWord);
3290
3291 DBG_MSG_WORD ("resize", 1, "<i>first word:</i> ", firstWord, "");
3292
3294 Widget *widget = word->content.widget;
3295 if (widget->instanceOf (RegardingBorder::CLASS_ID) &&
3296 // Exclude cases where a textblock constitutes a new floats
3297 // container.
3298 !isOOFContainer (widget, OOFM_FLOATS))
3299 widgetRegardingBorder = (RegardingBorder*)widget;
3300 }
3301 }
3302
3303 DBG_OBJ_LEAVE_VAL ("%p", widgetRegardingBorder);
3304 return widgetRegardingBorder;
3305}
3306
3311{
3312 // This method does not return an exact result: the position of the
3313 // new line, which does not yet exist, cannot be calculated, since
3314 // the top margin of the new line (which collapses either with the
3315 // top margin of the textblock widget, or the bottom margin of the
3316 // last line) must be taken into account. However, this method is
3317 // only called for positioning floats; here, a slight incorrectness
3318 // does not cause real harm.
3319
3320 // (Similar applies to the line *height*, which calculated in an
3321 // iterative way; see wrapWordInFlow. Using the same approach for
3322 // the *position* is possible, but not worth the increased
3323 // complexity.)
3324
3325 DBG_OBJ_ENTER0 ("line.yoffset", 0, "yOffsetOfLineToBeCreated");
3326
3327 int result;
3328
3329 if (lines->size () == 0) {
3330 result = calcVerticalBorder (getStyle()->padding.top,
3331 getStyle()->borderWidth.top + extraSpace.top,
3332 getStyle()->margin.top, 0, 0);
3333 if (lastMargin)
3334 *lastMargin = getStyle()->margin.top;
3335 } else {
3336 Line *firstLine = lines->getRef (0), *lastLine = lines->getLastRef ();
3337 result = calcVerticalBorder (getStyle()->padding.top,
3338 getStyle()->borderWidth.top,
3339 getStyle()->margin.top + extraSpace.top,
3340 firstLine->borderAscent,
3341 firstLine->marginAscent)
3342 - firstLine->borderAscent + lastLine->top + lastLine->totalHeight (0);
3343 if (lastMargin)
3344 *lastMargin = lastLine->marginDescent - lastLine->borderDescent;
3345 }
3346
3347 if (lastMargin)
3348 DBG_OBJ_LEAVE_VAL ("%d, %d", result, *lastMargin);
3349 else
3350 DBG_OBJ_LEAVE_VAL ("%d", result);
3351
3352 return result;
3353}
3354
3359{
3360 // Similar applies (regarding exactness) as to yOffsetOfLineToBeCreated.
3361
3362 DBG_OBJ_ENTER0 ("line.yoffset", 0, "yOffsetOfLineToBeCreated");
3363
3364 int result;
3365
3366 Line *firstLine = lines->getRef (0);
3367 result = calcVerticalBorder (getStyle()->padding.top,
3368 getStyle()->borderWidth.top,
3369 getStyle()->margin.top + extraSpace.top,
3370 firstLine->borderAscent,
3371 firstLine->marginAscent)
3372 - firstLine->borderAscent + line->top;
3373
3374 DBG_OBJ_LEAVE_VAL ("%d", result);
3375 return result;
3376}
3377
3378} // namespace dw
#define _MSG(...)
Definition bookmarks.c:44
Base class (rather a tag interface) for those widgets regarding borders defined by floats,...
void setPenalties(int penalty1, int penalty2)
Sets the penalty, multiplied by 100.
void getBgArea(int *x, int *y, int *width, int *height)
Return the area covered by the background image.
Definition textblock.cc:111
core::style::Style * getStyle()
Return the style this background image is part of.
Definition textblock.cc:119
static TextblockIterator * createWordIndexIterator(Textblock *textblock, core::Content::Type mask, int wordIndex)
Implementation used for words.
Definition textblock.hh:305
bool readyToDraw()
If this method returns false, nothing is done at all.
Definition textblock.cc:71
void draw(int x, int y, int width, int height)
Draw (or queue for drawing) an area, which is given in canvas coordinates.
Definition textblock.cc:104
void setData(int xWordWidget, int lineNo)
Definition textblock.cc:64
WordImgRenderer(Textblock *textblock, int wordNo)
Definition textblock.cc:49
void getBgArea(int *x, int *y, int *width, int *height)
Return the area covered by the background image.
Definition textblock.cc:81
void getRefArea(int *xRef, int *yRef, int *widthRef, int *heightRef)
Return the "reference area".
Definition textblock.cc:92
core::style::Style * getStyle()
Return the style this background image is part of.
Definition textblock.cc:99
A Widget for rendering text blocks, i.e.
Definition textblock.hh:206
void drawLine(Line *line, core::View *view, core::Rectangle *area, core::DrawingContext *context)
int findParagraphOfWord(int wordIndex)
Find the paragraph of word wordIndex.
void drawWord0(int wordIndex1, int wordIndex2, const char *text, int totalWidth, bool drawHyphen, core::style::Style *style, core::View *view, core::Rectangle *area, int xWidget, int yWidgetBase)
TODO Comment.
void calcTextSizes(const char *text, size_t textLen, core::style::Style *style, int numBreaks, int *breakPos, core::Requisition *wordSize)
void breakAdded()
Called directly after a (line or paragraph) break has been added.
void getExtremesSimpl(core::Extremes *extremes)
Simple variant, to be implemented by widgets with extremes not depending on positions.
Definition textblock.cc:468
struct dw::Textblock::@28 hlEnd[core::HIGHLIGHT_NUM_LAYERS]
void changeWordStyle(int from, int to, core::style::Style *style, bool includeFirstSpace, bool includeLastSpace)
void accumulateWordData(int wordIndex)
void addWidget(core::Widget *widget, core::style::Style *style)
Add a widget (word type) to the page.
void getWordExtremes(Word *word, core::Extremes *extremes)
Get the extremes of a word within a textblock.
Definition textblock.cc:458
void addSpace(core::style::Style *style)
?
lout::misc::NotSoSimpleVector< Word > * words
Definition textblock.hh:611
core::SizeParams sizeRequestParams
Definition textblock.hh:578
int yOffsetOfLineCreated(Line *line)
Includes margin, border, and padding.
void correctLastWordExtremes()
Called when something changed for the last word (space, hyphens etc.).
void setWordImgRenderer(int wordNo)
void enterNotifyImpl(core::EventCrossing *event)
void changeLinkColor(int link, int newColor)
void widgetRefSizeChanged(int externalIndex)
Called by an implementation of dw::oof::OutOfFlowMgr (actually only OOFPosRelMgr) for the generator o...
int numSizeReferences
Definition textblock.hh:619
Widget * getExtremesReference(int index)
See Sizes of Dillo Widgets (or Size requisitions depending on positions).
Definition textblock.cc:534
void handOverBreak(core::style::Style *style)
This function "hands" the last break of a page "over" to a parent page.
void processWord(int wordIndex)
void markSizeChange(int ref)
See Sizes of Dillo Widgets.
Definition textblock.cc:878
bool calcSizeOfWidgetInFlow(int wordIndex, Widget *widget, core::Requisition *size)
Calculate the size of a widget, and return whether it has to be positioned at the top of the line.
void oofSizeChanged(bool extremesChanged)
Called by an implementation of dw::oof::OutOfFlowMgr when the size of the container has changed,...
int numGetExtremesReferences()
See Sizes of Dillo Widgets (or Size requisitions depending on positions).
Definition textblock.cc:529
int getGeneratorX(int oofmIndex)
Return position relative to container, not regarding margin/border/padding, Called by OOFFloatsMgr to...
void fillParagraphs()
Counter part to rewrap(), but for extremes, not size calculation.
static void setStretchabilityFactor(int stretchabilityFactor)
Definition textblock.cc:202
void rewrap()
Rewrap the page from the line from which this is necessary.
void drawSpace(int wordIndex, core::View *view, core::Rectangle *area, int xWidget, int yWidgetBase)
void sizeAllocateImpl(core::Allocation *allocation)
See Sizes of Dillo Widgets.
Definition textblock.cc:570
static const char * hyphenDrawChar
The character which is used to draw a hyphen at the end of a line, either caused by automatic hyphena...
Definition textblock.hh:296
core::Iterator * iterator(core::Content::Type mask, bool atEnd)
Return an iterator for this widget.
void calcExtraSpaceImpl(int numPos, Widget **references, int *x, int *y)
Definition textblock.cc:735
int findLineIndexWhenNotAllocated(int y)
int lineYOffsetWidget(Line *line, core::Allocation *allocation)
Definition textblock.hh:712
void cleanupWord(int wordNo)
void calcTextOffset(int lineIndex, int totalWidth)
Word * findWord(int x, int y, bool *inSpace)
Find the index of the word, or -1.
static void setPenaltyHyphen(int penaltyHyphen)
Definition textblock.cc:176
void calcTextSize(const char *text, size_t len, core::style::Style *style, core::Requisition *size, bool isStart, bool isEnd)
Calculate the size of a text word.
void notifySetParent()
This method is called after a widget has been added to a parent.
Definition textblock.cc:547
RegardingBorder * getWidgetRegardingBorderForLine(Line *line)
void setBreakOption(Word *word, core::style::Style *style, int breakPenalty1, int breakPenalty2, bool forceBreak)
Set a break option, if allowed by the style.
void drawWord(Line *line, int wordIndex1, int wordIndex2, core::View *view, core::Rectangle *area, int xWidget, int yWidgetBase)
Draw a word of text.
int numSizeRequestReferences()
See Sizes of Dillo Widgets (or Size requisitions depending on positions).
Definition textblock.cc:423
static void setPenaltyEmDashLeft(int penaltyLeftEmDash)
Definition textblock.cc:186
int getAvailHeightOfChild(core::Widget *child, bool forceValue)
Definition textblock.cc:810
int nonTemporaryLines
Definition textblock.hh:610
bool limitTextWidth
Definition textblock.hh:572
lout::misc::SimpleVector< Anchor > * anchors
Definition textblock.hh:612
bool affectsSizeChangeContainerChild(Widget *child)
Definition textblock.cc:835
int yOffsetOfLineToBeCreated(int *lastMargin=NULL)
Includes margin, border, and padding.
void setSpaceImgRenderer(int wordNo)
static int stretchabilityFactor
...
Definition textblock.hh:570
static void setPenaltyHyphen2(int penaltyHyphen2)
Definition textblock.cc:181
bool addAnchor(const char *name, core::style::Style *style)
Add an anchor to the page.
static void setPenaltyEmDashRight2(int penaltyRightEmDash2)
Definition textblock.cc:197
void addText(const char *text, size_t len, core::style::Style *style)
Add a word to the page structure.
int getGeneratorY(int oofmIndex)
Return position relative to container, not regarding margin/border/padding, Called by OOFFloatsMgr to...
bool findSizeRequestReference(Widget *reference, int *xRef=NULL, int *yRef=NULL)
bool isBlockLevel()
Definition textblock.cc:939
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, bool isStart, bool isEnd)
void addBreakOption(core::style::Style *style, bool forceBreak)
Add a break option (see setBreakOption() for details).
int calcVerticalBorder(int widgetPadding, int widgetBorder, int widgetMargin, int lineBorderTotal, int lineMarginTotal)
Definition textblock.cc:433
int getMaxGeneratorWidth()
int getGeneratorRest(int oofmIndex)
static int penalties[PENALTY_NUM][2]
The penalties for hyphens and other, multiplied by 100.
Definition textblock.hh:565
void leaveNotifyImpl(core::EventCrossing *event)
void resizeDrawImpl()
Called after sizeAllocateImpl() to redraw necessary areas.
Definition textblock.cc:858
bool isBreakAllowed(core::style::Style *style)
static void setPenaltyEmDashRight(int penaltyRightEmDash)
Definition textblock.cc:192
bool usesAvailWidth()
Must be implemengted by a method returning true, when getAvailWidth() is called.
Definition textblock.cc:853
void addText0(const char *text, size_t len, short flags, core::style::Style *style, core::Requisition *size)
Add a word (without hyphens) to the page structure.
Widget * getWidgetAtPointLevel(int x, int y, int level, core::GettingWidgetAtPointContext *context)
int getGeneratorWidth()
Return width including margin/border/padding Called by OOFFloatsMgr to position floats.
struct dw::Textblock::@28 hlStart[core::HIGHLIGHT_NUM_LAYERS]
void decorateText(core::View *view, core::style::Style *style, core::style::Color::Shading shading, int x, int yBase, int width)
int findLineOfWord(int wordIndex)
Find the line of word wordIndex.
void removeChild(Widget *child)
bool mustQueueResize
Definition textblock.hh:557
int textWidth(const char *text, int start, int len, core::style::Style *style, bool isStart, bool isEnd)
void notifySetAsTopLevel()
This method is called after a widget has been set as the top of a widget tree.
Definition textblock.cc:539
void addParbreak(int space, core::style::Style *style)
Cause a paragraph break.
void fillSpace(int wordNo, core::style::Style *style)
Widget * sizeReferences[NUM_OOFM]
Definition textblock.hh:620
bool buttonPressImpl(core::EventButton *event)
Definition textblock.cc:944
static int CLASS_ID
Definition textblock.hh:867
void queueDrawRange(int index1, int index2)
Widget * sizeRequestReference(int index)
See Sizes of Dillo Widgets (or Size requisitions depending on positions).
Definition textblock.cc:428
bool isPossibleOOFContainer(int oofmIndex)
void containerSizeChangedForChildren()
Definition textblock.cc:820
void updateReference(int ref)
Update content in flow, down from ref.
bool usesMaxGeneratorWidth()
void fillWord(int wordNo, int width, int ascent, int descent, short flags, core::style::Style *style)
bool motionNotifyImpl(core::EventMotion *event)
Definition textblock.cc:959
bool sendSelectionEvent(core::SelectionState::EventType eventType, core::MousePositionEvent *event)
Send event to selection.
void addLinebreak(core::style::Style *style)
int findLineIndexWhenAllocated(int y)
void removeSpaceImgRenderer(int wordNo)
void initWord(int wordNo)
Basic initialization, which is necessary before fillWord.
bool hasListitemValue
Definition textblock.hh:531
void sizeRequestImpl(core::Requisition *requisition, int numPos, Widget **references, int *x, int *y)
The ascent of a textblock is the ascent of the first line, plus padding/border/margin.
Definition textblock.cc:309
int wrapRefParagraphs
Definition textblock.hh:583
bool buttonReleaseImpl(core::EventButton *event)
Definition textblock.cc:949
Textblock(bool limitTextWidth, bool treatAsInline=false)
Definition textblock.cc:207
Word * addWord(int width, int ascent, int descent, short flags, core::style::Style *style)
Add a new word (text, widget etc.) to a page.
int getAvailWidthOfChild(core::Widget *child, bool forceValue)
Computes the content width available of a child widget.
Definition textblock.cc:756
void removeWordImgRenderer(int wordNo)
static DivChar divChars[NUM_DIV_CHARS]
Definition textblock.hh:294
void markExtremesChange(int ref)
See Sizes of Dillo Widgets.
Definition textblock.cc:912
lout::misc::SimpleVector< Paragraph > * paragraphs
Definition textblock.hh:609
void drawLevel(core::View *view, core::Rectangle *area, int level, core::DrawingContext *context)
bool ignoreLine1OffsetSometimes
Definition textblock.hh:555
int lineYOffsetCanvas(Line *line, core::Allocation *allocation)
Definition textblock.hh:722
lout::misc::SimpleVector< Line > * lines
Definition textblock.hh:608
bool isPossibleOOFContainerParent(int oofmIndex)
int findLineIndex(int y)
Find the first line index that includes y, which is given in widget coordinates.
Set at the top when drawing.
Definition types.hh:295
Represents a button press or release event.
Definition events.hh:58
Represents a enter or leave notify event.
Definition events.hh:75
Represents a mouse motion event.
Definition events.hh:68
Set at the top when getting the widget at the point.
Definition types.hh:313
Iterators are used to iterate through the contents of a widget.
Definition iterator.hh:20
virtual void unref()
Delete the iterator.
Definition iterator.cc:82
lout::misc::ZoneAllocator * textZone
Definition layout.hh:297
int textWidth(style::Font *font, const char *text, int len)
Definition layout.hh:370
int nextGlyph(const char *text, int idx)
Definition layout.hh:385
bool emitLinkEnter(Widget *w, int link, int img, int x, int y)
Definition layout.hh:282
char * textToUpper(const char *text, int len)
Definition layout.hh:375
char * textToLower(const char *text, int len)
Definition layout.hh:380
ButtonState state
Definition events.hh:42
Base class for all mouse events related to a specific position.
Definition events.hh:49
dw::core::Shape implementation for simple rectangles.
Definition types.hh:70
Hold arguments passed to dw::core::Widget::sizeRequest and dw::core::Widget::getExtremes,...
Definition tools.hh:19
void fill(int numPos, Widget **references, int *x, int *y)
Definition tools.cc:65
void forChild(Widget *parent, Widget *child, int xRel, int yRel, SizeParams *childParams)
Definition tools.cc:88
Widget ** getReferences()
Definition tools.hh:55
bool findReference(Widget *reference, int *x, int *y)
Definition tools.cc:130
static bool handledByStackingContextMgr(Widget *widget)
An interface to encapsulate platform dependent drawing.
Definition view.hh:17
virtual void drawText(style::Font *font, style::Color *color, style::Color::Shading shading, int x, int y, const char *text, int len)=0
virtual void drawRectangle(style::Color *color, style::Color::Shading shading, bool filled, int x, int y, int width, int height)=0
The base class of all dillo widgets.
Definition widget.hh:44
virtual void setStyle(style::Style *style)
Change the style of a widget.
Definition widget.cc:1316
void setGenerator(Widget *generator)
Definition widget.hh:480
Allocation * getAllocation()
Definition widget.hh:484
void sizeAllocate(Allocation *allocation)
Wrapper for Widget::sizeAllocateImpl, calls the latter only when needed.
Definition widget.cc:1209
void setWidgetReference(WidgetReference *widgetReference)
Definition widget.hh:595
Extremes extremes
Analogue to dw::core::Widget::requisition.
Definition widget.hh:166
Layout * layout
Definition widget.hh:216
Widget * container
The containing widget, equivalent to the "containing block" defined by CSS.
Definition widget.hh:146
bool intersects(Widget *refWidget, Rectangle *area, Rectangle *intersection)
Calculates the intersection of the visible allocation (i.
Definition widget.cc:151
Allocation allocation
The current allocation: size and position, always relative to the canvas.
Definition widget.hh:204
virtual void draw(View *view, Rectangle *area, DrawingContext *context)=0
Area is given in widget coordinates.
void setButtonSensitive(bool buttonSensitive)
Definition widget.cc:1568
Widget * getParent()
Definition widget.hh:587
int boxDiffWidth()
Definition widget.hh:495
int parentRef
This value is defined by the parent widget, and used for incremential resizing.
Definition widget.hh:195
int getAvailWidth(bool forceValue)
Return available width including margin/border/padding (extraSpace?), not only the content width.
Definition widget.cc:665
void correctRequisition(Requisition *requisition, void(*splitHeightFun)(int, int *, int *), bool allowDecreaseWidth, bool allowDecreaseHeight)
Definition widget.cc:820
style::Style * getStyle()
Definition widget.hh:482
Requisition requisition
Size_request() stores the result of the last call of size_request_impl().
Definition widget.hh:160
Widget * getWidgetAtPointInterrupted(int x, int y, GettingWidgetAtPointContext *context)
Definition widget.cc:234
style::Box extraSpace
Space around the margin box.
Definition widget.hh:226
void queueDrawArea(int x, int y, int width, int height)
Definition widget.cc:297
bool selectionHandleEvent(SelectionState::EventType eventType, Iterator *it, int charPos, int linkNo, MousePositionEvent *event)
Definition widget.hh:426
void setCursor(style::Cursor cursor)
Definition widget.hh:411
bool extremesQueued()
Definition widget.hh:470
bool extremesChanged()
Definition widget.hh:474
void changeAnchor(char *name, int y)
Definition widget.hh:403
void setParent(Widget *parent)
Definition widget.cc:243
int boxRestWidth()
Definition widget.hh:493
int boxRestHeight()
Definition widget.hh:498
virtual Widget * getWidgetAtPoint(int x, int y, GettingWidgetAtPointContext *context)
Definition widget.cc:223
void drawInterruption(View *view, Rectangle *area, DrawingContext *context)
See Interrupted drawing for details.
Definition widget.cc:213
Widget * parent
The parent widget, NULL for top-level widgets.
Definition widget.hh:127
style::Style * style
Definition widget.hh:150
void containerSizeChanged()
Definition widget.cc:420
void queueResize(int ref, bool extremesChanged, bool fast)
This method should be called, when a widget changes its size.
Definition widget.cc:321
style::Color * getBgColor()
Get the actual background of a widget.
Definition widget.cc:1431
bool wasAllocated()
Definition widget.hh:475
void removeAnchor(char *name)
Definition widget.hh:406
void correctExtremes(Extremes *extremes, bool useAdjustmentWidth)
Definition widget.cc:870
void getExtremes(Extremes *extremes, int numPos=0, Widget **references=NULL, int *x=NULL, int *y=NULL)
Wrapper for Widget::getExtremesImpl().
Definition widget.cc:1093
void drawBox(View *view, style::Style *style, Rectangle *area, int x, int y, int width, int height, bool inverse)
Draw borders and background of a widget part, which allocation is given by (x, y, width,...
Definition widget.cc:1471
static Color * create(Layout *layout, int color)
Definition style.cc:528
TextTransform textTransform
Definition style.hh:546
StyleImage * backgroundImage
Definition style.hh:537
void setBorderColor(Color *val)
Definition style.hh:585
void putExternalImgRenderer(ImgRenderer *ir)
Add an additional ImgRenderer, especially used for drawing.
Definition style.hh:892
void removeExternalImgRenderer(ImgRenderer *ir)
Remove a previously added additional ImgRenderer.
Definition style.hh:898
static Style * create(StyleAttrs *attrs)
Definition style.hh:629
virtual void onMotion()
Definition style.hh:674
virtual void onLeave()
Definition style.hh:673
virtual void onEnter()
Definition style.hh:672
static bool testWidgetFixedlyPositioned(Widget *widget)
int getParentRefInFlowSubRef(int parentRef)
void sizeAllocateStart(core::Allocation *allocation)
OutOfFlowMgr * searchOutOfFlowMgr(int oofmIndex)
virtual int getGeneratorWidth()
Return width including margin/border/padding Called by OOFFloatsMgr to position floats.
bool isWidgetOOF(Widget *widget)
bool doesWidgetOOFInterruptDrawing(Widget *widget)
static bool testWidgetAbsolutelyPositioned(Widget *widget)
bool isParentRefOOF(int parentRef)
static const char * stackingLevelText(int level)
oof::OutOfFlowMgr * getWidgetOutOfFlowMgr(Widget *widget)
OOFAwareWidget * oofContainer[NUM_OOFM]
static bool isOOFContainer(Widget *widget, int oofmIndex)
void correctRequisitionByOOF(core::Requisition *requisition, void(*splitHeightFun)(int, int *, int *))
int getParentRefOOFSubRef(int parentRef)
int makeParentRefOOF(int oofmIndex, int oofmSubRef)
void correctExtremesByOOF(core::Extremes *extremes)
static const char * OOFM_NAME[NUM_OOFM]
static int getOOFMIndex(Widget *widget)
oof::OutOfFlowMgr * getParentRefOutOfFlowMgr(int parentRef)
static bool testWidgetFloat(Widget *widget)
static bool testWidgetOutOfFlow(Widget *widget)
int makeParentRefInFlow(int inFlowSubRef)
int getWidgetInFlowSubRef(Widget *widget)
Represents additional data for OOF containers.
virtual void addWidgetInFlow(OOFAwareWidget *widget, OOFAwareWidget *parent, int externalIndex)=0
virtual int addWidgetOOF(core::Widget *widget, OOFAwareWidget *generator, int externalIndex)=0
virtual void calcWidgetRefSize(core::Widget *widget, core::Requisition *size)=0
virtual void markSizeChange(int ref)=0
virtual int getAvailHeightOfChild(core::Widget *child, bool forceValue)=0
virtual int getAvailWidthOfChild(core::Widget *child, bool forceValue)=0
virtual bool dealingWithSizeOfChild(core::Widget *child)=0
virtual void markExtremesChange(int ref)=0
bool instanceOf(int otherClassId)
Returns, whether this class is an instance of the class, given by otherClassId, or of a sub class of ...
Definition identity.cc:105
void registerName(const char *className, int *classId)
This method must be called in the constructor for the sub class.
Definition identity.cc:83
const char * strndup(const char *str, size_t t)
Definition misc.hh:682
#define DBG_OBJ_ENTER0(aspect, prio, funname)
#define DBG_OBJ_SET_BOOL_O(obj, var, val)
#define DBG_OBJ_DELETE()
#define DBG_OBJ_CREATE(klass)
#define DBG_OBJ_SET_BOOL(var, val)
#define DBG_OBJ_MSG_END()
#define DBG_OBJ_MSGF(aspect, prio, fmt,...)
#define DBG_OBJ_SET_NUM(var, val)
#define DBG_OBJ_MSG(aspect, prio, msg)
#define DBG_OBJ_ENTER(aspect, prio, funname, fmt,...)
#define DBG_OBJ_LEAVE()
#define DBG_OBJ_MSG_START()
#define DBG_OBJ_ASSOC_CHILD(child)
#define DBG_OBJ_ARRSET_PTR(var, ind, val)
#define DBG_OBJ_ARRATTRSET_NUM(var, ind, attr, val)
#define DBG_OBJ_SET_NUM_O(obj, var, val)
#define DBG_OBJ_LEAVE_VAL(fmt,...)
static int dIspunct(unsigned char c)
Definition dlib.h:52
int multiplyWithPerLengthRounded(int x, Length l)
Like multiplyWithPerLength, but rounds to nearest integer instead of down.
Definition style.hh:484
@ TEXT_TRANSFORM_LOWERCASE
Definition style.hh:272
@ TEXT_TRANSFORM_UPPERCASE
Definition style.hh:271
@ TEXT_TRANSFORM_CAPITALIZE
Definition style.hh:270
@ TEXT_TRANSFORM_NONE
Definition style.hh:269
@ TEXT_DECORATION_OVERLINE
Definition style.hh:354
@ TEXT_DECORATION_LINE_THROUGH
Definition style.hh:355
@ TEXT_DECORATION_UNDERLINE
Definition style.hh:353
bool isAbsLength(Length l)
Returns true if l is an absolute length.
Definition style.hh:443
int absLengthVal(Length l)
Returns the value of a length in pixels, as an integer.
Definition style.hh:452
@ LENGTH_AUTO
Represents "auto" lengths.
Definition style.hh:495
@ WHITE_SPACE_PRE_LINE
Definition style.hh:364
@ WHITE_SPACE_PRE_WRAP
Definition style.hh:363
@ BUTTON1_MASK
Definition events.hh:20
@ HIGHLIGHT_NUM_LAYERS
Definition types.hh:46
void splitHeightPreserveAscent(int height, int *ascent, int *descent)
Definition widget.cc:2244
Dw is in this namespace, or sub namespaces of this one.
Miscellaneous stuff, which does not fit anywhere else.
Definition misc.cc:31
T min(T a, T b)
Definition misc.hh:40
T max(T a, T b)
Definition misc.hh:41
int roundInt(double d)
Definition misc.hh:82
void assertNotReached()
Definition misc.hh:56
const char * boolToStr(bool b)
Definition misc.hh:108
Stuff dealing with Unicode characters: UTF-8, character classes etc.
Definition unicode.cc:28
const char * nextUtf8Char(const char *s)
Definition unicode.cc:110
SpaceImgRenderer * spaceImgRenderer
Definition textblock.hh:499
core::style::Style * style
Definition textblock.hh:492
WordImgRenderer * wordImgRenderer
Definition textblock.hh:498
@ DIV_CHAR_AT_EOL
Must be drawn with a hyphen, when at the end of the line.
Definition textblock.hh:434
@ CAN_BE_HYPHENATED
Can be hyphenated automatically.
Definition textblock.hh:432
@ PERM_DIV_CHAR
Is or ends with a "division character", which is part of the word.
Definition textblock.hh:437
@ DRAW_AS_ONE_TEXT
This word must be drawn, together with the following word(s), by only one call of View::drawText(),...
Definition textblock.hh:442
core::Requisition size
Definition textblock.hh:463
BadnessAndPenalty badnessAndPenalty
Definition textblock.hh:489
core::style::Style * spaceStyle
Definition textblock.hh:493
core::Content content
Definition textblock.hh:475
Represents the allocation, i.e.
Definition types.hh:164
@ WIDGET_OOF_REF
reference to a widget out of flow (OOF); this widget (containing this content) is only the generator ...
Definition types.hh:217
@ WIDGET_IN_FLOW
widget in normal flow, so that this widget (containing this content) is both container (parent) and g...
Definition types.hh:207
const char * text
Definition types.hh:236
Widget * widget
Definition types.hh:237
static Content::Type maskForSelection(bool followReferences)
Definition types.cc:271
WidgetReference * widgetReference
Definition types.hh:238
static dw::core::style::Tooltip * hoverTooltip
Definition textblock.cc:38
#define PRINTF(...)
Definition textblock.hh:11
#define DBG_MSG_WORD(aspect, prio, prefix, n, suffix)
Definition textblock.hh:981
#define PUTCHAR(ch)
Definition textblock.hh:12
#define DBG_SET_WORD_SIZE(n)
Definition textblock.hh:971
#define DBG_SET_WORD(n)
Definition textblock.hh:934