Logo ROOT   6.21/01
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
1190  fLayoutContext.fLeft = 0;
1191 #else
1195 #endif
1196  fLayoutContext.fRight = 0;
1199  if (fLayoutContext.fPStart) {
1200  TGHtmlElement *p;
1201 
1204  btm = fLayoutContext.fBottom;
1207 #if 0
1209 #else
1211 #endif
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:427
TGHtml * fHtml
Definition: TGHtml.h:794
#define IMAGE_ALIGN_Left
Definition: TGHtml.h:566
#define CLEAR_Both
Html_u8_t fAscent
Definition: TGHtml.h:309
#define IMAGE_ALIGN_TextTop
Definition: TGHtml.h:563
void Paragraph(TGHtmlElement *p)
Increase the headroom to create a paragraph break at the current token.
Html_16_t fAscent
Definition: TGHtml.h:546
TGHtmlElement * AttrElem(const char *name, char *value)
Returns html element matching attribute name and value.
Definition: TGHtml.cxx:1848
TGHtmlElement * fNextPlaced
Definition: TGHtml.h:1133
TGHtmlElement * fPOther
Definition: TGHtml.h:410
#define STY_Invisible
Definition: TGHtml.h:239
UInt_t fYMargin
Definition: TGView.h:61
TGLongPosition fVisible
Definition: TGView.h:52
Html_32_t fY
Definition: TGHtml.h:595
Html_16_t fX
Definition: TGHtml.h:548
void Sizer()
Compute the size of all elements in the widget.
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:656
#define ALIGN_Center
Definition: TGHtml.h:210
#define STY_DT
Definition: TGHtml.h:238
Html_u8_t fTextAscent
Definition: TGHtml.h:540
#define HTML_INDENT
Definition: TGHtml.h:753
void PushIndent()
Adjust (push) ident.
SHtmlMargin_t * fLeftMargin
Definition: TGHtml.h:804
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:1406
int fBottom
Definition: TGHtml.h:744
UInt_t GetWidth() const
Definition: TGFrame.h:271
TGHtmlElement * TableLayout(TGHtmlTable *p)
Do all layout for a single table.
#define STY_Preformatted
Definition: TGHtml.h:233
Html_16_t fW
Definition: TGHtml.h:545
TGHtmlElement * fPNext
Definition: TGHtml.h:261
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:653
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:144
Html_u16_t fW
Definition: TGHtml.h:655
int fMaxY
Definition: TGHtml.h:1262
Html_u8_t fAlign
Definition: TGHtml.h:539
int GetRuleRelief() const
Definition: TGHtml.h:900
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_32_t fY
Definition: TGHtml.h:549
int GetMarginWidth()
Definition: TGHtml.h:963
void RedrawText(int y)
Call this routine to cause all of the rendered HTML at the virtual canvas coordinate of Y and beyond ...
Definition: TGHtml.cxx:875
TGHtmlElement * fPEnd
Definition: TGHtml.h:796
#define HTML_RELIEF_SUNKEN
Definition: TGHtml.h:51
#define IMAGE_ALIGN_AbsBottom
Definition: TGHtml.h:565
#define IMAGE_ALIGN_Top
Definition: TGHtml.h:562
SHtmlStyle_t fStyle
Definition: TGHtml.h:263
TGViewFrame * fCanvas
Definition: TGView.h:62
Html_u16_t fW
Definition: TGHtml.h:597
Html_u8_t fPadLeft
Definition: TGHtml.h:598
#define CLEAR_Right
Double_t fY
Y position of text (left,center,etc..)
Definition: TText.h:27
Html_u8_t fTextDescent
Definition: TGHtml.h:541
Html_u16_t fX
Definition: TGHtml.h:596
SHtmlMargin_t * fPNext
Definition: TGHtml.h:746
#define ALIGN_None
Definition: TGHtml.h:211
char * fZGoto
Definition: TGHtml.h:1272
static void indent(ostringstream &buf, int indent_level)
Html_u8_t fType
Definition: TGHtml.h:264
virtual int IsMarkup() const
Definition: TGHtml.h:253
#define HTML_NewLine
Definition: TGHtml.h:275
Html_u16_t fH
Definition: TGHtml.h:655
include TDocParser_001 C image html pict1_TDocParser_001 png width
Definition: TDocParser.cxx:121
unsigned int fFlags
Definition: TGHtml.h:150
Html_u16_t fX
Definition: TGHtml.h:654
void PopOneMargin(SHtmlMargin_t **ppMargin)
Pop one margin off of the given margin stack.
#define IMAGE_ALIGN_Middle
Definition: TGHtml.h:561
TText * text
Html_16_t fH
Definition: TGHtml.h:544
int fMaxX
Definition: TGHtml.h:1262
#define CLEAR_First
Html_u8_t fTextAscent
Definition: TGHtml.h:600
int GetMarginHeight()
Definition: TGHtml.h:964
Definition: TGFont.h:149
#define VSCROLL
Definition: TGHtml.h:1323
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:597
TGHtmlElement * fPFirst
Definition: TGHtml.h:1128
#define STY_NoBreak
Definition: TGHtml.h:236
#define IMAGE_ALIGN_AbsMiddle
Definition: TGHtml.h:564
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:428
you should not use this method at all Int_t Int_t z
Definition: TRolke.cxx:630
#define HTML_RELIEF_RAISED
Definition: TGHtml.h:52
Html_u8_t fDescent
Definition: TGHtml.h:425
int fFlags
Definition: TGHtml.h:1269
#define IMAGE_ALIGN_Bottom
Definition: TGHtml.h:560
Html_16_t fCount
Definition: TGHtml.h:266
Html_u8_t fTextDescent
Definition: TGHtml.h:601
TGHtmlElement * fPStart
Definition: TGHtml.h:795
int InWrapAround()
Return TRUE (non-zero) if we are currently wrapping text around one or more images.
Int_t fAscent
Definition: TGFont.h:62
#define ALIGN_Right
Definition: TGHtml.h:209
Int_t fDescent
Definition: TGFont.h:63
void ComputeMargins(int *pX, int *pY, int *pW)
Compute the current margins for layout.
unsigned int fAlign
Definition: TGHtml.h:147
void GetFontMetrics(FontMetrics_t *m) const
Get font metrics.
Definition: TGFont.cxx:274
#define HSCROLL
Definition: TGHtml.h:1322
void ClearMarginStack(SHtmlMargin_t **ppMargin)
Clear a margin stack to reclaim memory.
#define IMAGE_ALIGN_Right
Definition: TGHtml.h:567
int fIndent
Definition: TGHtml.h:743
Html_16_t fW
Definition: TGHtml.h:308
virtual const char * MarkupArg(const char *tag, const char *zDefault)
Lookup an argument in the given markup with the name given.
#define CLEAR_Left
signed int fSubscript
Definition: TGHtml.h:146
int GetRulePadding() const
Definition: TGHtml.h:901
SHtmlMargin_t * fRightMargin
Definition: TGHtml.h:805
Html_16_t fDescent
Definition: TGHtml.h:547
void PopIndent()
Adjust (pop) ident.
TGHtmlLayoutContext fLayoutContext
Definition: TGHtml.h:1207
void Reset()
Reset the layout context.
Html_u8_t fFlags
Definition: TGHtml.h:265
Double_t fX
X position of text (left,center,etc..)
Definition: TText.h:26
int NextMarkupType(TGHtmlElement *p)
Return the next markup type [TGHtmlElement::NextMarkupType].