Logo ROOT   6.08/07
Reference Guide
TGHtmlLayout.cxx
Go to the documentation of this file.
1 // $Id: TGHtmlLayout.cxx,v 1.1 2007/05/04 17:07:01 brun Exp $
2 // Author: Valeriy Onuchin 03/05/2007
3 
4 /**************************************************************************
5 
6  HTML widget for xclass. Based on tkhtml 1.28
7  Copyright (C) 1997-2000 D. Richard Hipp <drh@acm.org>
8  Copyright (C) 2002-2003 Hector Peraza.
9 
10  This library is free software; you can redistribute it and/or
11  modify it under the terms of the GNU Library General Public
12  License as published by the Free Software Foundation; either
13  version 2 of the License, or (at your option) any later version.
14 
15  This library is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  Library General Public License for more details.
19 
20  You should have received a copy of the GNU Library General Public
21  License along with this library; if not, write to the Free
22  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 
24 **************************************************************************/
25 
26 // This file contains the code used to position elements of the
27 // HTML file on the screen.
28 
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include "TGHtml.h"
33 
34 
35 ////////////////////////////////////////////////////////////////////////////////
36 /// Html Layout Context constructor.
37 
39 {
40  fPStart = 0;
41  fPEnd = 0;
42  fLeftMargin = 0;
43  fRightMargin = 0;
44  fHtml = 0;
45  fLeft = 0;
46  fRight = 0;
47  fMaxX = 0;
48  fMaxY = 0;
49  fPageWidth = 0;
50  Reset();
51 }
52 
53 ////////////////////////////////////////////////////////////////////////////////
54 /// Reset the layout context.
55 
57 {
58  fHeadRoom = 0;
59  fTop = 0;
60  fBottom = 0;
63 }
64 
65 ////////////////////////////////////////////////////////////////////////////////
66 /// Push a new margin onto the given margin stack.
67 ///
68 /// If the "bottom" parameter is non-negative, then this margin will
69 /// automatically expire for all text that is placed below the y-coordinate
70 /// given by "bottom". This feature is used for <IMG ALIGN=left> and <IMG
71 /// ALIGN=right> kinds of markup. It allows text to flow around an image.
72 ///
73 /// If "bottom" is negative, then the margin stays in force until it is
74 /// explicitly canceled by a call to PopMargin().
75 ///
76 /// ppMargin - The margin stack onto which to push
77 /// indent - The indentation for the new margin
78 /// mbottom - The margin expires at this Y coordinate
79 /// tag - Markup that will cancel this margin
80 
82  int indent, int mbottom, int tag)
83 {
84  SHtmlMargin_t *pNew = new SHtmlMargin_t;
85  pNew->fPNext = *ppMargin;
86  if (pNew->fPNext) {
87  pNew->fIndent = indent + pNew->fPNext->fIndent;
88  } else {
89  pNew->fIndent = indent;
90  }
91  pNew->fBottom = mbottom;
92  pNew->fTag = tag;
93  *ppMargin = pNew;
94 }
95 
96 ////////////////////////////////////////////////////////////////////////////////
97 /// Pop one margin off of the given margin stack.
98 
100 {
101  if (*ppMargin) {
102  SHtmlMargin_t *pOld = *ppMargin;
103  *ppMargin = pOld->fPNext;
104  delete pOld;
105  }
106 }
107 
108 ////////////////////////////////////////////////////////////////////////////////
109 /// Pop as many margins as necessary until the margin that was
110 /// created with "tag" is popped off. Update the layout context
111 /// to move past obstacles, if necessary.
112 ///
113 /// If there are some margins on the stack that contain non-negative
114 /// bottom fields, that means there are some obstacles that we have
115 /// not yet cleared. If these margins get popped off the stack,
116 /// then we have to be careful to advance the 'bottom' value so
117 /// that the next line of text will clear the obstacle.
118 
120 {
121  int bot = -1;
122  int oldTag;
123  SHtmlMargin_t *pM;
124 
125  for (pM = *ppMargin; pM && pM->fTag != tag; pM = pM->fPNext) {}
126  if (pM == 0) {
127  // No matching margin is found. Do nothing.
128  return;
129  }
130  while ((pM = *ppMargin) != 0) {
131  if (pM->fBottom > bot) bot = pM->fBottom;
132  oldTag = pM->fTag;
133  PopOneMargin(ppMargin);
134  if (oldTag == tag) break;
135  }
136  if (fBottom < bot) {
137  fHeadRoom += bot - fBottom;
138  fBottom = bot;
139  }
140 }
141 
142 ////////////////////////////////////////////////////////////////////////////////
143 /// Pop all expired margins from the stack.
144 ///
145 /// An expired margin is one with a non-negative bottom parameter
146 /// that is less than the value "y". "y" is the Y-coordinate of
147 /// the top edge the next line of text to by positioned. What this
148 /// function does is check to see if we have cleared any obstacles
149 /// (an obstacle is an <IMG ALIGN=left> or <IMG ALIGN=right>) and
150 /// expands the margins if we have.
151 
153 {
154  while (*ppMarginStack && (**ppMarginStack).fBottom >= 0 &&
155  (**ppMarginStack).fBottom <= y) {
156  PopOneMargin(ppMarginStack);
157  }
158 }
159 
160 ////////////////////////////////////////////////////////////////////////////////
161 /// Clear a margin stack to reclaim memory. This routine just blindly
162 /// pops everything off the stack. Typically used when the screen is
163 /// cleared or the widget is deleted, etc.
164 
166 {
167  while (*ppMargin) PopOneMargin(ppMargin);
168 }
169 
170 ////////////////////////////////////////////////////////////////////////////////
171 /// This routine gathers as many tokens as will fit on one line.
172 ///
173 /// The candidate tokens begin with fPStart and go thru the end of
174 /// the list or to fPEnd, whichever comes first. The first token
175 /// at the start of the next line is returned. NULL is returned if
176 /// we exhaust data.
177 ///
178 /// "width" is the maximum allowed width of the line. The actual
179 /// width is returned in *actualWidth. The actual width does not
180 /// include any trailing spaces. Sometimes the actual width will
181 /// be greater than the maximum width. This will happen, for example,
182 /// for text enclosed in <pre>..</pre> that has lines longer than
183 /// the width of the page.
184 ///
185 /// If the list begins with text, at least one token is returned,
186 /// even if that one token is longer than the allowed line length.
187 /// But if the list begins with some kind of break markup (possibly
188 /// preceded by white space) then the returned list may be empty.
189 ///
190 /// The "x" coordinates of all elements are set assuming that the line
191 /// begins at 0. The calling routine should adjust these coordinates
192 /// to position the line horizontally. (The FixLine() procedure does
193 /// this.) Note that the "x" coordinate of <li> elements will be negative.
194 /// Text within <dt>..</dt> might also have a negative "x" coordinate.
195 /// But in no case will the x coordinate every be less than "minX".
196 ///
197 /// p_start - First token on new line
198 /// p_end - End of line. Might be NULL
199 /// width - How much space is on this line
200 /// minX - The minimum value of the X coordinate
201 /// actualWidth - Return space actually required
202 
204  TGHtmlElement *p_end, int width, int minX, int *actualWidth)
205 {
206  int x; // Current X coordinate
207  int spaceWanted = 0; // Add this much space before next token
208  TGHtmlElement *p; // For looping over tokens
209  TGHtmlElement *lastBreak = 0; // Last line-break opportunity
210  int isEmpty = 1; // True if link contains nothing
211  int origin; // Initial value of "x"
212 
213  *actualWidth = 0;
214  p = p_start;
215  while (p && p != p_end && (p->fStyle.fFlags & STY_Invisible) != 0) {
216  p = p->fPNext;
217  }
218  if (p && p->fStyle.fFlags & STY_DT) {
219  origin = -HTML_INDENT;
220  } else {
221  origin = 0;
222  }
223  x = origin;
224  if (x < minX) x = minX;
225  if (p && p != p_end && p->fType == Html_LI) {
226  TGHtmlLi *li = (TGHtmlLi *) p;
227  li->fX = x - HTML_INDENT / 3;
228  if (li->fX - (HTML_INDENT * 2) / 3 < minX) {
229  x += minX - li->fX + (HTML_INDENT * 2) / 3;
230  li->fX = minX + (HTML_INDENT * 2) / 3;
231  }
232  isEmpty = 0;
233  *actualWidth = 1;
234  p = p->fPNext;
235  while (p && (p->fType == Html_Space || p->fType == Html_P)) {
236  p = p->fPNext;
237  }
238  }
239  // coverity[dead_error_line]
240  for (; p && p != p_end; p = p ? p->fPNext : 0) {
241  if (p->fStyle.fFlags & STY_Invisible) continue;
242  switch (p->fType) {
243  case Html_Text: {
245  text->fX = x + spaceWanted;
246  if ((p->fStyle.fFlags & STY_Preformatted) == 0) {
247  if (lastBreak && x + spaceWanted + text->fW > width)
248  return lastBreak;
249  }
250 // TRACE(HtmlTrace_GetLine2, ("Place token %s at x=%d w=%d\n",
251 // HtmlTokenName(p), text->fX, text->fW));
252  x += text->fW + spaceWanted;
253  isEmpty = 0;
254  spaceWanted = 0;
255  break;
256  }
257 
258  case Html_Space: {
259  TGHtmlSpaceElement *space = (TGHtmlSpaceElement *) p;
260  if (p->fStyle.fFlags & STY_Preformatted) {
261  if (p->fFlags & HTML_NewLine) {
262  *actualWidth = (x <= 0) ? 1 : x;
263  return p->fPNext;
264  }
265  x += space->fW * p->fCount;
266  } else {
267  int w;
268  if ((p->fStyle.fFlags & STY_NoBreak) == 0) {
269  lastBreak = p->fPNext;
270  *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
271  }
272  w = space->fW;
273  if (spaceWanted < w && x > origin) spaceWanted = w;
274  }
275  break;
276  }
277 
278  case Html_IMG: {
279  TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) p;
280  switch (image->fAlign) {
281  case IMAGE_ALIGN_Left:
282  case IMAGE_ALIGN_Right:
283  *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
284  return p;
285  default:
286  break;
287  }
288  image->fX = x + spaceWanted;
289  if ((p->fStyle.fFlags & STY_Preformatted) == 0) {
290  if (lastBreak && x + spaceWanted + image->fW > width) {
291  return lastBreak;
292  }
293  }
294 // TRACE(HtmlTrace_GetLine2, ("Place in-line image %s at x=%d w=%d\n",
295 // HtmlTokenName(p), p->image.x, p->image.w));
296  x += image->fW + spaceWanted;
297  if ((p->fStyle.fFlags & STY_NoBreak) == 0) {
298  lastBreak = p->fPNext;
299  *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
300  }
301  spaceWanted = 0;
302  isEmpty = 0;
303  break;
304  }
305 
306  case Html_APPLET:
307  case Html_EMBED:
308  case Html_INPUT:
309  case Html_SELECT:
310  case Html_TEXTAREA: {
311  TGHtmlInput *input = (TGHtmlInput *) p;
312  input->fX = x + spaceWanted + input->fPadLeft;
313  if ((p->fStyle.fFlags & STY_Preformatted) == 0) {
314  if (lastBreak && x + spaceWanted + input->fW > width) {
315  return lastBreak;
316  }
317  }
318 // TRACE(HtmlTrace_GetLine2, ("Place token %s at x=%d w=%d\n",
319 // HtmlTokenName(p), p->input.x, p->input.w));
320  x = input->fX + input->fW;
321  if ((p->fStyle.fFlags & STY_NoBreak) == 0) {
322  lastBreak = p->fPNext;
323  *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
324  }
325  spaceWanted = 0;
326  isEmpty = 0;
327  break;
328  }
329 
330  case Html_EndTEXTAREA: {
331  TGHtmlRef *ref = (TGHtmlRef *) p;
332  if (ref->fPOther) {
333  // fHtml->ResetTextarea(ref->fPOther);
334  }
335  break;
336  }
337 
338  case Html_DD: {
339  TGHtmlRef *ref = (TGHtmlRef *) p;
340  if (ref->fPOther == 0) break;
341  if (((TGHtmlListStart *)ref->fPOther)->fCompact == 0 ||
342  x + spaceWanted >= 0) {
343  *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
344  return p;
345  }
346  x = 0;
347  spaceWanted = 0;
348  break;
349  }
350 
351  case Html_WBR:
352  *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
353  if (x + spaceWanted >= width) {
354  return p->fPNext;
355  } else {
356  lastBreak = p->fPNext;
357  }
358  break;
359 
360  case Html_ADDRESS:
361  case Html_EndADDRESS:
362  case Html_BLOCKQUOTE:
363  case Html_EndBLOCKQUOTE:
364  case Html_BODY:
365  case Html_EndBODY:
366  case Html_BR:
367  case Html_CAPTION:
368  case Html_EndCAPTION:
369  case Html_CENTER:
370  case Html_EndCENTER:
371  case Html_EndDD:
372  case Html_DIV:
373  case Html_EndDIV:
374  case Html_DL:
375  case Html_EndDL:
376  case Html_DT:
377  case Html_H1:
378  case Html_EndH1:
379  case Html_H2:
380  case Html_EndH2:
381  case Html_H3:
382  case Html_EndH3:
383  case Html_H4:
384  case Html_EndH4:
385  case Html_H5:
386  case Html_EndH5:
387  case Html_H6:
388  case Html_EndH6:
389  case Html_EndHTML:
390  case Html_HR:
391  case Html_LI:
392  case Html_LISTING:
393  case Html_EndLISTING:
394  case Html_MENU:
395  case Html_EndMENU:
396  case Html_OL:
397  case Html_EndOL:
398  case Html_P:
399  case Html_EndP:
400  case Html_PRE:
401  case Html_EndPRE:
402  case Html_TABLE:
403  case Html_EndTABLE:
404  case Html_TD:
405  case Html_EndTD:
406  case Html_TH:
407  case Html_EndTH:
408  case Html_TR:
409  case Html_EndTR:
410  case Html_UL:
411  case Html_EndUL:
412  case Html_EndFORM:
413  *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
414  return p;
415 
416  default:
417  break;
418  }
419  }
420  *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
421 
422  return p;
423 }
424 
425 ////////////////////////////////////////////////////////////////////////////////
426 /// Set the y coordinate for every anchor in the given list
427 
429 {
430  while (p && p != p_end) {
431  if (p->fType == Html_A) ((TGHtmlAnchor *)p)->fY = y;
432  p = p->fPNext;
433  }
434 }
435 
436 ////////////////////////////////////////////////////////////////////////////////
437 /// This routine computes the X and Y coordinates for all elements of
438 /// a line that has been gathered using GetLine() above. It also figures
439 /// the ascent and descent for in-line images.
440 ///
441 /// The value returned is the Y coordinate of the bottom edge of the
442 /// new line. The X coordinates are computed by adding the left margin
443 /// plus any extra space needed for centering or right-justification.
444 ///
445 /// p_start - Start of tokens for this line
446 /// p_end - First token past end of this line. Maybe NULL
447 /// mbottom - Put the top of this line here
448 /// width - This is the space available to the line
449 /// actualWidth - This is the actual width needed by the line
450 /// lMargin - The current left margin
451 /// max_x - Write maximum X coordinate of ink here
452 
454  TGHtmlElement *p_end, int mbottom, int width,
455  int actualWidth, int lMargin, int *max_x)
456 {
457  int dx; // Amount by which to increase all X coordinates
458  int maxAscent; // Maximum height above baseline
459  int maxTextAscent; // Maximum height above baseline for text
460  int maxDescent; // Maximum depth below baseline
461  int ascent, descent; // Computed ascent and descent for one element
462  TGHtmlElement *p; // For looping
463  int y; // Y coordinate of the baseline
464  int dy2center; // Distance from baseline to text font center
465  int max = 0;
466 
467  if (actualWidth > 0) {
468  for (p = p_start; p && p != p_end && p->fType != Html_Text; p = p->fPNext) {}
469  if (p == p_end || p == 0) p = p_start;
470  maxAscent = maxTextAscent = 0;
471  for (p = p_start; p && p != p_end; p = p->fPNext) {
472  int ss;
473  if (p->fStyle.fAlign == ALIGN_Center) {
474  dx = lMargin + (width - actualWidth) / 2;
475  } else if (p->fStyle.fAlign == ALIGN_Right) {
476  dx = lMargin + (width - actualWidth);
477  } else {
478  dx = lMargin;
479  }
480  if (dx < 0) dx = 0;
481  if (p->fStyle.fFlags & STY_Invisible) continue;
482  switch (p->fType) {
483  case Html_Text: {
485  text->fX += dx;
486  max = text->fX + text->fW;
487  ss = p->fStyle.fSubscript;
488  if (ss > 0) {
489  int ascent2 = text->fAscent;
490  int delta = (ascent2 + text->fDescent) * ss / 2;
491  ascent2 += delta;
492  text->fY = -delta;
493  if (ascent2 > maxAscent) maxAscent = ascent2;
494  if (ascent2 > maxTextAscent) maxTextAscent = ascent2;
495  } else if (ss < 0) {
496  int descent2 = text->fDescent;
497  int delta = (descent2 + text->fAscent) * (-ss) / 2;
498  descent2 += delta;
499  text->fY = delta;
500  } else {
501  text->fY = 0;
502  if (text->fAscent > maxAscent) maxAscent = text->fAscent;
503  if (text->fAscent > maxTextAscent) maxTextAscent = text->fAscent;
504  }
505  break;
506  }
507 
508  case Html_Space: {
509  TGHtmlSpaceElement *space = (TGHtmlSpaceElement *) p;
510  if (space->fAscent > maxAscent) maxAscent = space->fAscent;
511  break;
512  }
513 
514  case Html_LI: {
515  TGHtmlLi *li = (TGHtmlLi *) p;
516  li->fX += dx;
517  if (li->fX > max) max = li->fX;
518  break;
519  }
520 
521  case Html_IMG: {
522  TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) p;
523  image->fX += dx;
524  max = image->fX + image->fW;
525  switch (image->fAlign) {
526  case IMAGE_ALIGN_Middle:
527  image->fDescent = image->fH / 2;
528  image->fAscent = image->fH - image->fDescent;
529  if (image->fAscent > maxAscent) maxAscent = image->fAscent;
530  break;
531 
533  dy2center = (image->fTextDescent - image->fTextAscent) / 2;
534  image->fDescent = image->fH / 2 + dy2center;
535  image->fAscent = image->fH - image->fDescent;
536  if (image->fAscent > maxAscent) maxAscent = image->fAscent;
537  break;
538 
539  case IMAGE_ALIGN_Bottom:
540  image->fDescent = 0;
541  image->fAscent = image->fH;
542  if (image->fAscent > maxAscent) maxAscent = image->fAscent;
543  break;
544 
546  image->fDescent = image->fTextDescent;
547  image->fAscent = image->fH - image->fDescent;
548  if (image->fAscent > maxAscent) maxAscent = image->fAscent;
549  break;
550 
551  default:
552  break;
553  }
554  break;
555  }
556 
557  case Html_TABLE:
558  break;
559 
560  case Html_TEXTAREA:
561  case Html_INPUT:
562  case Html_SELECT:
563  case Html_EMBED:
564  case Html_APPLET: {
565  TGHtmlInput *input = (TGHtmlInput *) p;
566  input->fX += dx;
567  max = input->fX + input->fW;
568  dy2center = (input->fTextDescent - input->fTextAscent) / 2;
569  input->fY = dy2center - input->fH / 2;
570  ascent = -input->fY;
571  if (ascent > maxAscent) maxAscent = ascent;
572  break;
573  }
574 
575  default:
576  // Shouldn't happen
577  break;
578  }
579  }
580 
581  *max_x = max;
582  y = maxAscent + mbottom;
583  maxDescent = 0;
584 
585  for (p = p_start; p && p != p_end; p = p->fPNext) {
586  if (p->fStyle.fFlags & STY_Invisible) continue;
587  switch (p->fType) {
588  case Html_Text: {
590  text->fY += y;
591  if (text->fDescent > maxDescent) maxDescent = text->fDescent;
592  break;
593  }
594 
595  case Html_LI: {
596  TGHtmlLi *li = (TGHtmlLi *) p;
597  li->fY = y;
598  if (li->fDescent > maxDescent) maxDescent = li->fDescent;
599  break;
600  }
601 
602  case Html_IMG: {
603  TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) p;
604  image->fY = y;
605  switch (image->fAlign) {
606  case IMAGE_ALIGN_Top:
607  image->fAscent = maxAscent;
608  image->fDescent = image->fH - maxAscent;
609  break;
610 
611  case IMAGE_ALIGN_TextTop:
612  image->fAscent = maxTextAscent;
613  image->fDescent = image->fH - maxTextAscent;
614  break;
615 
616  default:
617  break;
618  }
619  if (image->fDescent > maxDescent) maxDescent = image->fDescent;
620  break;
621  }
622 
623  case Html_TABLE:
624  break;
625 
626  case Html_INPUT:
627  case Html_SELECT:
628  case Html_TEXTAREA:
629  case Html_APPLET:
630  case Html_EMBED: {
631  TGHtmlInput *input = (TGHtmlInput *) p;
632  descent = input->fY + input->fH;
633  input->fY += y;
634  if (descent > maxDescent) maxDescent = descent;
635  break;
636  }
637 
638  default:
639  /* Shouldn't happen */
640  break;
641  }
642  }
643 
644 // TRACE(HtmlTrace_FixLine,
645 // ("Setting baseline to %d. mbottom=%d ascent=%d descent=%d dx=%d\n",
646 // y, mbottom, maxAscent, maxDescent, dx));
647 
648  } else {
649  maxDescent = 0;
650  y = mbottom;
651  }
652 
653  return y + maxDescent;
654 }
655 
656 ////////////////////////////////////////////////////////////////////////////////
657 /// Increase the headroom to create a paragraph break at the current token
658 
660 {
661  int headroom;
662 
663  if (p == 0) return;
664 
665  if (p->fType == Html_Text) {
667  headroom = text->fAscent + text->fDescent;
668  } else if (p->fPNext && p->fPNext->fType == Html_Text) {
670  headroom = text->fAscent + text->fDescent;
671  } else {
672  //// headroom = 10;
673  FontMetrics_t fontMetrics;
674  TGFont *font;
675  font = fHtml->GetFont(p->fStyle.fFont);
676  if (font == 0) return;
677  font->GetFontMetrics(&fontMetrics);
678  headroom = fontMetrics.fDescent + fontMetrics.fAscent;
679  }
680  if (fHeadRoom < headroom && fBottom > fTop) fHeadRoom = headroom;
681 }
682 
683 ////////////////////////////////////////////////////////////////////////////////
684 /// Compute the current margins for layout. Three values are returned:
685 ///
686 /// *pY The top edge of the area in which we can put ink. This
687 /// takes into account any requested headroom.
688 ///
689 /// *pX The left edge of the inkable area. The takes into account
690 /// any margin requests active at vertical position specified
691 /// in pLC->bottom.
692 ///
693 /// *pW The width of the inkable area. This takes into account
694 /// an margin requests that are active at the vertical position
695 /// pLC->bottom.
696 ///
697 
698 void TGHtmlLayoutContext::ComputeMargins(int *pX, int *pY, int *pW)
699 {
700  int x, y, w;
701 
702  y = fBottom + fHeadRoom;
705  w = fPageWidth - fRight;
706  if (fLeftMargin) {
707  x = fLeftMargin->fIndent + fLeft;
708  } else {
709  x = fLeft;
710  }
711  w -= x;
712  if (fRightMargin) w -= fRightMargin->fIndent;
713 
714  *pX = x;
715  *pY = y;
716  *pW = w;
717 }
718 
719 #define CLEAR_Left 0
720 #define CLEAR_Right 1
721 #define CLEAR_Both 2
722 #define CLEAR_First 3
723 ////////////////////////////////////////////////////////////////////////////////
724 /// Clear a wrap-around obstacle. The second option determines the
725 /// precise behavior.
726 ///
727 /// CLEAR_Left Clear all obstacles on the left.
728 ///
729 /// CLEAR_Right Clear all obstacles on the right.
730 ///
731 /// CLEAR_Both Clear all obstacles on both sides.
732 ///
733 /// CLEAR_First Clear only the first obstacle on either side.
734 
736 {
737  int newBottom = fBottom;
738 
741 
742  switch (mode) {
743  case CLEAR_Both:
746  break;
747 
748  case CLEAR_Left:
749  while (fLeftMargin && fLeftMargin->fBottom >= 0) {
750  if (newBottom < fLeftMargin->fBottom) {
751  newBottom = fLeftMargin->fBottom;
752  }
754  }
755  if (newBottom > fBottom + fHeadRoom) {
756  fHeadRoom = 0;
757  } else {
758  fHeadRoom = newBottom - fBottom;
759  }
760  fBottom = newBottom;
762  break;
763 
764  case CLEAR_Right:
765  while (fRightMargin && fRightMargin->fBottom >= 0) {
766  if (newBottom < fRightMargin->fBottom) {
767  newBottom = fRightMargin->fBottom;
768  }
770  }
771  if (newBottom > fBottom + fHeadRoom) {
772  fHeadRoom = 0;
773  } else {
774  fHeadRoom = newBottom - fBottom;
775  }
776  fBottom = newBottom;
778  break;
779 
780  case CLEAR_First:
781  if (fLeftMargin && fLeftMargin->fBottom >= 0) {
782  if (fRightMargin &&
784  if (newBottom < fRightMargin->fBottom) {
785  newBottom = fRightMargin->fBottom;
786  }
788  } else {
789  if (newBottom < fLeftMargin->fBottom) {
790  newBottom = fLeftMargin->fBottom;
791  }
793  }
794  } else if (fRightMargin && fRightMargin->fBottom >= 0) {
795  newBottom = fRightMargin->fBottom;
797  }
798  if (newBottom > fBottom + fHeadRoom) {
799  fHeadRoom = 0;
800  } else {
801  fHeadRoom = newBottom - fBottom;
802  }
803  fBottom = newBottom;
804  break;
805 
806  default:
807  break;
808  }
809 }
810 
811 ////////////////////////////////////////////////////////////////////////////////
812 /// Return the next markup type [TGHtmlElement::NextMarkupType]
813 
815 {
816  while ((p = p->fPNext)) {
817  if (p->IsMarkup()) return p->fType;
818  }
819  return Html_Unknown;
820 }
821 
822 ////////////////////////////////////////////////////////////////////////////////
823 /// Break markup is any kind of markup that might force a line-break. This
824 /// routine handles a single element of break markup and returns a pointer
825 /// to the first element past that markup. If p doesn't point to break
826 /// markup, then p is returned. If p is an incomplete table (a <TABLE>
827 /// that lacks a </TABLE>), then NULL is returned.
828 
830 {
831  TGHtmlElement *fPNext = p->fPNext;
832  const char *z;
833  int x, y, w;
834 
835  switch (p->fType) {
836  case Html_A:
837  ((TGHtmlAnchor *)p)->fY = fBottom;
838  break;
839 
840  case Html_BLOCKQUOTE:
843  Paragraph(p);
844  break;
845 
846  case Html_EndBLOCKQUOTE:
849  Paragraph(p);
850  break;
851 
852  case Html_IMG: {
853  TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) p;
854  switch (image->fAlign) {
855  case IMAGE_ALIGN_Left:
856  ComputeMargins(&x, &y, &w);
857  image->fX = x;
858  image->fY = y;
859  image->fAscent = 0;
860  image->fDescent = image->fH;
861  PushMargin(&fLeftMargin, image->fW + 2, y + image->fH, 0);
862  if (fMaxY < y + image->fH) fMaxY = y + image->fH;
863  if (fMaxX < x + image->fW) fMaxX = x + image->fW;
864  break;
865 
866  case IMAGE_ALIGN_Right:
867  ComputeMargins(&x, &y, &w);
868  image->fX = x + w - image->fW;
869  image->fY = y;
870  image->fAscent = 0;
871  image->fDescent = image->fH;
872  PushMargin(&fRightMargin, image->fW + 2, y + image->fH, 0);
873  if (fMaxY < y + image->fH) fMaxY = y + image->fH;
874  if (fMaxX < x + image->fW) fMaxX = x + image->fW;
875  break;
876 
877  default:
878  fPNext = p;
879  break;
880  }
881  break;
882  }
883 
884  case Html_PRE:
885  // Skip space tokens thru the next newline.
886  while (fPNext->fType == Html_Space) {
887  TGHtmlElement *pThis = fPNext;
888  fPNext = fPNext->fPNext;
889  if (pThis->fFlags & HTML_NewLine) break;
890  }
891  Paragraph(p);
892  break;
893 
894  case Html_UL:
895  case Html_MENU:
896  case Html_DIR:
897  case Html_OL:
898  if (((TGHtmlListStart *)p)->fCompact == 0) Paragraph(p);
899  PushMargin(&fLeftMargin, HTML_INDENT, -1, p->fType + 1);
900  break;
901 
902  case Html_EndOL:
903  case Html_EndUL:
904  case Html_EndMENU:
905  case Html_EndDIR: {
906  TGHtmlRef *ref = (TGHtmlRef *) p;
907  if (ref->fPOther) {
909  if (!((TGHtmlListStart *)ref->fPOther)->fCompact) Paragraph(p);
910  }
911  break;
912  }
913 
914  case Html_DL:
915  Paragraph(p);
917  break;
918 
919  case Html_EndDL:
921  Paragraph(p);
922  break;
923 
924  case Html_HR: {
925  int zl, wd;
926  TGHtmlHr *hr = (TGHtmlHr *) p;
927  hr->fIs3D = (p->MarkupArg("noshade", 0) == 0);
928  z = p->MarkupArg("size", 0);
929  if (z) {
930  int hrsz = atoi(z);
931  hr->fH = (hrsz < 0) ? 2 : hrsz;
932  } else {
933  hr->fH = 0;
934  }
935  if (hr->fH < 1) {
936  int relief = fHtml->GetRuleRelief();
937  if (hr->fIs3D &&
938  (relief == HTML_RELIEF_SUNKEN || relief == HTML_RELIEF_RAISED)) {
939  hr->fH = 3;
940  } else {
941  hr->fH = 2;
942  }
943  }
944  ComputeMargins(&x, &y, &w);
945  hr->fY = y + fHtml->GetRulePadding();
946  y += hr->fH + fHtml->GetRulePadding() * 2 + 1;
947  hr->fX = x;
948  z = p->MarkupArg("width", "100%");
949  zl = z ? strlen(z) : 0;
950  if (zl > 0 && z[zl-1] == '%') {
951  wd = (atoi(z) * w) / 100;
952  } else {
953  wd = z ? atoi(z) : w;
954  }
955  if (wd > w) wd = w;
956  hr->fW = wd;
957  switch (p->fStyle.fAlign) {
958  case ALIGN_Center:
959  case ALIGN_None:
960  hr->fX += (w - wd) / 2;
961  break;
962 
963  case ALIGN_Right:
964  hr->fX += (w - wd);
965  break;
966 
967  default:
968  break;
969  }
970  if (fMaxY < y) fMaxY = y;
971  if (fMaxX < wd + hr->fX) fMaxX = wd + hr->fX;
972  fBottom = y;
973  fHeadRoom = 0;
974  break;
975  }
976 
977  case Html_ADDRESS:
978  case Html_EndADDRESS:
979  case Html_CENTER:
980  case Html_EndCENTER:
981  case Html_DIV:
982  case Html_EndDIV:
983  case Html_H1:
984  case Html_EndH1:
985  case Html_H2:
986  case Html_EndH2:
987  case Html_H3:
988  case Html_EndH3:
989  case Html_H4:
990  case Html_EndH4:
991  case Html_H5:
992  case Html_EndH5:
993  case Html_H6:
994  case Html_EndH6:
995  case Html_P:
996  case Html_EndP:
997  case Html_EndPRE:
998  case Html_EndFORM:
999  Paragraph(p);
1000  break;
1001 
1002  case Html_TABLE:
1003  fPNext = TableLayout((TGHtmlTable *) p);
1004  break;
1005 
1006  case Html_BR:
1007  z = p->MarkupArg("clear",0);
1008  if (z) {
1009  if (strcasecmp(z, "left") == 0) {
1011  } else if (strcasecmp(z, "right") == 0) {
1013  } else {
1015  }
1016  }
1017  if (p->fPNext && p->fPNext->fPNext && p->fPNext->fType == Html_Space &&
1018  p->fPNext->fPNext->fType == Html_BR) {
1019  Paragraph(p);
1020  }
1021  break;
1022 
1023  // All of the following tags need to be handed to the GetLine() routine
1024  case Html_Text:
1025  case Html_Space:
1026  case Html_LI:
1027  case Html_INPUT:
1028  case Html_SELECT:
1029  case Html_TEXTAREA:
1030  case Html_APPLET:
1031  case Html_EMBED:
1032  fPNext = p;
1033  break;
1034 
1035  default:
1036  break;
1037  }
1038 
1039  return fPNext;
1040 }
1041 
1042 ////////////////////////////////////////////////////////////////////////////////
1043 /// Return TRUE (non-zero) if we are currently wrapping text around
1044 /// one or more images.
1045 
1047 {
1048  if (fLeftMargin && fLeftMargin->fBottom >= 0) return 1;
1049  if (fRightMargin && fRightMargin->fBottom >= 0) return 1;
1050  return 0;
1051 }
1052 
1053 ////////////////////////////////////////////////////////////////////////////////
1054 /// Move past obstacles until a linewidth of reqWidth is obtained,
1055 /// or until all obstacles are cleared.
1056 ///
1057 /// reqWidth - Requested line width
1058 /// pX, pY, pW - The margins. See ComputeMargins()
1059 
1060 void TGHtmlLayoutContext::WidenLine(int reqWidth, int *pX, int *pY, int *pW)
1061 {
1062  ComputeMargins(pX, pY, pW);
1063  if (*pW < reqWidth && InWrapAround()) {
1065  ComputeMargins(pX, pY, pW);
1066  }
1067 }
1068 
1069 
1070 #ifdef TABLE_TRIM_BLANK
1071 int HtmlLineWasBlank = 0;
1072 #endif // TABLE_TRIM_BLANK
1073 
1074 ////////////////////////////////////////////////////////////////////////////////
1075 /// Do as much layout as possible on the block of text defined by
1076 /// the HtmlLayoutContext.
1077 
1079 {
1080  TGHtmlElement *p, *pNext;
1081 
1082  for (p = fPStart; p && p != fPEnd; p = pNext) {
1083  int lineWidth;
1084  int actualWidth;
1085  int y = 0;
1086  int lMargin;
1087  int max_x = 0;
1088 
1089  // Do as much break markup as we can.
1090  while (p && p != fPEnd) {
1091  pNext = DoBreakMarkup(p);
1092  if (pNext == p) break;
1093  if (pNext) {
1094 // TRACE(HtmlTrace_BreakMarkup,
1095 // ("Processed token %s as break markup\n", HtmlTokenName(p)));
1096  fPStart = p;
1097  }
1098  p = pNext;
1099  }
1100 
1101  if (p == 0 || p == fPEnd) break;
1102 
1103 #ifdef TABLE_TRIM_BLANK
1104  HtmlLineWasBlank = 0;
1105 #endif // TABLE_TRIM_BLANK
1106 
1107  // We might try several times to layout a single line...
1108  while (1) {
1109 
1110  // Compute margins
1111  ComputeMargins(&lMargin, &y, &lineWidth);
1112 
1113  // Layout a single line of text
1114  pNext = GetLine(p, fPEnd, lineWidth, fLeft-lMargin, &actualWidth);
1115 // TRACE(HtmlTrace_GetLine,
1116 // ("GetLine page=%d left=%d right=%d available=%d used=%d\n",
1117 // fPageWidth, fLeft, fRight, lineWidth, actualWidth));
1118  FixAnchors(p, pNext, fBottom);
1119 
1120  // Move down and repeat the layout if we exceeded the available
1121  // line length and it is possible to increase the line length by
1122  // moving past some obstacle.
1123 
1124  if (actualWidth > lineWidth && InWrapAround()) {
1126  continue;
1127  }
1128 
1129  // Lock the line into place and exit the loop
1130  y = FixLine(p, pNext, y, lineWidth, actualWidth, lMargin, &max_x);
1131  break;
1132  }
1133 
1134 #ifdef TABLE_TRIM_BLANK
1135 
1136  // I noticed that a newline following break markup would result
1137  // in a blank line being drawn. So if an "empty" line was found
1138  // I subtract any whitespace caused by break markup.
1139 
1140  if (actualWidth <= 0) HtmlLineWasBlank = 1;
1141 
1142 #endif // TABLE_TRIM_BLANK
1143 
1144  // If a line was completed, advance to the next line
1145  if (pNext && actualWidth > 0 && y > fBottom) {
1146  PopIndent();
1147  fBottom = y;
1148  fPStart = pNext;
1149  }
1150  if (y > fMaxY) fMaxY = y;
1151  if (max_x > fMaxX) fMaxX = max_x;
1152  }
1153 }
1154 
1155 ////////////////////////////////////////////////////////////////////////////////
1156 /// Adjust (push) ident.
1157 
1159 {
1161  if (fHtml->GetMarginWidth()) {
1164  }
1165 }
1166 
1167 ////////////////////////////////////////////////////////////////////////////////
1168 /// Adjust (pop) ident.
1169 
1171 {
1172  if (fHeadRoom <= 0) return;
1173  fHeadRoom = 0;
1175 }
1176 
1177 ////////////////////////////////////////////////////////////////////////////////
1178 /// Advance the layout as far as possible
1179 
1181 {
1182  int btm;
1183 
1184  if (fPFirst == 0) return;
1185  Sizer();
1186  fLayoutContext.fHtml = this;
1187 #if 0 // orig
1188  fLayoutContext.PushIndent();
1189  fLayoutContext.fPageWidth = fCanvas->GetWidth();
1190  fLayoutContext.fLeft = 0;
1191 #else
1192  fLayoutContext.fHeadRoom = HTML_INDENT/4;
1193  fLayoutContext.fPageWidth = fCanvas->GetWidth() - HTML_INDENT/4;
1194  fLayoutContext.fLeft = HTML_INDENT/4;
1195 #endif
1196  fLayoutContext.fRight = 0;
1197  fLayoutContext.fPStart = fNextPlaced;
1198  if (fLayoutContext.fPStart == 0) fLayoutContext.fPStart = fPFirst;
1199  if (fLayoutContext.fPStart) {
1200  TGHtmlElement *p;
1201 
1202  fLayoutContext.fMaxX = fMaxX;
1203  fLayoutContext.fMaxY = fMaxY;
1204  btm = fLayoutContext.fBottom;
1205  fLayoutContext.LayoutBlock();
1206  fMaxX = fLayoutContext.fMaxX;
1207 #if 0
1208  fMaxY = fLayoutContext.fMaxY;
1209 #else
1210  fMaxY = fLayoutContext.fMaxY + fYMargin;
1211 #endif
1212  fNextPlaced = fLayoutContext.fPStart;
1213  fFlags |= HSCROLL | VSCROLL;
1214  if (fZGoto && (p = AttrElem("name", fZGoto+1))) {
1215  fVisible.fY = ((TGHtmlAnchor *)p)->fY;
1216  delete[] fZGoto;
1217  fZGoto = 0;
1218  }
1219  RedrawText(btm);
1220  }
1221 }
TGHtmlElement * GetLine(TGHtmlElement *pStart, TGHtmlElement *pEnd, int width, int minX, int *actualWidth)
This routine gathers as many tokens as will fit on one line.
Html_16_t fX
Definition: TGHtml.h:431
TGHtml * fHtml
Definition: TGHtml.h:798
#define IMAGE_ALIGN_Left
Definition: TGHtml.h:570
#define CLEAR_Both
Html_u8_t fAscent
Definition: TGHtml.h:313
#define IMAGE_ALIGN_TextTop
Definition: TGHtml.h:567
void Paragraph(TGHtmlElement *p)
Increase the headroom to create a paragraph break at the current token.
Html_16_t fAscent
Definition: TGHtml.h:550
TGHtmlElement * fPOther
Definition: TGHtml.h:414
#define STY_Invisible
Definition: TGHtml.h:243
Html_32_t fY
Definition: TGHtml.h:599
Html_16_t fX
Definition: TGHtml.h:552
void LayoutBlock()
Do as much layout as possible on the block of text defined by the HtmlLayoutContext.
void ClearObstacle(int mode)
Clear a wrap-around obstacle.
Html_u8_t fIs3D
Definition: TGHtml.h:660
#define ALIGN_Center
Definition: TGHtml.h:214
#define STY_DT
Definition: TGHtml.h:242
Html_u8_t fTextAscent
Definition: TGHtml.h:544
#define HTML_INDENT
Definition: TGHtml.h:757
void PushIndent()
Adjust (push) ident.
SHtmlMargin_t * fLeftMargin
Definition: TGHtml.h:808
TGHtmlLayoutContext()
Html Layout Context constructor.
virtual TGFont * GetFont(int iFont)
The rendering and layout routines should call this routine in order to get a font structure...
Definition: TGHtml.cxx:1404
int fBottom
Definition: TGHtml.h:748
TGHtmlElement * TableLayout(TGHtmlTable *p)
Do all layout for a single table.
#define STY_Preformatted
Definition: TGHtml.h:237
Html_16_t fW
Definition: TGHtml.h:549
TGHtmlElement * fPNext
Definition: TGHtml.h:265
TGHtmlElement * DoBreakMarkup(TGHtmlElement *p)
Break markup is any kind of markup that might force a line-break.
void PushMargin(SHtmlMargin_t **ppMargin, int indent, int bottom, int tag)
Push a new margin onto the given margin stack.
void PopExpiredMargins(SHtmlMargin_t **ppMarginStack, int y)
Pop all expired margins from the stack.
Html_32_t fY
Definition: TGHtml.h:657
Double_t x[n]
Definition: legend1.C:17
void FixAnchors(TGHtmlElement *p, TGHtmlElement *pEnd, int y)
Set the y coordinate for every anchor in the given list.
void LayoutDoc()
Advance the layout as far as possible.
unsigned int fFont
Definition: TGHtml.h:148
Html_u16_t fW
Definition: TGHtml.h:659
Html_u8_t fAlign
Definition: TGHtml.h:543
int GetRuleRelief() const
Definition: TGHtml.h:904
int FixLine(TGHtmlElement *pStart, TGHtmlElement *pEnd, int bottom, int width, int actualWidth, int leftMargin, int *maxX)
This routine computes the X and Y coordinates for all elements of a line that has been gathered using...
Html_16_t fX
Definition: TGHtml.h:299
Html_32_t fY
Definition: TGHtml.h:553
int GetMarginWidth()
Definition: TGHtml.h:967
TGHtmlElement * fPEnd
Definition: TGHtml.h:800
Html_u8_t fAscent
Definition: TGHtml.h:301
#define HTML_RELIEF_SUNKEN
Definition: TGHtml.h:55
#define IMAGE_ALIGN_AbsBottom
Definition: TGHtml.h:569
#define IMAGE_ALIGN_Top
Definition: TGHtml.h:566
SHtmlStyle_t fStyle
Definition: TGHtml.h:267
Html_u16_t fW
Definition: TGHtml.h:601
Html_u8_t fPadLeft
Definition: TGHtml.h:602
#define CLEAR_Right
Html_u8_t fTextDescent
Definition: TGHtml.h:545
Html_u16_t fX
Definition: TGHtml.h:600
SHtmlMargin_t * fPNext
Definition: TGHtml.h:750
#define ALIGN_None
Definition: TGHtml.h:215
static void indent(ostringstream &buf, int indent_level)
Html_u8_t fType
Definition: TGHtml.h:268
virtual int IsMarkup() const
Definition: TGHtml.h:257
#define HTML_NewLine
Definition: TGHtml.h:279
Html_u16_t fH
Definition: TGHtml.h:659
unsigned int fFlags
Definition: TGHtml.h:154
Html_u16_t fX
Definition: TGHtml.h:658
void PopOneMargin(SHtmlMargin_t **ppMargin)
Pop one margin off of the given margin stack.
#define IMAGE_ALIGN_Middle
Definition: TGHtml.h:565
TText * text
Html_16_t fH
Definition: TGHtml.h:548
#define CLEAR_First
Html_u8_t fTextAscent
Definition: TGHtml.h:604
int GetMarginHeight()
Definition: TGHtml.h:968
Definition: TGFont.h:155
#define VSCROLL
Definition: TGHtml.h:1327
Html_16_t fW
Definition: TGHtml.h:300
Double_t y[n]
Definition: legend1.C:17
void PopMargin(SHtmlMargin_t **ppMargin, int tag)
Pop as many margins as necessary until the margin that was created with "tag" is popped off...
Html_u16_t fH
Definition: TGHtml.h:601
#define STY_NoBreak
Definition: TGHtml.h:240
#define IMAGE_ALIGN_AbsMiddle
Definition: TGHtml.h:568
void WidenLine(int reqWidth, int *pX, int *pY, int *pW)
Move past obstacles until a linewidth of reqWidth is obtained, or until all obstacles are cleared...
Html_32_t fY
Definition: TGHtml.h:432
you should not use this method at all Int_t Int_t z
Definition: TRolke.cxx:630
#define HTML_RELIEF_RAISED
Definition: TGHtml.h:56
Html_u8_t fDescent
Definition: TGHtml.h:429
#define IMAGE_ALIGN_Bottom
Definition: TGHtml.h:564
Html_16_t fCount
Definition: TGHtml.h:270
Html_u8_t fTextDescent
Definition: TGHtml.h:605
TGHtmlElement * fPStart
Definition: TGHtml.h:799
int InWrapAround()
Return TRUE (non-zero) if we are currently wrapping text around one or more images.
Int_t fAscent
Definition: TGFont.h:68
#define ALIGN_Right
Definition: TGHtml.h:213
Int_t fDescent
Definition: TGFont.h:69
void ComputeMargins(int *pX, int *pY, int *pW)
Compute the current margins for layout.
unsigned int fAlign
Definition: TGHtml.h:151
void GetFontMetrics(FontMetrics_t *m) const
Get font metrics.
Definition: TGFont.cxx:274
#define HSCROLL
Definition: TGHtml.h:1326
void ClearMarginStack(SHtmlMargin_t **ppMargin)
Clear a margin stack to reclaim memory.
#define IMAGE_ALIGN_Right
Definition: TGHtml.h:571
int fIndent
Definition: TGHtml.h:747
Html_16_t fW
Definition: TGHtml.h:312
virtual const char * MarkupArg(const char *tag, const char *zDefault)
Lookup an argument in the given markup with the name given.
#define CLEAR_Left
Html_32_t fY
Definition: TGHtml.h:298
signed int fSubscript
Definition: TGHtml.h:150
int GetRulePadding() const
Definition: TGHtml.h:905
SHtmlMargin_t * fRightMargin
Definition: TGHtml.h:809
Html_16_t fDescent
Definition: TGHtml.h:551
void PopIndent()
Adjust (pop) ident.
void Reset()
Reset the layout context.
Html_u8_t fFlags
Definition: TGHtml.h:269
int NextMarkupType(TGHtmlElement *p)
Return the next markup type [TGHtmlElement::NextMarkupType].
Html_u8_t fDescent
Definition: TGHtml.h:302