TGHtmlLayout.cxx

Go to the documentation of this file.
00001 // $Id: TGHtmlLayout.cxx,v 1.1 2007/05/04 17:07:01 brun Exp $
00002 // Author:  Valeriy Onuchin   03/05/2007
00003 
00004 /**************************************************************************
00005 
00006     HTML widget for xclass. Based on tkhtml 1.28
00007     Copyright (C) 1997-2000 D. Richard Hipp <drh@acm.org>
00008     Copyright (C) 2002-2003 Hector Peraza.
00009 
00010     This library is free software; you can redistribute it and/or
00011     modify it under the terms of the GNU Library General Public
00012     License as published by the Free Software Foundation; either
00013     version 2 of the License, or (at your option) any later version.
00014 
00015     This library is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018     Library General Public License for more details.
00019 
00020     You should have received a copy of the GNU Library General Public
00021     License along with this library; if not, write to the Free
00022     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00023 
00024 **************************************************************************/
00025 
00026 // This file contains the code used to position elements of the
00027 // HTML file on the screen.
00028 
00029 #include <stdlib.h>
00030 #include <string.h>
00031 
00032 #include "TGHtml.h"
00033 
00034 
00035 //______________________________________________________________________________
00036 TGHtmlLayoutContext::TGHtmlLayoutContext()
00037 {
00038    // Html Layout Context constructor.
00039 
00040    fPStart = 0;
00041    fPEnd = 0;
00042    fLeftMargin = 0;
00043    fRightMargin = 0;
00044    fHtml = 0;
00045    fLeft = 0;
00046    fRight = 0;
00047    fMaxX = 0;
00048    fMaxY = 0;
00049    fPageWidth = 0;
00050    Reset();
00051 }
00052 
00053 //______________________________________________________________________________
00054 void TGHtmlLayoutContext::Reset()
00055 {
00056    // Reset the layout context.
00057 
00058    fHeadRoom = 0;
00059    fTop = 0;
00060    fBottom = 0;
00061    ClearMarginStack(&fLeftMargin);
00062    ClearMarginStack(&fRightMargin);
00063 }
00064 
00065 //______________________________________________________________________________
00066 void TGHtmlLayoutContext::PushMargin(SHtmlMargin_t **ppMargin,
00067                                     int indent, int mbottom, int tag)
00068 {
00069    // Push a new margin onto the given margin stack.
00070    //
00071    // If the "bottom" parameter is non-negative, then this margin will
00072    // automatically expire for all text that is placed below the y-coordinate
00073    // given by "bottom". This feature is used for <IMG ALIGN=left> and <IMG
00074    // ALIGN=right> kinds of markup. It allows text to flow around an image.
00075    //
00076    // If "bottom" is negative, then the margin stays in force until it is
00077    // explicitly canceled by a call to PopMargin().
00078    //
00079    //  ppMargin - The margin stack onto which to push
00080    //  indent   - The indentation for the new margin
00081    //  mbottom  - The margin expires at this Y coordinate
00082    //  tag      - Markup that will cancel this margin
00083 
00084    SHtmlMargin_t *pNew = new SHtmlMargin_t;
00085    pNew->fPNext = *ppMargin;
00086    if (pNew->fPNext) {
00087       pNew->fIndent = indent + pNew->fPNext->fIndent;
00088    } else {
00089       pNew->fIndent = indent;
00090    }
00091    pNew->fBottom = mbottom;
00092    pNew->fTag = tag;
00093    *ppMargin = pNew;
00094 }
00095 
00096 //______________________________________________________________________________
00097 void TGHtmlLayoutContext::PopOneMargin(SHtmlMargin_t **ppMargin)
00098 {
00099    // Pop one margin off of the given margin stack.
00100 
00101    if (*ppMargin) {
00102       SHtmlMargin_t *pOld = *ppMargin;
00103       *ppMargin = pOld->fPNext;
00104       delete pOld;
00105    }
00106 }
00107 
00108 //______________________________________________________________________________
00109 void TGHtmlLayoutContext::PopMargin(SHtmlMargin_t **ppMargin, int tag)
00110 {
00111    // Pop as many margins as necessary until the margin that was
00112    // created with "tag" is popped off. Update the layout context
00113    // to move past obstacles, if necessary.
00114    //
00115    // If there are some margins on the stack that contain non-negative
00116    // bottom fields, that means there are some obstacles that we have
00117    // not yet cleared. If these margins get popped off the stack,
00118    // then we have to be careful to advance the 'bottom' value so
00119    // that the next line of text will clear the obstacle.
00120 
00121    int bot = -1;
00122    int oldTag;
00123    SHtmlMargin_t *pM;
00124 
00125    for (pM = *ppMargin; pM && pM->fTag != tag; pM = pM->fPNext) {}
00126    if (pM == 0) {
00127       // No matching margin is found. Do nothing.
00128       return;
00129    }
00130    while ((pM = *ppMargin) != 0) {
00131       if (pM->fBottom > bot) bot = pM->fBottom;
00132       oldTag = pM->fTag;
00133       PopOneMargin(ppMargin);
00134       if (oldTag == tag) break;
00135    }
00136    if (fBottom < bot) {
00137       fHeadRoom += bot - fBottom;
00138       fBottom = bot;
00139    }
00140 }
00141 
00142 //______________________________________________________________________________
00143 void TGHtmlLayoutContext::PopExpiredMargins(SHtmlMargin_t **ppMarginStack, int y)
00144 {
00145    // Pop all expired margins from the stack.
00146    //
00147    // An expired margin is one with a non-negative bottom parameter
00148    // that is less than the value "y". "y" is the Y-coordinate of
00149    // the top edge the next line of text to by positioned. What this
00150    // function does is check to see if we have cleared any obstacles
00151    // (an obstacle is an <IMG ALIGN=left> or <IMG ALIGN=right>) and
00152    // expands the margins if we have.
00153 
00154    while (*ppMarginStack && (**ppMarginStack).fBottom >= 0 &&
00155          (**ppMarginStack).fBottom <= y) {
00156       PopOneMargin(ppMarginStack);
00157    }
00158 }
00159 
00160 //______________________________________________________________________________
00161 void TGHtmlLayoutContext::ClearMarginStack(SHtmlMargin_t **ppMargin)
00162 {
00163    // Clear a margin stack to reclaim memory. This routine just blindly
00164    // pops everything off the stack. Typically used when the screen is
00165    // cleared or the widget is deleted, etc.
00166 
00167    while (*ppMargin) PopOneMargin(ppMargin);
00168 }
00169 
00170 //______________________________________________________________________________
00171 TGHtmlElement *TGHtmlLayoutContext::GetLine(TGHtmlElement *p_start,
00172                 TGHtmlElement *p_end, int width, int minX, int *actualWidth)
00173 {
00174    // This routine gathers as many tokens as will fit on one line.
00175    //
00176    // The candidate tokens begin with fPStart and go thru the end of
00177    // the list or to fPEnd, whichever comes first. The first token
00178    // at the start of the next line is returned. NULL is returned if
00179    // we exhaust data.
00180    //
00181    // "width" is the maximum allowed width of the line. The actual
00182    // width is returned in *actualWidth. The actual width does not
00183    // include any trailing spaces. Sometimes the actual width will
00184    // be greater than the maximum width. This will happen, for example,
00185    // for text enclosed in <pre>..</pre> that has lines longer than
00186    // the width of the page.
00187    //
00188    // If the list begins with text, at least one token is returned,
00189    // even if that one token is longer than the allowed line length.
00190    // But if the list begins with some kind of break markup (possibly
00191    // preceded by white space) then the returned list may be empty.
00192    //
00193    // The "x" coordinates of all elements are set assuming that the line
00194    // begins at 0. The calling routine should adjust these coordinates
00195    // to position the line horizontally. (The FixLine() procedure does
00196    // this.)  Note that the "x" coordinate of <li> elements will be negative.
00197    // Text within <dt>..</dt> might also have a negative "x" coordinate.
00198    // But in no case will the x coordinate every be less than "minX".
00199    //
00200    // p_start     - First token on new line
00201    // p_end       - End of line. Might be NULL
00202    // width       - How much space is on this line
00203    // minX        - The minimum value of the X coordinate
00204    // actualWidth - Return space actually required
00205 
00206    int x;                        // Current X coordinate
00207    int spaceWanted = 0;          // Add this much space before next token
00208    TGHtmlElement *p;              // For looping over tokens
00209    TGHtmlElement *lastBreak = 0;  // Last line-break opportunity
00210    int isEmpty = 1;              // True if link contains nothing
00211    int origin;                   // Initial value of "x"
00212 
00213    *actualWidth = 0;
00214    p = p_start;
00215    while (p && p != p_end && (p->fStyle.fFlags & STY_Invisible) != 0) {
00216       p = p->fPNext;
00217    }
00218    if (p && p->fStyle.fFlags & STY_DT) {
00219       origin = -HTML_INDENT;
00220    } else {
00221       origin = 0;
00222    }
00223    x = origin;
00224    if (x < minX) x = minX;
00225    if (p && p != p_end && p->fType == Html_LI) {
00226       TGHtmlLi *li = (TGHtmlLi *) p;
00227       li->fX = x - HTML_INDENT / 3;
00228       if (li->fX - (HTML_INDENT * 2) / 3 < minX) {
00229          x += minX - li->fX + (HTML_INDENT * 2) / 3;
00230          li->fX = minX + (HTML_INDENT * 2) / 3;
00231       }
00232       isEmpty = 0;
00233       *actualWidth = 1;
00234       p = p->fPNext;
00235       while (p && (p->fType == Html_Space || p->fType == Html_P)) {
00236          p = p->fPNext;
00237       }
00238    }
00239    for (; p && p != p_end; p = p ? p->fPNext : 0) {
00240       if (p->fStyle.fFlags & STY_Invisible) continue;
00241       switch (p->fType) {
00242          case Html_Text: {
00243             TGHtmlTextElement *text = (TGHtmlTextElement *) p;
00244             text->fX = x + spaceWanted;
00245             if ((p->fStyle.fFlags & STY_Preformatted) == 0) {
00246                if (lastBreak && x + spaceWanted + text->fW > width)
00247                   return lastBreak;
00248             }
00249 //        TRACE(HtmlTrace_GetLine2, ("Place token %s at x=%d w=%d\n",
00250 //           HtmlTokenName(p), text->fX, text->fW));
00251             x += text->fW + spaceWanted;
00252             isEmpty = 0;
00253             spaceWanted = 0;
00254             break;
00255          }
00256 
00257          case Html_Space: {
00258             TGHtmlSpaceElement *space = (TGHtmlSpaceElement *) p;
00259             if (p->fStyle.fFlags & STY_Preformatted) {
00260                if (p->fFlags & HTML_NewLine) {
00261                   *actualWidth = (x <= 0) ? 1 : x;
00262                   return p->fPNext;
00263                }
00264                x += space->fW * p->fCount;
00265             } else {
00266                int w;
00267                if ((p->fStyle.fFlags & STY_NoBreak) == 0) {
00268                   lastBreak = p->fPNext;
00269                   *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
00270                }
00271                w = space->fW;
00272                if (spaceWanted < w && x > origin) spaceWanted = w;
00273             }
00274             break;
00275          }
00276 
00277          case Html_IMG: {
00278             TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) p;
00279             switch (image->fAlign) {
00280                case IMAGE_ALIGN_Left:
00281                case IMAGE_ALIGN_Right:
00282                   *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
00283                   return p;
00284                default:
00285                   break;
00286             }
00287             image->fX = x + spaceWanted;
00288             if ((p->fStyle.fFlags & STY_Preformatted) == 0) {
00289                if (lastBreak && x + spaceWanted + image->fW > width) {
00290                   return lastBreak;
00291                }
00292             }
00293 //        TRACE(HtmlTrace_GetLine2, ("Place in-line image %s at x=%d w=%d\n",
00294 //           HtmlTokenName(p), p->image.x, p->image.w));
00295             x += image->fW + spaceWanted;
00296             if ((p->fStyle.fFlags & STY_NoBreak) == 0) {
00297                lastBreak = p->fPNext;
00298                *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
00299             }
00300             spaceWanted = 0;
00301             isEmpty = 0;
00302             break;
00303          }
00304 
00305          case Html_APPLET:
00306          case Html_EMBED:
00307          case Html_INPUT:
00308          case Html_SELECT:
00309          case Html_TEXTAREA: {
00310             TGHtmlInput *input = (TGHtmlInput *) p;
00311             input->fX = x + spaceWanted + input->fPadLeft;
00312             if ((p->fStyle.fFlags & STY_Preformatted) == 0) {
00313                if (lastBreak && x + spaceWanted + input->fW > width) {
00314                   return lastBreak;
00315                }
00316             }
00317 //        TRACE(HtmlTrace_GetLine2, ("Place token %s at x=%d w=%d\n",
00318 //           HtmlTokenName(p), p->input.x, p->input.w));
00319             x = input->fX + input->fW;
00320             if ((p->fStyle.fFlags & STY_NoBreak) == 0) {
00321                lastBreak = p->fPNext;
00322                *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
00323             }
00324             spaceWanted = 0;
00325             isEmpty = 0;
00326             break;
00327          }
00328 
00329          case Html_EndTEXTAREA: {
00330             TGHtmlRef *ref = (TGHtmlRef *) p;
00331             if (ref->fPOther) {
00332                // fHtml->ResetTextarea(ref->fPOther);
00333             }
00334             break;
00335          }
00336 
00337          case Html_DD: {
00338             TGHtmlRef *ref = (TGHtmlRef *) p;
00339             if (ref->fPOther == 0) break;
00340                if (((TGHtmlListStart *)ref->fPOther)->fCompact == 0 ||
00341                   x + spaceWanted >= 0) {
00342                   *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
00343                   return p;
00344                }
00345                x = 0;
00346                spaceWanted = 0;
00347                break;
00348          }
00349 
00350          case Html_WBR:
00351             *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
00352             if (x + spaceWanted >= width) {
00353                return p->fPNext;
00354             } else {
00355                lastBreak = p->fPNext;
00356             }
00357             break;
00358 
00359          case Html_ADDRESS:
00360          case Html_EndADDRESS:
00361          case Html_BLOCKQUOTE:
00362          case Html_EndBLOCKQUOTE:
00363          case Html_BODY:
00364          case Html_EndBODY:
00365          case Html_BR:
00366          case Html_CAPTION:
00367          case Html_EndCAPTION:
00368          case Html_CENTER:
00369          case Html_EndCENTER:
00370          case Html_EndDD:
00371          case Html_DIV:
00372          case Html_EndDIV:
00373          case Html_DL:
00374          case Html_EndDL:
00375          case Html_DT:
00376          case Html_H1:
00377          case Html_EndH1:
00378          case Html_H2:
00379          case Html_EndH2:
00380          case Html_H3:
00381          case Html_EndH3:
00382          case Html_H4:
00383          case Html_EndH4:
00384          case Html_H5:
00385          case Html_EndH5:
00386          case Html_H6:
00387          case Html_EndH6:
00388          case Html_EndHTML:
00389          case Html_HR:
00390          case Html_LI:
00391          case Html_LISTING:
00392          case Html_EndLISTING:
00393          case Html_MENU:
00394          case Html_EndMENU:
00395          case Html_OL:
00396          case Html_EndOL:
00397          case Html_P:
00398          case Html_EndP:
00399          case Html_PRE:
00400          case Html_EndPRE:
00401          case Html_TABLE:
00402          case Html_EndTABLE:
00403          case Html_TD:
00404          case Html_EndTD:
00405          case Html_TH:
00406          case Html_EndTH:
00407          case Html_TR:
00408          case Html_EndTR:
00409          case Html_UL:
00410          case Html_EndUL:
00411          case Html_EndFORM:
00412             *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
00413             return p;
00414 
00415          default:
00416             break;
00417       }
00418    }
00419    *actualWidth = ((x <= 0) && !isEmpty) ? 1 : x;
00420 
00421    return p;
00422 }
00423 
00424 //______________________________________________________________________________
00425 void TGHtmlLayoutContext::FixAnchors(TGHtmlElement *p, TGHtmlElement *p_end, int y)
00426 {
00427    // Set the y coordinate for every anchor in the given list
00428 
00429    while (p && p != p_end) {
00430       if (p->fType == Html_A) ((TGHtmlAnchor *)p)->fY = y;
00431       p = p->fPNext;
00432    }
00433 }
00434 
00435 //______________________________________________________________________________
00436 int TGHtmlLayoutContext::FixLine(TGHtmlElement *p_start,
00437                TGHtmlElement *p_end, int mbottom, int width,
00438                int actualWidth, int lMargin, int *max_x)
00439 {
00440    // This routine computes the X and Y coordinates for all elements of
00441    // a line that has been gathered using GetLine() above. It also figures
00442    // the ascent and descent for in-line images.
00443    //
00444    // The value returned is the Y coordinate of the bottom edge of the
00445    // new line. The X coordinates are computed by adding the left margin
00446    // plus any extra space needed for centering or right-justification.
00447    //
00448    // p_start     - Start of tokens for this line
00449    // p_end       - First token past end of this line. Maybe NULL
00450    // mbottom     - Put the top of this line here
00451    // width       - This is the space available to the line
00452    // actualWidth - This is the actual width needed by the line
00453    // lMargin     - The current left margin
00454    // max_x       - Write maximum X coordinate of ink here
00455 
00456    int dx;                // Amount by which to increase all X coordinates
00457    int maxAscent;         // Maximum height above baseline
00458    int maxTextAscent;     // Maximum height above baseline for text
00459    int maxDescent;        // Maximum depth below baseline
00460    int ascent, descent;   // Computed ascent and descent for one element
00461    TGHtmlElement *p;      // For looping
00462    int y;                 // Y coordinate of the baseline
00463    int dy2center;         // Distance from baseline to text font center
00464    int max = 0;
00465 
00466    if (actualWidth > 0) {
00467       for (p = p_start; p && p != p_end && p->fType != Html_Text; p = p->fPNext) {}
00468       if (p == p_end || p == 0) p = p_start;
00469       maxAscent = maxTextAscent = 0;
00470       for (p = p_start; p && p != p_end; p = p->fPNext) {
00471          int ss;
00472          if (p->fStyle.fAlign == ALIGN_Center) {
00473             dx = lMargin + (width - actualWidth) / 2;
00474          } else if (p->fStyle.fAlign == ALIGN_Right) {
00475             dx = lMargin + (width - actualWidth);
00476          } else {
00477             dx = lMargin;
00478          }
00479          if (dx < 0) dx = 0;
00480          if (p->fStyle.fFlags & STY_Invisible) continue;
00481          switch (p->fType) {
00482             case Html_Text: {
00483                TGHtmlTextElement *text = (TGHtmlTextElement *) p;
00484                text->fX += dx;
00485                max = text->fX + text->fW;
00486                ss = p->fStyle.fSubscript;
00487                if (ss > 0) {
00488                   int ascent2 = text->fAscent;
00489                   int delta = (ascent2 + text->fDescent) * ss / 2;
00490                   ascent2 += delta;
00491                   text->fY = -delta;
00492                   if (ascent2 > maxAscent) maxAscent = ascent2;
00493                   if (ascent2 > maxTextAscent) maxTextAscent = ascent2;
00494                } else if (ss < 0) {
00495                   int descent2 = text->fDescent;
00496                   int delta = (descent2 + text->fAscent) * (-ss) / 2;
00497                   descent2 += delta;
00498                   text->fY = delta;
00499                } else {
00500                   text->fY = 0;
00501                   if (text->fAscent > maxAscent) maxAscent = text->fAscent;
00502                   if (text->fAscent > maxTextAscent) maxTextAscent = text->fAscent;
00503                }
00504                break;
00505             }
00506 
00507             case Html_Space: {
00508                TGHtmlSpaceElement *space = (TGHtmlSpaceElement *) p;
00509                if (space->fAscent > maxAscent) maxAscent = space->fAscent;
00510                break;
00511             }
00512 
00513             case Html_LI: {
00514                TGHtmlLi *li = (TGHtmlLi *) p;
00515                li->fX += dx;
00516                if (li->fX > max) max = li->fX;
00517                break;
00518             }
00519 
00520             case Html_IMG: {
00521                TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) p;
00522                image->fX += dx;
00523                max = image->fX + image->fW;
00524                switch (image->fAlign) {
00525                   case IMAGE_ALIGN_Middle:
00526                      image->fDescent = image->fH / 2;
00527                      image->fAscent = image->fH - image->fDescent;
00528                      if (image->fAscent > maxAscent) maxAscent = image->fAscent;
00529                      break;
00530 
00531                   case IMAGE_ALIGN_AbsMiddle:
00532                      dy2center = (image->fTextDescent - image->fTextAscent) / 2;
00533                      image->fDescent = image->fH / 2 + dy2center;
00534                      image->fAscent = image->fH - image->fDescent;
00535                      if (image->fAscent > maxAscent) maxAscent = image->fAscent;
00536                      break;
00537 
00538                   case IMAGE_ALIGN_Bottom:
00539                      image->fDescent = 0;
00540                      image->fAscent = image->fH;
00541                      if (image->fAscent > maxAscent) maxAscent = image->fAscent;
00542                      break;
00543 
00544                   case IMAGE_ALIGN_AbsBottom:
00545                      image->fDescent = image->fTextDescent;
00546                      image->fAscent = image->fH - image->fDescent;
00547                      if (image->fAscent > maxAscent) maxAscent = image->fAscent;
00548                      break;
00549 
00550                   default:
00551                      break;
00552                }
00553                break;
00554             }
00555 
00556             case Html_TABLE:
00557                break;
00558 
00559             case Html_TEXTAREA:
00560             case Html_INPUT:
00561             case Html_SELECT:
00562             case Html_EMBED:
00563             case Html_APPLET: {
00564                TGHtmlInput *input = (TGHtmlInput *) p;
00565                input->fX += dx;
00566                max = input->fX + input->fW;
00567                dy2center = (input->fTextDescent - input->fTextAscent) / 2;
00568                input->fY = dy2center - input->fH / 2;
00569                ascent = -input->fY;
00570                if (ascent > maxAscent) maxAscent = ascent;
00571                break;
00572             }
00573 
00574             default:
00575                // Shouldn't happen
00576                break;
00577          }
00578       }
00579 
00580       *max_x = max;
00581       y = maxAscent + mbottom;
00582       maxDescent = 0;
00583 
00584       for (p = p_start; p && p != p_end; p = p->fPNext) {
00585          if (p->fStyle.fFlags & STY_Invisible) continue;
00586          switch (p->fType) {
00587             case Html_Text: {
00588                TGHtmlTextElement *text = (TGHtmlTextElement *) p;
00589                text->fY += y;
00590                if (text->fDescent > maxDescent) maxDescent = text->fDescent;
00591                break;
00592             }
00593 
00594             case Html_LI: {
00595                TGHtmlLi *li = (TGHtmlLi *) p;
00596                li->fY = y;
00597                if (li->fDescent > maxDescent) maxDescent = li->fDescent;
00598                break;
00599             }
00600 
00601             case Html_IMG: {
00602                TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) p;
00603                image->fY = y;
00604                switch (image->fAlign) {
00605                   case IMAGE_ALIGN_Top:
00606                      image->fAscent = maxAscent;
00607                      image->fDescent = image->fH - maxAscent;
00608                      break;
00609 
00610                   case IMAGE_ALIGN_TextTop:
00611                      image->fAscent = maxTextAscent;
00612                      image->fDescent = image->fH - maxTextAscent;
00613                      break;
00614 
00615                   default:
00616                      break;
00617                }
00618                if (image->fDescent > maxDescent) maxDescent = image->fDescent;
00619                break;
00620             }
00621 
00622             case Html_TABLE:
00623                break;
00624 
00625             case Html_INPUT:
00626             case Html_SELECT:
00627             case Html_TEXTAREA:
00628             case Html_APPLET:
00629             case Html_EMBED: {
00630                TGHtmlInput *input = (TGHtmlInput *) p;
00631                descent = input->fY + input->fH;
00632                input->fY += y;
00633                if (descent > maxDescent) maxDescent = descent;
00634                break;
00635             }
00636 
00637             default:
00638                /* Shouldn't happen */
00639                break;
00640          }
00641       }
00642 
00643 //    TRACE(HtmlTrace_FixLine,
00644 //       ("Setting baseline to %d. mbottom=%d ascent=%d descent=%d dx=%d\n",
00645 //       y, mbottom, maxAscent, maxDescent, dx));
00646 
00647    } else {
00648       maxDescent = 0;
00649       y = mbottom;
00650    }
00651 
00652    return y + maxDescent;
00653 }
00654 
00655 //______________________________________________________________________________
00656 void TGHtmlLayoutContext::Paragraph(TGHtmlElement *p)
00657 {
00658    // Increase the headroom to create a paragraph break at the current token
00659 
00660    int headroom;
00661 
00662    if (p == 0) return;
00663 
00664    if (p->fType == Html_Text) {
00665       TGHtmlTextElement *text = (TGHtmlTextElement *) p;
00666       headroom = text->fAscent + text->fDescent;
00667    } else if (p->fPNext && p->fPNext->fType == Html_Text) {
00668       TGHtmlTextElement *text = (TGHtmlTextElement *) p->fPNext;
00669       headroom = text->fAscent + text->fDescent;
00670    } else {
00671       //// headroom = 10;
00672       FontMetrics_t fontMetrics;
00673       TGFont *font;
00674       font = fHtml->GetFont(p->fStyle.fFont);
00675       if (font == 0) return;
00676       font->GetFontMetrics(&fontMetrics);
00677       headroom = fontMetrics.fDescent + fontMetrics.fAscent;
00678    }
00679    if (fHeadRoom < headroom && fBottom > fTop) fHeadRoom = headroom;
00680 }
00681 
00682 //______________________________________________________________________________
00683 void TGHtmlLayoutContext::ComputeMargins(int *pX, int *pY, int *pW)
00684 {
00685    // Compute the current margins for layout. Three values are returned:
00686    //
00687    //    *pY       The top edge of the area in which we can put ink. This
00688    //              takes into account any requested headroom.
00689    //
00690    //    *pX       The left edge of the inkable area. The takes into account
00691    //              any margin requests active at vertical position specified
00692    //              in pLC->bottom.
00693    //
00694    //    *pW       The width of the inkable area. This takes into account
00695    //              an margin requests that are active at the vertical position
00696    //              pLC->bottom.
00697    //
00698 
00699    int x, y, w;
00700 
00701    y = fBottom + fHeadRoom;
00702    PopExpiredMargins(&fLeftMargin, fBottom);
00703    PopExpiredMargins(&fRightMargin, fBottom);
00704    w = fPageWidth - fRight;
00705    if (fLeftMargin) {
00706       x = fLeftMargin->fIndent + fLeft;
00707    } else {
00708       x = fLeft;
00709    }
00710    w -= x;
00711    if (fRightMargin) w -= fRightMargin->fIndent;
00712 
00713    *pX = x;
00714    *pY = y;
00715    *pW = w;
00716 }
00717 
00718 #define CLEAR_Left  0
00719 #define CLEAR_Right 1
00720 #define CLEAR_Both  2
00721 #define CLEAR_First 3
00722 //______________________________________________________________________________
00723 void TGHtmlLayoutContext::ClearObstacle(int mode)
00724 {
00725    // Clear a wrap-around obstacle. The second option determines the
00726    // precise behavior.
00727    //
00728    //    CLEAR_Left        Clear all obstacles on the left.
00729    //
00730    //    CLEAR_Right       Clear all obstacles on the right.
00731    //
00732    //    CLEAR_Both        Clear all obstacles on both sides.
00733    //
00734    //    CLEAR_First       Clear only the first obstacle on either side.
00735 
00736    int newBottom = fBottom;
00737 
00738    PopExpiredMargins(&fLeftMargin, fBottom);
00739    PopExpiredMargins(&fRightMargin, fBottom);
00740 
00741    switch (mode) {
00742       case CLEAR_Both:
00743          ClearObstacle(CLEAR_Left);
00744          ClearObstacle(CLEAR_Right);
00745          break;
00746 
00747       case CLEAR_Left:
00748          while (fLeftMargin && fLeftMargin->fBottom >= 0) {
00749             if (newBottom < fLeftMargin->fBottom) {
00750                newBottom = fLeftMargin->fBottom;
00751             }
00752             PopOneMargin(&fLeftMargin);
00753          }
00754          if (newBottom > fBottom + fHeadRoom) {
00755             fHeadRoom = 0;
00756          } else {
00757             fHeadRoom = newBottom - fBottom;
00758          }
00759          fBottom = newBottom;
00760          PopExpiredMargins(&fRightMargin, fBottom);
00761          break;
00762 
00763       case CLEAR_Right:
00764          while (fRightMargin && fRightMargin->fBottom >= 0) {
00765             if (newBottom < fRightMargin->fBottom) {
00766                newBottom = fRightMargin->fBottom;
00767             }
00768             PopOneMargin(&fRightMargin);
00769          }
00770          if (newBottom > fBottom + fHeadRoom) {
00771             fHeadRoom = 0;
00772          } else {
00773             fHeadRoom = newBottom - fBottom;
00774          }
00775          fBottom = newBottom;
00776          PopExpiredMargins(&fLeftMargin, fBottom);
00777          break;
00778 
00779       case CLEAR_First:
00780          if (fLeftMargin && fLeftMargin->fBottom >= 0) {
00781             if (fRightMargin &&
00782                 fRightMargin->fBottom < fLeftMargin->fBottom) {
00783                if (newBottom < fRightMargin->fBottom) {
00784                   newBottom = fRightMargin->fBottom;
00785                }
00786                PopOneMargin(&fRightMargin);
00787             } else {
00788                if (newBottom < fLeftMargin->fBottom) {
00789                   newBottom = fLeftMargin->fBottom;
00790                }
00791                PopOneMargin(&fLeftMargin);
00792             }
00793          } else if (fRightMargin && fRightMargin->fBottom >= 0) {
00794             newBottom = fRightMargin->fBottom;
00795             PopOneMargin(&fRightMargin);
00796          }
00797          if (newBottom > fBottom + fHeadRoom) {
00798             fHeadRoom = 0;
00799          } else {
00800             fHeadRoom = newBottom - fBottom;
00801          }
00802          fBottom = newBottom;
00803          break;
00804 
00805       default:
00806          break;
00807    }
00808 }
00809 
00810 //______________________________________________________________________________
00811 int TGHtml::NextMarkupType(TGHtmlElement *p)
00812 {
00813    // Return the next markup type  [TGHtmlElement::NextMarkupType]
00814 
00815    while ((p = p->fPNext)) {
00816       if (p->IsMarkup()) return p->fType;
00817    }
00818    return Html_Unknown;
00819 }
00820 
00821 //______________________________________________________________________________
00822 TGHtmlElement *TGHtmlLayoutContext::DoBreakMarkup(TGHtmlElement *p)
00823 {
00824    // Break markup is any kind of markup that might force a line-break. This
00825    // routine handles a single element of break markup and returns a pointer
00826    // to the first element past that markup. If p doesn't point to break
00827    // markup, then p is returned. If p is an incomplete table (a <TABLE>
00828    // that lacks a </TABLE>), then NULL is returned.
00829 
00830    TGHtmlElement *fPNext = p->fPNext;
00831    const char *z;
00832    int x, y, w;
00833 
00834    switch (p->fType) {
00835       case Html_A:
00836          ((TGHtmlAnchor *)p)->fY = fBottom;
00837          break;
00838 
00839       case Html_BLOCKQUOTE:
00840          PushMargin(&fLeftMargin, HTML_INDENT, -1, Html_EndBLOCKQUOTE);
00841          PushMargin(&fRightMargin, HTML_INDENT, -1, Html_EndBLOCKQUOTE);
00842          Paragraph(p);
00843          break;
00844 
00845       case Html_EndBLOCKQUOTE:
00846          PopMargin(&fLeftMargin, Html_EndBLOCKQUOTE);
00847          PopMargin(&fRightMargin, Html_EndBLOCKQUOTE);
00848          Paragraph(p);
00849          break;
00850 
00851       case Html_IMG: {
00852          TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) p;
00853          switch (image->fAlign) {
00854             case IMAGE_ALIGN_Left:
00855                ComputeMargins(&x, &y, &w);
00856                image->fX = x;
00857                image->fY = y;
00858                image->fAscent = 0;
00859                image->fDescent = image->fH;
00860                PushMargin(&fLeftMargin, image->fW + 2, y + image->fH, 0);
00861                if (fMaxY < y + image->fH) fMaxY = y + image->fH;
00862                if (fMaxX < x + image->fW) fMaxX = x + image->fW;
00863                break;
00864 
00865             case IMAGE_ALIGN_Right:
00866                ComputeMargins(&x, &y, &w);
00867                image->fX = x + w - image->fW;
00868                image->fY = y;
00869                image->fAscent = 0;
00870                image->fDescent = image->fH;
00871                PushMargin(&fRightMargin, image->fW + 2, y + image->fH, 0);
00872                if (fMaxY < y + image->fH) fMaxY = y + image->fH;
00873                if (fMaxX < x + image->fW) fMaxX = x + image->fW;
00874                break;
00875 
00876             default:
00877                fPNext = p;
00878                break;
00879          }
00880          break;
00881       }
00882 
00883       case Html_PRE:
00884          // Skip space tokens thru the next newline.
00885          while (fPNext->fType == Html_Space) {
00886             TGHtmlElement *pThis = fPNext;
00887             fPNext = fPNext->fPNext;
00888             if (pThis->fFlags & HTML_NewLine) break;
00889          }
00890          Paragraph(p);
00891          break;
00892 
00893       case Html_UL:
00894       case Html_MENU:
00895       case Html_DIR:
00896       case Html_OL:
00897          if (((TGHtmlListStart *)p)->fCompact == 0) Paragraph(p);
00898          PushMargin(&fLeftMargin, HTML_INDENT, -1, p->fType + 1);
00899          break;
00900 
00901       case Html_EndOL:
00902       case Html_EndUL:
00903       case Html_EndMENU:
00904       case Html_EndDIR: {
00905          TGHtmlRef *ref = (TGHtmlRef *) p;
00906          if (ref->fPOther) {
00907             PopMargin(&fLeftMargin, p->fType);
00908             if (!((TGHtmlListStart *)ref->fPOther)->fCompact) Paragraph(p);
00909          }
00910          break;
00911       }
00912 
00913       case Html_DL:
00914          Paragraph(p);
00915          PushMargin(&fLeftMargin, HTML_INDENT, -1, Html_EndDL);
00916          break;
00917 
00918       case Html_EndDL:
00919          PopMargin(&fLeftMargin, Html_EndDL);
00920          Paragraph(p);
00921          break;
00922 
00923       case Html_HR: {
00924          int zl, wd;
00925          TGHtmlHr *hr = (TGHtmlHr *) p;
00926          hr->fIs3D = (p->MarkupArg("noshade", 0) == 0);
00927          z = p->MarkupArg("size", 0);
00928          if (z) {
00929             int hrsz = atoi(z);
00930             hr->fH = (hrsz < 0) ? 2 : hrsz;
00931          } else {
00932             hr->fH = 0;
00933          }
00934          if (hr->fH < 1) {
00935             int relief = fHtml->GetRuleRelief();
00936             if (hr->fIs3D &&
00937                 (relief == HTML_RELIEF_SUNKEN || relief == HTML_RELIEF_RAISED)) {
00938                hr->fH = 3;
00939             } else {
00940                hr->fH = 2;
00941             }
00942          }
00943          ComputeMargins(&x, &y, &w);
00944          hr->fY = y + fHtml->GetRulePadding();
00945          y += hr->fH + fHtml->GetRulePadding() * 2 + 1;
00946          hr->fX = x;
00947          z = p->MarkupArg("width", "100%");
00948          zl = strlen(z);
00949          if (zl > 0 && z[zl-1] == '%') {
00950             wd = (atoi(z) * w) / 100;
00951          } else {
00952             wd = atoi(z);
00953          }
00954          if (wd > w) wd = w;
00955          hr->fW = wd;
00956          switch (p->fStyle.fAlign) {
00957             case ALIGN_Center:
00958             case ALIGN_None:
00959                hr->fX += (w - wd) / 2;
00960                break;
00961 
00962             case ALIGN_Right:
00963                hr->fX += (w - wd);
00964                break;
00965 
00966             default:
00967                break;
00968          }
00969          if (fMaxY < y) fMaxY = y;
00970          if (fMaxX < wd + hr->fX) fMaxX = wd + hr->fX;
00971          fBottom = y;
00972          fHeadRoom = 0;
00973          break;
00974       }
00975 
00976       case Html_ADDRESS:
00977       case Html_EndADDRESS:
00978       case Html_CENTER:
00979       case Html_EndCENTER:
00980       case Html_DIV:
00981       case Html_EndDIV:
00982       case Html_H1:
00983       case Html_EndH1:
00984       case Html_H2:
00985       case Html_EndH2:
00986       case Html_H3:
00987       case Html_EndH3:
00988       case Html_H4:
00989       case Html_EndH4:
00990       case Html_H5:
00991       case Html_EndH5:
00992       case Html_H6:
00993       case Html_EndH6:
00994       case Html_P:
00995       case Html_EndP:
00996       case Html_EndPRE:
00997       case Html_EndFORM:
00998          Paragraph(p);
00999          break;
01000 
01001       case Html_TABLE:
01002          fPNext = TableLayout((TGHtmlTable *) p);
01003          break;
01004 
01005       case Html_BR:
01006          z = p->MarkupArg("clear",0);
01007          if (z) {
01008             if (strcasecmp(z, "left") == 0) {
01009                ClearObstacle(CLEAR_Left);
01010             } else if (strcasecmp(z, "right") == 0) {
01011                ClearObstacle(CLEAR_Right);
01012             } else {
01013                ClearObstacle(CLEAR_Both);
01014             }
01015          }
01016          if (p->fPNext && p->fPNext->fPNext && p->fPNext->fType == Html_Space &&
01017              p->fPNext->fPNext->fType == Html_BR) {
01018             Paragraph(p);
01019          }
01020          break;
01021 
01022       // All of the following tags need to be handed to the GetLine() routine
01023       case Html_Text:
01024       case Html_Space:
01025       case Html_LI:
01026       case Html_INPUT:
01027       case Html_SELECT:
01028       case Html_TEXTAREA:
01029       case Html_APPLET:
01030       case Html_EMBED:
01031          fPNext = p;
01032          break;
01033 
01034       default:
01035          break;
01036    }
01037 
01038    return fPNext;
01039 }
01040 
01041 //______________________________________________________________________________
01042 int TGHtmlLayoutContext::InWrapAround()
01043 {
01044    // Return TRUE (non-zero) if we are currently wrapping text around
01045    // one or more images.
01046 
01047    if (fLeftMargin && fLeftMargin->fBottom >= 0) return 1;
01048    if (fRightMargin && fRightMargin->fBottom >= 0) return 1;
01049    return 0;
01050 }
01051 
01052 //______________________________________________________________________________
01053 void TGHtmlLayoutContext::WidenLine(int reqWidth, int *pX, int *pY, int *pW)
01054 {
01055    // Move past obstacles until a linewidth of reqWidth is obtained,
01056    // or until all obstacles are cleared.
01057    //
01058    // reqWidth   - Requested line width
01059    // pX, pY, pW - The margins. See ComputeMargins()
01060 
01061    ComputeMargins(pX, pY, pW);
01062    if (*pW < reqWidth && InWrapAround()) {
01063       ClearObstacle(CLEAR_First);
01064       ComputeMargins(pX, pY, pW);
01065    }
01066 }
01067 
01068 
01069 #ifdef TABLE_TRIM_BLANK
01070 int HtmlLineWasBlank = 0;
01071 #endif // TABLE_TRIM_BLANK
01072 
01073 //______________________________________________________________________________
01074 void TGHtmlLayoutContext::LayoutBlock()
01075 {
01076    // Do as much layout as possible on the block of text defined by
01077    // the HtmlLayoutContext.
01078 
01079    TGHtmlElement *p, *pNext;
01080 
01081    for (p = fPStart; p && p != fPEnd; p = pNext) {
01082       int lineWidth;
01083       int actualWidth;
01084       int y = 0;
01085       int lMargin;
01086       int max_x = 0;
01087 
01088       // Do as much break markup as we can.
01089       while (p && p != fPEnd) {
01090          pNext = DoBreakMarkup(p);
01091          if (pNext == p) break;
01092          if (pNext) {
01093 //        TRACE(HtmlTrace_BreakMarkup,
01094 //           ("Processed token %s as break markup\n", HtmlTokenName(p)));
01095             fPStart = p;
01096          }
01097          p = pNext;
01098       }
01099 
01100       if (p == 0 || p == fPEnd) break;
01101 
01102 #ifdef TABLE_TRIM_BLANK
01103     HtmlLineWasBlank = 0;
01104 #endif // TABLE_TRIM_BLANK
01105 
01106       // We might try several times to layout a single line...
01107       while (1) {
01108 
01109          // Compute margins
01110          ComputeMargins(&lMargin, &y, &lineWidth);
01111 
01112          // Layout a single line of text
01113          pNext = GetLine(p, fPEnd, lineWidth, fLeft-lMargin, &actualWidth);
01114 //      TRACE(HtmlTrace_GetLine,
01115 //         ("GetLine page=%d left=%d right=%d available=%d used=%d\n",
01116 //         fPageWidth, fLeft, fRight, lineWidth, actualWidth));
01117          FixAnchors(p, pNext, fBottom);
01118 
01119          // Move down and repeat the layout if we exceeded the available
01120          // line length and it is possible to increase the line length by
01121          // moving past some obstacle.
01122 
01123          if (actualWidth > lineWidth && InWrapAround()) {
01124             ClearObstacle(CLEAR_First);
01125             continue;
01126          }
01127 
01128          // Lock the line into place and exit the loop
01129          y = FixLine(p, pNext, y, lineWidth, actualWidth, lMargin, &max_x);
01130          break;
01131       }
01132 
01133 #ifdef TABLE_TRIM_BLANK
01134 
01135       // I noticed that a newline following break markup would result
01136       // in a blank line being drawn. So if an "empty" line was found
01137       // I subtract any whitespace caused by break markup.
01138 
01139       if (actualWidth <= 0) HtmlLineWasBlank = 1;
01140 
01141 #endif // TABLE_TRIM_BLANK
01142 
01143       // If a line was completed, advance to the next line
01144       if (pNext && actualWidth > 0 && y > fBottom) {
01145          PopIndent();
01146          fBottom = y;
01147          fPStart = pNext;
01148       }
01149       if (y > fMaxY) fMaxY = y;
01150       if (max_x > fMaxX) fMaxX = max_x;
01151    }
01152 }
01153 
01154 //______________________________________________________________________________
01155 void TGHtmlLayoutContext::PushIndent()
01156 {
01157    // Adjust (push) ident.
01158 
01159    fHeadRoom += fHtml->GetMarginHeight();
01160    if (fHtml->GetMarginWidth()) {
01161       PushMargin(&fLeftMargin, fHtml->GetMarginWidth(), -1, Html_EndBLOCKQUOTE);
01162       PushMargin(&fRightMargin, fHtml->GetMarginWidth(), -1, Html_EndBLOCKQUOTE);
01163    }
01164 }
01165 
01166 //______________________________________________________________________________
01167 void TGHtmlLayoutContext::PopIndent()
01168 {
01169    // Adjust (pop) ident.
01170 
01171    if (fHeadRoom <= 0) return;
01172    fHeadRoom = 0;
01173    PopMargin(&fRightMargin, Html_EndBLOCKQUOTE);
01174 }
01175 
01176 //______________________________________________________________________________
01177 void TGHtml::LayoutDoc()
01178 {
01179    // Advance the layout as far as possible
01180 
01181    int btm;
01182 
01183    if (fPFirst == 0) return;
01184    Sizer();
01185    fLayoutContext.fHtml = this;
01186 #if 0  // orig
01187    fLayoutContext.PushIndent();
01188    fLayoutContext.fPageWidth = fCanvas->GetWidth();
01189    fLayoutContext.fLeft = 0;
01190 #else
01191    fLayoutContext.fHeadRoom = HTML_INDENT/4;
01192    fLayoutContext.fPageWidth = fCanvas->GetWidth() - HTML_INDENT/4;
01193    fLayoutContext.fLeft = HTML_INDENT/4;
01194 #endif
01195    fLayoutContext.fRight = 0;
01196    fLayoutContext.fPStart = fNextPlaced;
01197    if (fLayoutContext.fPStart == 0) fLayoutContext.fPStart = fPFirst;
01198    if (fLayoutContext.fPStart) {
01199       TGHtmlElement *p;
01200 
01201       fLayoutContext.fMaxX = fMaxX;
01202       fLayoutContext.fMaxY = fMaxY;
01203       btm = fLayoutContext.fBottom;
01204       fLayoutContext.LayoutBlock();
01205       fMaxX = fLayoutContext.fMaxX;
01206 #if 0
01207       fMaxY = fLayoutContext.fMaxY;
01208 #else
01209       fMaxY = fLayoutContext.fMaxY + fYMargin;
01210 #endif
01211       fNextPlaced = fLayoutContext.fPStart;
01212       fFlags |= HSCROLL | VSCROLL;
01213       if (fZGoto && (p = AttrElem("name", fZGoto+1))) {
01214          fVisible.fY = ((TGHtmlAnchor *)p)->fY;
01215          delete[] fZGoto;
01216          fZGoto = 0;
01217       }
01218       RedrawText(btm);
01219    }
01220 }

Generated on Tue Jul 5 14:22:19 2011 for ROOT_528-00b_version by  doxygen 1.5.1