1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
|
#ifndef __DW_TEXTBLOCK_HH__
#define __DW_TEXTBLOCK_HH__
#include <limits.h>
#include "oofawarewidget.hh"
#include "../lout/misc.hh"
// These were used when improved line breaking and hyphenation were implemented.
// Should be, bit by bit, replaced by RTFL (see ../lout/debug.hh).
#define PRINTF(fmt, ...)
#define PUTCHAR(ch)
#ifdef DBG_RTFL
# define DEBUG
#endif
namespace dw {
/**
* \brief A Widget for rendering text blocks, i.e. paragraphs or sequences
* of paragraphs.
*
* <div style="border: 2px solid #ffff00; margin-top: 0.5em;
* margin-bottom: 0.5em; padding: 0.5em 1em; background-color:
* #ffffe0"><b>Info:</b> The recent changes (line breaking and
* hyphenation on one hand, floats on the other hand) have not yet
* been incorporated into this documentation. See \ref
* dw-line-breaking and \ref dw-out-of-flow.</div>
*
* <h3>Signals</h3>
*
* dw::Textblock uses the signals defined in
* dw::core::Layout::LinkReceiver, related to links. The coordinates are
* always -1.
*
*
* <h3>Collapsing Spaces</h3>
*
* The idea behind this is that every paragraph has a specific vertical
* space around and that they are combined to one space, according to
* rules stated below. A paragraph consists either of the lines between
* two paragraph breaks within a dw::Textblock, or of a dw::Textblock
* within a dw::Textblock, in a single line; the latter is used for
* indented boxes and list items.
*
* The rules:
*
* <ol>
* <li> If a paragraph is following by another, the space between them is the
* maximum of both box spaces:
*
* \image html dw-textblock-collapsing-spaces-1-1.png
*
* are combined like this:
*
* \image html dw-textblock-collapsing-spaces-1-2.png
*
* <li> a) If one paragraph is the first paragraph within another, the upper
* space of these paragraphs collapse. b) The analogue is the case for the
* last box:
*
* \image html dw-textblock-collapsing-spaces-2-1.png
*
* If B and C are put into A, the result is:
*
* \image html dw-textblock-collapsing-spaces-2-2.png
* </ol>
*
* For achieving this, there are some features of dw::Textblock:
*
* <ul>
* <li> Consequent breaks are automatically combined, according to
* rule 1. See the code of dw::Textblock::addParBreak for details.
*
* <li> If a break is added as the first word of the dw::Textblock within
* another dw::Textblock, collapsing according to rule 2a is done
* automatically. See the code of dw::Textblock::addParBreak.
*
* <li> To collapse spaces according to rule 2b,
* dw::Textblock::addParBreak::handOverBreak must be called for
* the \em inner widget. The HTML parser does this in
* Html_eventually_pop_dw.
* </ul>
*
*
* <h3>Collapsing Margins</h3>
*
* Collapsing margins, as defined in the CSS2 specification, are,
* supported in addition to collapsing spaces. Also, spaces and margins
* collapse themselves. I.e., the space between two paragraphs is the
* maximum of the space calculated as described in "Collapsing Spaces"
* and the space calculated according to the rules for collapsing margins.
*
* (This is an intermediate hybrid state, collapsing spaces are used in
* the current version of dillo, while I implemented collapsing margins
* for the CSS prototype and integrated it already into the main trunk. For
* a pure CSS-based dillo, collapsing spaces will not be needed anymore, and
* may be removed for simplicity.)
*
*
* <h3>Some Internals</h3>
*
* There are 4 lists, dw::Textblock::words, dw::Textblock::paragraphs,
* dw::Textblock::lines, and dw::Textblock::anchors. The word list is
* quite static; only new words may be added. A word is either text, a
* widget, or a break.
*
* Lines refer to the word list (first and last). They are completely
* redundant, i.e., they can be rebuilt from the words. Lines can be
* rewrapped either completely or partially (see "Incremental Resizing"
* below). For the latter purpose, several values are accumulated in the
* lines. See dw::Textblock::Line for details.
*
* A recent change was the introduction of the paragraphs list, which
* works quite similar, is also redundant, but is used to calculate
* the extremes, not the size.
*
* Anchors associate the anchor name with the index of the next word at
* the point of the anchor.
*
* <h4>Incremental Resizing</h4>
*
* dw::Textblock makes use of incremental resizing as described in \ref
* dw-widget-sizes. The parentRef is, for children of a dw::Textblock, simply
* the number of the line. [<b>Update:</b> Incorrect; see \ref dw-out-of-flow.]
*
* Generally, there are three cases which may change the size of the
* widget:
*
* <ul>
* <li> The line break size of the widget has changed, e.g., because the
* user has changed the size of the browser window. In this case,
* it is necessary to rewrap all the lines.
*
* <li> A child widget has changed its size. In this case, only a rewrap
* down from the line where this widget is located is necessary.
*
* (This case is very important for tables. Tables are quite at the
* bottom, so that a partial rewrap is relevant. Otherwise, tables
* change their size quite often, so that this is necessary for a
* fast, non-blocking rendering)
*
* <li> A word (or widget, break etc.) is added to the text block. This
* makes it possible to reuse the old size by simply adjusting the
* current width and height, so no rewrapping is necessary.
* </ul>
*
* The state of the size calculation is stored in wrapRef within
* dw::Textblock, which has the value -1 if no rewrapping of lines
* necessary, or otherwise the line from which a rewrap is necessary.
*
*/
class Textblock: public oof::OOFAwareWidget
{
private:
/**
* This class encapsulates the badness/penalty calculation, and so
* (i) makes changes (hopefully) simpler, and (ii) hides the
* integer arithmetic (floating point arithmetic avoided for
* performance reasons). Unfortunately, the value range of the
* badness is not well defined, so fiddling with the penalties is a
* bit difficult.
*/
enum {
PENALTY_FORCE_BREAK = INT_MIN,
PENALTY_PROHIBIT_BREAK = INT_MAX
};
class BadnessAndPenalty
{
private:
enum { NOT_STRETCHABLE, QUITE_LOOSE, BADNESS_VALUE, TOO_TIGHT }
badnessState;
int ratio; // ratio is only defined when badness is defined
int badness, penalty[2];
// For debugging: define DEBUG for more informations in print().
#ifdef DEBUG
int totalWidth, idealWidth, totalStretchability, totalShrinkability;
#endif
// "Infinity levels" are used to represent very large numbers,
// including "quasi-infinite" numbers. A couple of infinity
// level and number can be mathematically represented as
//
// number * N ^ (infinity level)
//
// where N is a number which is large enough. Practically,
// infinity levels are used to circumvent limited ranges for
// integer numbers.
// Here, all infinity levels have got special meanings.
enum {
INF_VALUE = 0, /* simple values */
INF_LARGE, /* large values, like QUITE_LOOSE */
INF_NOT_STRETCHABLE, /* reserved for NOT_STRECTHABLE */
INF_TOO_TIGHT, /* used for lines, which are too tight */
INF_PENALTIES, /* used for penalties */
INF_MAX = INF_PENALTIES
// That INF_PENALTIES is the last value means that an
// infinite penalty (breaking is prohibited) makes a break
// not possible at all, so that pre-formatted text
// etc. works.
};
void setSinglePenalty (int index, int penalty);
int badnessValue (int infLevel);
int penaltyValue (int index, int infLevel);
public:
void calcBadness (int totalWidth, int idealWidth,
int totalStretchability, int totalShrinkability);
inline void setPenalty (int penalty) { setPenalties (penalty, penalty); }
void setPenalties (int penalty1, int penalty2);
// Rather for debugging:
inline int getPenalty (int i) { return penalty[i]; }
bool lineLoose ();
bool lineTight ();
bool lineTooTight ();
bool lineMustBeBroken (int penaltyIndex);
bool lineCanBeBroken (int penaltyIndex);
int compareTo (int penaltyIndex, BadnessAndPenalty *other);
void intoStringBuffer(lout::misc::StringBuffer *sb);
void print ();
};
enum { PENALTY_HYPHEN, PENALTY_EM_DASH_LEFT, PENALTY_EM_DASH_RIGHT,
PENALTY_NUM };
enum { NUM_DIV_CHARS = 4 };
typedef struct
{
const char *s;
bool charRemoved, canBeHyphenated, unbreakableForMinWidth;
int penaltyIndexLeft, penaltyIndexRight;
} DivChar;
static DivChar divChars[NUM_DIV_CHARS];
static const char *hyphenDrawChar;
protected:
/**
* \brief Implementation used for words.
*/
class WordImgRenderer:
public core::style::StyleImage::ExternalWidgetImgRenderer
{
protected:
Textblock *textblock;
int wordNo, xWordWidget, lineNo;
bool dataSet;
public:
WordImgRenderer (Textblock *textblock, int wordNo);
~WordImgRenderer ();
void setData (int xWordWidget, int lineNo);
bool readyToDraw ();
void getBgArea (int *x, int *y, int *width, int *height);
void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef);
core::style::Style *getStyle ();
void draw (int x, int y, int width, int height);
virtual void print ();
};
class SpaceImgRenderer: public WordImgRenderer
{
public:
inline SpaceImgRenderer (Textblock *textblock, int wordNo) :
WordImgRenderer (textblock, wordNo) { }
void getBgArea (int *x, int *y, int *width, int *height);
core::style::Style *getStyle ();
void print ();
};
struct Paragraph
{
int firstWord; /* first word's index in word vector */
int lastWord; /* last word's index in word vector */
/*
* General remark: all values include the last hyphen width, but
* not the last space; these values are, however corrected, when
* another word is added.
*
* Also, as opposed to lines, paragraphs are created with the
* first, not the last word, so these values change when new
* words are added.
*/
int parMin; /* The sum of all word minima (plus spaces,
hyphen width etc.) since the last possible
break within this paragraph. */
int parMinIntrinsic;
int parMax; /* The sum of all word maxima in this
paragraph (plus spaces, hyphen width
etc.). */
int parMaxIntrinsic;
int maxParMin; /* Maximum of all paragraph minima (value of
"parMin"), including this paragraph. */
int maxParMinIntrinsic;
int maxParMax; /* Maximum of all paragraph maxima (value of
"parMax""), including this paragraph. */
int maxParMaxIntrinsic;
};
struct Line
{
int firstWord; /* first word's index in word vector */
int lastWord; /* last word's index in word vector */
int top; /* "top" is always relative to the top
of the first line, i.e.
page->lines[0].top is always 0. */
int boxAscent; /* Maximum of all ascents of the words
in this line. This is the actual
ascent of the line. */
int boxDescent; /* Maximum of all decents of the words
in this line. This is the actual
descent of the line. */
int contentAscent; /* ??? */
int contentDescent; /* ??? */
int breakSpace; /* Space between this line and the next one. */
int textOffset; /* ??? */
/* This is similar to descent, but includes the bottom margins of the
* widgets within this line. */
int marginDescent;
/* Maximum of all line widths, including this line. Does not
* include the last space, but the last hyphen width. Please
* notice a change: until recently (before hyphenation and
* changed line breaking), the values were accumulated up to the
* last line, not this line.*/
int maxLineWidth;
/* Set to false at the beginning of addLine(), and to true at
* the end. Should be checked by some methods which are called
* by addLine(). */
bool finished;
/* The word index of the last OOF reference (most importantly:
* float) whic is positioned before this line, or -1, if there
* is no OOF reference positioned before.
*
* **Important:** These references may still be part of this or
* even a following line, when positioned before (this is the
* reason this attribute exists); see \ref dw-out-of-flow. */
int lastOofRefPositionedBeforeThisLine;
int leftOffset, rightOffset;
enum { LEFT, RIGHT, CENTER } alignment;
};
struct Word
{
enum {
/** Can be hyphenated automatically. (Cleared after
* hyphenation.) */
CAN_BE_HYPHENATED = 1 << 0,
/** Must be drawn with a hyphen, when at the end of the line. */
DIV_CHAR_AT_EOL = 1 << 1,
/** Is or ends with a "division character", which is part of
* the word. */
PERM_DIV_CHAR = 1 << 2,
/** This word must be drawn, together with the following
* word(s), by only one call of View::drawText(), to get
* kerning, ligatures etc. right. The last of the words drawn
* as one text does *not* have this flag set. */
DRAW_AS_ONE_TEXT = 1 << 3,
/* When calculating the minimal width (as part of extremes),
* do not consider this word as breakable. This flag is
* ignored when the line is actually broken. */
UNBREAKABLE_FOR_MIN_WIDTH = 1 << 4,
/* If a word represents a "real" text word, or (after
* hyphenation) the first part of a "real" text word, this
* flag is set. Plays a role for text transformation. */
WORD_START = 1 << 5,
/* If a word represents a "real" text word, or (after
* hyphenation) the last part of a "real" text word, this
* flag is set. Analogue to WORD_START. */
WORD_END = 1 << 6
};
/* TODO: perhaps add a xLeft? */
core::Requisition size;
/* Space after the word, only if it's not a break: */
short origSpace; /* from font, set by addSpace */
short effSpace; /* effective space, set by wordWrap,
* used for drawing etc. */
short hyphenWidth; /* Additional width, when a word is part
* (except the last part) of a hyphenationed
* word. Has to be added to the width, when
* this is the last word of the line, and
* "hyphenWidth > 0" is also used to decide
* whether to draw a hyphen. */
short flags;
core::Content content;
// accumulated values, relative to the beginning of the line
int totalWidth; /* The sum of all word widths; plus all
spaces, excluding the one of this
word; plus the hyphen width of this
word (but of course, no hyphen
widths of previous words. In other
words: the value compared to the
ideal width of the line, if the line
would be broken after this word. */
int maxAscent, maxDescent;
int totalSpaceStretchability; // includes all *before* current word
int totalSpaceShrinkability; // includes all *before* current word
BadnessAndPenalty badnessAndPenalty; /* when line is broken after this
* word */
core::style::Style *style;
core::style::Style *spaceStyle; /* initially the same as of the word,
later set by a_Dw_page_add_space */
// These two are used rarely, so there is perhaps a way to store
// them which is consuming less memory.
WordImgRenderer *wordImgRenderer;
SpaceImgRenderer *spaceImgRenderer;
};
void printWordShort (Word *word);
void printWordFlags (short flags);
void printWordWithFlags (Word *word);
void printWord (Word *word);
struct Anchor
{
char *name;
int wordIndex;
};
class TextblockIterator: public core::Iterator
{
private:
enum { NUM_SECTIONS = NUM_OOFM + 1 };
int sectionIndex; // 0 means in flow, otherwise OOFM index + 1
int index;
TextblockIterator (Textblock *textblock, core::Content::Type mask,
int sectionIndex, int index);
int numParts (int sectionIndex);
void getPart (int sectionIndex, int index, core::Content *content);
public:
TextblockIterator (Textblock *textblock, core::Content::Type mask,
bool atEnd);
inline static TextblockIterator *createWordIndexIterator
(Textblock *textblock, core::Content::Type mask, int wordIndex)
{ return new TextblockIterator (textblock, mask, 0, wordIndex); }
lout::object::Object *clone();
int compareTo(lout::object::Comparable *other);
bool next ();
bool prev ();
void highlight (int start, int end, core::HighlightLayer layer);
void unhighlight (int direction, core::HighlightLayer layer);
void getAllocation (int start, int end, core::Allocation *allocation);
void print ();
};
friend class TextblockIterator;
// See sizeAllocateImpl for details. It is also used elsewhere.
core::Allocation childBaseAllocation;
/* These fields provide some ad-hoc-functionality, used by sub-classes. */
bool hasListitemValue; /* If true, the first word of the page is treated
specially (search in source). */
int leftInnerPadding; /* This is an additional padding on the left side
(used by ListItem). */
int line1Offset; /* This is an additional offset of the first line.
May be negative (shift to left) or positive
(shift to right). */
int line1OffsetEff; /* The "effective" value of line1_offset, may
differ from line1_offset when
ignoreLine1OffsetSometimes is set to true. */
/* The following is really hackish: It is used for DwTableCell (see
* comment in dw_table_cell.c), to avoid too wide table columns. If
* set to true, it has following effects:
*
* (i) line1_offset is ignored in calculating the minimal width
* (which is used by DwTable!), and
* (ii) line1_offset is ignored (line1_offset_eff is set to 0),
* when line1_offset plus the width of the first word is
* greater than the the line break witdh.
*
* \todo Eliminate all these ad-hoc features by a new, simpler and
* more elegant design. ;-)
*/
bool ignoreLine1OffsetSometimes;
bool mustQueueResize;
/**
* The penalties for hyphens and other, multiplied by 100. So, 100
* means 1.0. INT_MAX and INT_MIN are also allowed. See
* dw::Textblock::BadnessAndPenalty::setPenalty for more
* details. Set from preferences.
*/
static int penalties[PENALTY_NUM][2];
/**
* ...
*/
static int stretchabilityFactor;
bool limitTextWidth; /* from preferences */
int redrawY;
int lastWordDrawn;
/* This value is (currently) set by setAscent(). */
int lineBreakWidth;
// Additional vertical offset, used for the "clear" attribute.
int verticalOffset;
int wrapRefLines, wrapRefParagraphs; /* 0-based. Important: Both
are the line numbers, not
the value stored in
parentRef. */
// These four values are calculated by containingBlock->outOfFlowMgr
// (when defined; otherwise, they are false, or 0, respectively), for
// the newly constructed line, only when needed: when a new line is
// added, or if something in the line currently constucted has
// changed, e. g. a float has been added.
bool newLineHasFloatLeft, newLineHasFloatRight;
int newLineLeftBorder, newLineRightBorder; /* As returned by
outOfFlowMgr->get...Border,
or 0, if outOfFlowMgr
is NULL */
int newLineLeftFloatHeight, newLineRightFloatHeight;
// Ascent and descent of the newly constructed line, i. e. maximum
// of all words ascent/descent since the end of the last line. Not
// neccessary the ascent and descent of the newly added line, since
// not all words are added to it.
int newLineAscent, newLineDescent;
lout::misc::SimpleVector <Line> *lines;
lout::misc::SimpleVector <Paragraph> *paragraphs;
int nonTemporaryLines;
lout::misc::NotSoSimpleVector <Word> *words;
lout::misc::SimpleVector <Anchor> *anchors;
struct {int index, nChar;}
hlStart[core::HIGHLIGHT_NUM_LAYERS], hlEnd[core::HIGHLIGHT_NUM_LAYERS];
int hoverLink; /* The link under the mouse pointer */
void queueDrawRange (int index1, int index2);
void getWordExtremes (Word *word, core::Extremes *extremes);
void justifyLine (Line *line, int diff);
Line *addLine (int firstWord, int lastWord, int newLastOofPos,
bool temporary, int minHeight);
void calcWidgetSize (core::Widget *widget, core::Requisition *size);
void rewrap ();
void fillParagraphs ();
void initNewLine ();
void calcBorders (int lastOofRef, int height);
void showMissingLines ();
void removeTemporaryLines ();
void setVerticalOffset (int verticalOffset);
void decorateText (core::View *view, core::style::Style *style,
core::style::Color::Shading shading,
int x, int yBase, int width);
void drawText (core::View *view, core::style::Style *style,
core::style::Color::Shading shading, int x, int y,
const char *text, int start, int len, bool isStart,
bool isEnd);
void drawWord (Line *line, int wordIndex1, int wordIndex2, core::View *view,
core::Rectangle *area, int xWidget, int yWidgetBase);
void drawWord0 (int wordIndex1, int wordIndex2,
const char *text, int totalWidth, bool drawHyphen,
core::style::Style *style, core::View *view,
core::Rectangle *area, int xWidget, int yWidgetBase);
void drawSpace (int wordIndex, core::View *view, core::Rectangle *area,
int xWidget, int yWidgetBase);
void drawLine (Line *line, core::View *view, core::Rectangle *area);
int findLineIndex (int y);
int findLineIndexWhenNotAllocated (int y);
int findLineIndexWhenAllocated (int y);
int findLineIndex (int y, int ascent);
int findLineOfWord (int wordIndex);
int findParagraphOfWord (int wordIndex);
Word *findWord (int x, int y, bool *inSpace);
Word *addWord (int width, int ascent, int descent, short flags,
core::style::Style *style);
void breakAdded ();
void initWord (int wordNo);
void cleanupWord (int wordNo);
void removeWordImgRenderer (int wordNo);
void setWordImgRenderer (int wordNo);
void removeSpaceImgRenderer (int wordNo);
void setSpaceImgRenderer (int wordNo);
void fillWord (int wordNo, int width, int ascent, int descent,
short flags, core::style::Style *style);
void fillSpace (int wordNo, core::style::Style *style);
void setBreakOption (Word *word, core::style::Style *style,
int breakPenalty1, int breakPenalty2, bool forceBreak);
bool isBreakAllowedInWord (Word *word)
{ return isBreakAllowed (word->style); }
bool isBreakAllowed (core::style::Style *style);
int textWidth (const char *text, int start, int len,
core::style::Style *style, bool isStart, bool isEnd);
void calcTextSize (const char *text, size_t len, core::style::Style *style,
core::Requisition *size, bool isStart, bool isEnd);
/**
* Of nested text blocks, only the most inner one must regard the
* borders of floats.
*/
inline bool mustBorderBeRegarded (Line *line)
{
return getTextblockForLine (line) == NULL;
}
inline bool mustBorderBeRegarded (int lineNo)
{
return getTextblockForLine (lineNo) == NULL;
}
inline int _lineYOffsetWidgetAllocation (Line *line,
core::Allocation *allocation)
{
return line->top + (allocation->ascent - lines->getRef(0)->boxAscent);
}
inline int lineYOffsetWidget (Line *line)
{
return _lineYOffsetWidgetAllocation (line, &childBaseAllocation);
}
/**
* Like lineYOffsetCanvas, but with the allocation as parameter. Rarely used
* outside of lineYOffsetCanvas.
*/
inline int _lineYOffsetCanvasAllocation (Line *line,
core::Allocation *allocation)
{
return allocation->y + _lineYOffsetWidgetAllocation (line, allocation);
}
/**
* Returns the y offset (within the canvas) of a line.
*/
inline int lineYOffsetCanvas (Line *line)
{
return _lineYOffsetCanvasAllocation(line, &childBaseAllocation);
}
inline int lineYOffsetWidgetI (int lineIndex)
{
return lineYOffsetWidget (lines->getRef (lineIndex));
}
inline int lineYOffsetWidgetIAllocation (int lineIndex,
core::Allocation *allocation)
{
return _lineYOffsetWidgetAllocation (lines->getRef (lineIndex),
allocation);
}
inline int lineYOffsetCanvasI (int lineIndex)
{
return lineYOffsetCanvas (lines->getRef (lineIndex));
}
inline int calcPenaltyIndexForNewLine ()
{
if (lines->size() == 0)
return 0;
else {
Line *line = lines->getLastRef();
if (line->firstWord <= line->lastWord)
return
(words->getRef(line->lastWord)->flags &
(Word::DIV_CHAR_AT_EOL | Word::PERM_DIV_CHAR)) ? 1 : 0;
else
// empty line
return 0;
}
}
Textblock *getTextblockForLine (Line *line);
Textblock *getTextblockForLine (int lineNo);
Textblock *getTextblockForLine (int firstWord, int lastWord);
void printBorderChangedErrorAndAbort (int y, Widget *vloat,
int wrapLineIndex);
int yOffsetOfPossiblyMissingLine (int lineNo);
int heightOfPossiblyMissingLine (int lineNo);
bool sendSelectionEvent (core::SelectionState::EventType eventType,
core::MousePositionEvent *event);
void processWord (int wordIndex);
virtual int wordWrap (int wordIndex, bool wrapAll);
int wrapWordInFlow (int wordIndex, bool wrapAll);
void balanceBreakPosAndHeight (int wordIndex, int firstIndex,
int *searchUntil, bool tempNewLine,
int penaltyIndex, bool borderIsCalculated,
bool *thereWillBeMoreSpace, bool wrapAll,
int *diffWords, int *wordIndexEnd,
int *lastFloatPos, bool regardBorder,
int *height, int *breakPos);
int searchBreakPos (int wordIndex, int firstIndex, int *searchUntil,
bool tempNewLine, int penaltyIndex,
bool thereWillBeMoreSpace, bool wrapAll,
int *diffWords, int *wordIndexEnd,
int *addIndex1 = NULL);
int searchMinBap (int firstWord, int lastWordm, int penaltyIndex,
bool thereWillBeMoreSpace, bool correctAtEnd);
int considerHyphenation (int firstIndex, int breakPos);
bool isHyphenationCandidate (Word *word);
int calcLinePartHeight (int firstWord, int lastWord);
void handleWordExtremes (int wordIndex);
void correctLastWordExtremes ();
static int getSpaceShrinkability(struct Word *word);
static int getSpaceStretchability(struct Word *word);
int getLineShrinkability(int lastWordIndex);
int getLineStretchability(int lastWordIndex);
int hyphenateWord (int wordIndex, int *addIndex1 = NULL);
void moveWordIndices (int wordIndex, int num, int *addIndex1 = NULL);
void accumulateWordForLine (int lineIndex, int wordIndex);
void accumulateWordData (int wordIndex);
int calcLineBreakWidth (int lineIndex);
void initLine1Offset (int wordIndex);
void alignLine (int lineIndex);
void calcTextOffset (int lineIndex, int totalWidth);
void sizeRequestImpl (core::Requisition *requisition);
void getExtremesImpl (core::Extremes *extremes);
void sizeAllocateImpl (core::Allocation *allocation);
int getAvailWidthOfChild (core::Widget *child, bool forceValue);
int getAvailHeightOfChild (core::Widget *child, bool forceValue);
void containerSizeChangedForChildren ();
bool affectsSizeChangeContainerChild (Widget *child);
bool usesAvailWidth ();
void resizeDrawImpl ();
void markSizeChange (int ref);
void markExtremesChange (int ref);
bool isBlockLevel ();
void draw (core::View *view, core::Rectangle *area);
bool buttonPressImpl (core::EventButton *event);
bool buttonReleaseImpl (core::EventButton *event);
bool motionNotifyImpl (core::EventMotion *event);
void enterNotifyImpl (core::EventCrossing *event);
void leaveNotifyImpl (core::EventCrossing *event);
void removeChild (Widget *child);
void addText0 (const char *text, size_t len, short flags,
core::style::Style *style, core::Requisition *size);
void calcTextSizes (const char *text, size_t textLen,
core::style::Style *style,
int numBreaks, int *breakPos,
core::Requisition *wordSize);
inline bool mustBeWidenedToAvailWidth () {
DBG_OBJ_ENTER0 ("resize", 0, "mustBeWidenedToAvailWidth");
bool toplevel = getParent () == NULL,
block = getStyle()->display == core::style::DISPLAY_BLOCK,
vloat = testWidgetFloat (this),
abspos = testWidgetAbsolutelyPositioned (this),
fixpos = testWidgetFixedlyPositioned (this),
// In detail, this depends on what the respective OOFM does
// with the child widget:
result = toplevel || (block && !(vloat || abspos || fixpos));
DBG_OBJ_MSGF ("resize", 0,
"=> %s (toplevel: %s, block: %s, float: %s, abspos: %s, "
"fixpos: %s)",
result ? "true" : "false", toplevel ? "true" : "false",
block ? "true" : "false", vloat ? "true" : "false",
abspos ? "true" : "false", fixpos ? "true" : "false");
DBG_OBJ_LEAVE ();
return result;
}
public:
static int CLASS_ID;
static void setPenaltyHyphen (int penaltyHyphen);
static void setPenaltyHyphen2 (int penaltyHyphen2);
static void setPenaltyEmDashLeft (int penaltyLeftEmDash);
static void setPenaltyEmDashRight (int penaltyRightEmDash);
static void setPenaltyEmDashRight2 (int penaltyRightEmDash2);
static void setStretchabilityFactor (int stretchabilityFactor);
Textblock (bool limitTextWidth);
~Textblock ();
core::Iterator *iterator (core::Content::Type mask, bool atEnd);
void flush ();
void addText (const char *text, size_t len, core::style::Style *style);
inline void addText (const char *text, core::style::Style *style)
{
addText (text, strlen(text), style);
}
void addWidget (core::Widget *widget, core::style::Style *style);
bool addAnchor (const char *name, core::style::Style *style);
void addSpace (core::style::Style *style);
void addBreakOption (core::style::Style *style, bool forceBreak);
void addParbreak (int space, core::style::Style *style);
void addLinebreak (core::style::Style *style);
core::Widget *getWidgetAtPoint (int x, int y, int level);
void handOverBreak (core::style::Style *style);
void changeLinkColor (int link, int newColor);
void changeWordStyle (int from, int to, core::style::Style *style,
bool includeFirstSpace, bool includeLastSpace);
void borderChanged (int y, core::Widget *vloat);
void oofSizeChanged (bool extremesChanged);
int getLineBreakWidth ();
bool isPossibleContainer (int oofmIndex);
bool isPossibleContainerParent (int oofmIndex);
};
#define DBG_SET_WORD_PENALTY(n, i, is) \
D_STMT_START { \
if (words->getRef(n)->badnessAndPenalty.getPenalty (i) == INT_MIN) \
DBG_OBJ_ARRATTRSET_SYM ("words", n, "penalty." is, "-inf"); \
else if (words->getRef(n)->badnessAndPenalty.getPenalty (i) == INT_MAX) \
DBG_OBJ_ARRATTRSET_SYM ("words", n, "penalty." is, "inf"); \
else \
DBG_OBJ_ARRATTRSET_NUM ("words", n, "penalty." is, \
words->getRef(n)->badnessAndPenalty \
.getPenalty (i)); \
} D_STMT_END
#define DBG_SET_WORD(n) \
D_STMT_START { \
switch (words->getRef(n)->content.type) { \
case ::dw::core::Content::TEXT: \
DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "TEXT"); \
DBG_OBJ_ARRATTRSET_STR ("words", n, "text/widget/breakSpace", \
words->getRef(n)->content.text); \
break; \
case ::dw::core::Content::WIDGET_IN_FLOW: \
DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "WIDGET_IN_FLOW"); \
DBG_OBJ_ARRATTRSET_PTR ("words", n, "text/widget/breakSpace", \
words->getRef(n)->content.widget); \
break; \
case ::dw::core::Content::WIDGET_OOF_REF: \
DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "WIDGET_OOF_REF"); \
DBG_OBJ_ARRATTRSET_PTR ("words", n, "text/widget/breakSpace", \
words->getRef(n)->content.widget); \
break; \
case ::dw::core::Content::BREAK: \
DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "BREAK"); \
DBG_OBJ_ARRATTRSET_NUM ("words", n, "text/widget/breakSpace", \
words->getRef(n)->content.breakSpace); \
break; \
default: \
DBG_OBJ_ARRATTRSET_SYM ("words", n, "type", "???"); \
DBG_OBJ_ARRATTRSET_SYM ("words", n, "text/widget/breakSpace", "???"); \
} \
DBG_SET_WORD_PENALTY (n, 0, "0"); \
DBG_SET_WORD_PENALTY (n, 1, "1"); \
} D_STMT_END
#define DBG_MSG_WORD(aspect, prio, prefix, n, suffix) \
D_STMT_START { \
if ((n) < 0 || (n) >= words->size ()) \
DBG_OBJ_MSG (aspect, prio, prefix "undefined (wrong index)" suffix); \
else { \
switch (words->getRef(n)->content.type) { \
case ::dw::core::Content::TEXT: \
DBG_OBJ_MSGF (aspect, prio, prefix "TEXT / \"%s\"" suffix, \
words->getRef(n)->content.text); \
break; \
case ::dw::core::Content::WIDGET_IN_FLOW: \
DBG_OBJ_MSGF (aspect, prio, prefix "WIDGET_IN_FLOW / %p" suffix, \
words->getRef(n)->content.widget); \
break; \
case ::dw::core::Content::WIDGET_OOF_REF: \
DBG_OBJ_MSGF (aspect, prio, prefix "WIDGET_OOF_REF / %p" suffix, \
words->getRef(n)->content.widget); \
break; \
case ::dw::core::Content::BREAK: \
DBG_OBJ_MSGF (aspect, prio, prefix "BREAK / %d" suffix, \
words->getRef(n)->content.breakSpace); \
break; \
default: \
DBG_OBJ_MSG (aspect, prio, prefix "??? / ???" suffix); \
} \
} \
} D_STMT_END
} // namespace dw
#endif // __DW_TEXTBLOCK_HH__
|