#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "TGHtml.h"
#include "THashTable.h"
#include "TObjString.h"
#include "TGIdleHandler.h"
#include "TImage.h"
#include "TGScrollBar.h"
ClassImp(TGHtml)
int HtmlTraceMask = 0; 
int HtmlDepth = 0;
#define DEF_FRAME_BG_COLOR        "#c0c0c0"
#define DEF_FRAME_CURSOR          ""
#define DEF_BUTTON_FG             "black"
#define DEF_BUTTON_HIGHLIGHT_BG   "#d9d9d9"
#define DEF_BUTTON_HIGHLIGHT      "black"
TGHtml::TGHtml(const TGWindow *p, int w, int h, int id) : TGView(p, w, h, id)
{
   
   int i;
   _exiting = 0;
   pFirst = 0;
   pLast = 0;
   nToken = 0;
   lastSized = 0;
   nextPlaced = 0;
   firstBlock = 0;
   lastBlock = 0;
   firstInput = 0;
   lastInput = 0;
   nInput = 0;
   nForm = 0;
   varId = 0;  
   inputIdx = 0;
   radioIdx = 0;
   selBegin.p = 0;
   selEnd.p = 0;
   pSelStartBlock = 0;
   pSelEndBlock = 0;
   insOnTime = DEF_HTML_INSERT_ON_TIME;
   insOffTime = DEF_HTML_INSERT_OFF_TIME;
   insStatus = 0;
   insTimer = 0;
   ins.p = 0;
   pInsBlock = 0;
   insIndex = 0;
   zText = 0;
   nText = 0;
   nAlloc = 0;
   nComplete = 0;
   iCol = 0;
   iPlaintext = 0;
   pScript = 0;
   fIdle = 0;
   styleStack = 0;
   paraAlignment = ALIGN_None;
   rowAlignment = ALIGN_None;
   anchorFlags = 0;
   inDt = 0;
   inTr = 0;
   inTd = 0;
   anchorStart = 0;
   formStart = 0;
   formElemStart = 0;
   formElemLast = 0;
   loEndPtr = 0;
   loFormStart = 0;
   innerList = 0;
   ResetLayoutContext();
   highlightWidth = 0;
   highlightBgColorPtr = 0;
   highlightColorPtr = 0;
   for (i = 0; i < N_FONT; ++i) aFont[i] = 0;
   memset(fontValid, 0, sizeof(fontValid));
   for (i = 0; i < N_COLOR; ++i) {
      apColor[i] = 0;
      iDark[i] = 0;
      iLight[i] = 0;
   }
   fgColor = AllocColor("black");
   bgColor = AllocColor("white"); 
   newLinkColor = AllocColor(DEF_HTML_UNVISITED);
   oldLinkColor = AllocColor(DEF_HTML_VISITED);
   selectionColor = AllocColor(DEF_HTML_SELECTION_COLOR);
   apColor[COLOR_Normal] = fgColor;
   apColor[COLOR_Visited] = oldLinkColor;
   apColor[COLOR_Unvisited] = newLinkColor;
   apColor[COLOR_Selection] = selectionColor;
   apColor[COLOR_Background] = bgColor;
   bgImage = 0;
   SetBackgroundColor(apColor[COLOR_Background]->fPixel);
   SetBackgroundPixmap(0);  
   colorUsed = 0;
   for (i = 0; i < N_CACHE_GC; ++i) aGcCache[i].index = 0;
   GcNextToFree = 0;
   imageList = 0;
   zBaseHref = 0;
   innerList = 0;
   formPadding = 5;
   overrideFonts = 0;
   overrideColors = 0;
   HasScript = 0;
   HasFrames = 0;
   AddEndTags = 0;
   TableBorderMin = 0;
   varind = 0;
   idind = 0;
   inParse = 0;
   zGoto = 0;
   exts = 0;
   underlineLinks = kTRUE;
   exportSelection = DEF_HTML_EXPORT_SEL;
   tableRelief = HTML_RELIEF_RAISED;
   ruleRelief = HTML_RELIEF_SUNKEN;
   rulePadding = 5;
   zBase = 0;
   zBaseHref = 0;
   cursor = kPointer;
   maxX = 0;
   maxY = 0;
   fXMargin = fYMargin = 0; 
   flags = RESIZE_ELEMENTS | RELAYOUT;
   dirtyLeft = LARGE_NUMBER;
   dirtyRight = 0;
   dirtyTop = LARGE_NUMBER;
   dirtyBottom = 0;
   fVsb->SetAccelerated();
   fHsb->SetAccelerated();
   _lastUri = 0;
   AddInput(kExposureMask | kFocusChangeMask);
   AddInput(kButtonPressMask | kButtonReleaseMask | kPointerMotionMask);
   fUidTable = new THashTable(100);
}
TGHtml::~TGHtml()
{
   
   int i;
   _exiting = 1;
   _Clear();
   for (i = 0; i < N_FONT; i++) {
      if (aFont[i] != 0) fClient->FreeFont(aFont[i]);
   }
   if (insTimer) delete insTimer;
   if (fIdle) delete fIdle;
  
}
void TGHtml::UpdateBackgroundStart()
{
   
   
   
   
   
   
}
void TGHtml::FreeColor(ColorStruct_t *color)
{
   
  
   gVirtualX->FreeColor(gClient->GetDefaultColormap(), color->fPixel);
   delete color;
}
ColorStruct_t *TGHtml::AllocColor(const char *name)
{
   
   ColorStruct_t *color = new ColorStruct_t;
   color->fPixel = 0;
   if (gVirtualX->ParseColor(fClient->GetDefaultColormap(), name, *color)) {
      if (!gVirtualX->AllocColor(fClient->GetDefaultColormap(), *color)) {
         
         gVirtualX->QueryColor(fClient->GetDefaultColormap(), *color);
         gVirtualX->AllocColor(fClient->GetDefaultColormap(), *color);
      }
   }
   return color;
}
ColorStruct_t *TGHtml::AllocColorByValue(ColorStruct_t *color)
{
   
   ColorStruct_t *c = new ColorStruct_t;
   *c = *color;
   if (!gVirtualX->AllocColor(gClient->GetDefaultColormap(), *c)) {
      
      c->fPixel = 0;
      gVirtualX->QueryColor(gClient->GetDefaultColormap(), *c);
      gVirtualX->AllocColor(gClient->GetDefaultColormap(), *c);
   }
   return c;
}
void TGHtml::Clear(Option_t *)
{
   
   
   _Clear();
   TGView::Clear();
   flags |= REDRAW_TEXT | VSCROLL | HSCROLL;
   ScheduleRedraw();
}
int TGHtml::ParseText(char *text, const char *index)
{
   
   
   
   
   
   SHtmlIndex iStart;
   TGHtmlElement *savePtr=0;
   iStart.p = 0;
   iStart.i = 0;
   loEndPtr = pLast;
   if (index) {
      int rc = GetIndex(index, &iStart.p, &iStart.i);
      if (rc != 0) return kFALSE;  
      if (iStart.p) {
         savePtr = iStart.p->pNext;
         pLast = iStart.p;
         iStart.p->pNext = 0;
      }
   }
   TokenizerAppend(text);
   if (loEndPtr) {
      formStart = loFormStart;
      if (iStart.p && savePtr) {
         AddStyle(loEndPtr);
         pLast->pNext = savePtr;
         savePtr->pPrev = pLast;
         pLast = loEndPtr;
         flags |= REDRAW_TEXT | RELAYOUT;
         ScheduleRedraw();
      } else if (loEndPtr->pNext) {
         AddStyle(loEndPtr->pNext);
      }
   } else if (pFirst) {
      paraAlignment = ALIGN_None;
      rowAlignment = ALIGN_None; 
      anchorFlags = 0;
      inDt = 0;
      anchorStart = 0;
      formStart = 0;  
      innerList = 0;  
      nInput = 0;
      AddStyle(pFirst);
   }
#if 1
   loEndPtr = pLast;
   loFormStart = formStart;
#endif
   flags |= EXTEND_LAYOUT;
   ScheduleRedraw();
   return kTRUE;
}
void TGHtml::SetTableRelief(int relief)
{
   
   if (tableRelief != relief) {
      tableRelief = relief;
      flags |= RELAYOUT;
      RedrawEverything();
   }
}
void TGHtml::SetRuleRelief(int relief)
{
   
   if (ruleRelief != relief) {
      ruleRelief = relief;
      flags |= RELAYOUT;
      RedrawEverything();
   }
}
void TGHtml::UnderlineLinks(int onoff)
{
   
   if (underlineLinks != onoff) {
      underlineLinks = onoff;
      TGHtmlElement *p;
      SHtmlStyle style = GetCurrentStyle();
      for (p = pFirst; p; p = p->pNext) {
         if (p->type == Html_A) {
            if (anchorStart) {
               style = PopStyleStack(Html_EndA);
               anchorStart = 0;
               anchorFlags = 0;
            }
            char *z = p->MarkupArg("href", 0);
            if (z) {
               style.color = GetLinkColor(z);
               if (underlineLinks) style.flags |= STY_Underline;
               anchorFlags |= STY_Anchor;
               PushStyleStack(Html_EndA, style);
               anchorStart = (TGHtmlAnchor *) p;
            }
         } else if (p->type == Html_EndA) {
            if (anchorStart) {
               ((TGHtmlRef *)p)->pOther = anchorStart;
               style = PopStyleStack(Html_EndA);
               anchorStart = 0;
               anchorFlags = 0;
            }
         }
         p->style.flags &= ~STY_Underline;
         p->style.flags |= (style.flags & STY_Underline);
      }
      RedrawEverything();
   }
}
void TGHtml::SetBaseUri(const char *uri)
{
   
   if (zBase) delete[] zBase;
   zBase = 0;
   if (uri) zBase = StrDup(uri);
}
int TGHtml::GotoAnchor(const char *name)
{
   
   char *z;
   TGHtmlElement *p;
   for (p = pFirst; p; p = p->pNext) {
      if (p->type == Html_A) {
         z = p->MarkupArg("name", 0);
         if (z && strcmp(z, name) == 0) {
            ScrollToPosition(TGLongPosition(fVisible.fX, ((TGHtmlAnchor *)p)->y));
            return kTRUE;
         }
      }
   }
   return kFALSE;
}
const char *TGHtml::GetUid(const char *string)
{
   
   
   
   
   
   
   
   
   
   
   TObjString *obj = 0;
   obj = (TObjString*)fUidTable->FindObject(string);
   if (!obj) {
      obj = new TObjString(string);
      fUidTable->Add(obj);
   }
   return (const char *)obj->GetName();
}
void TGHtml::ComputeVirtualSize()
{
   
   fVirtualSize = TGDimension(maxX, maxY);
}
void TGHtml::ClearGcCache()
{
   
   int i;
   for (i = 0; i < N_CACHE_GC; i++) {
      if (aGcCache[i].index) {
         gVirtualX->DeleteGC(aGcCache[i].gc);
         aGcCache[i].index = 0;
      }
   }
   GcNextToFree = 0;
}
void TGHtml::ResetLayoutContext()
{
   
   
   layoutContext.Reset();
}
void TGHtml::Redraw()
{
   
   
   
   
   Pixmap_t pixmap;           
   int x, y, w, h;          
   int hw;                  
   int clipwinH, clipwinW;  
   TGHtmlBlock *pBlock;      
   int redoSelection = 0;   
  
   
   if (inParse) {
      flags &= ~REDRAW_PENDING;
      return;
   }
   
   
   
   
   
   
   
   
   if ((flags & RESIZE_ELEMENTS) != 0 && (flags & STYLER_RUNNING) == 0) {
      TGHtmlImage *pImage;
      for (pImage = imageList; pImage; pImage = pImage->pNext) {
         pImage->pList = 0;
      }
      lastSized = 0;
      flags &= ~RESIZE_ELEMENTS;
      flags |= RELAYOUT;
   }
   
   
   
   
   
   
   
   
   
   if ((flags & (RELAYOUT | EXTEND_LAYOUT)) != 0 
      && (flags & STYLER_RUNNING) == 0) {
      nextPlaced = 0;
      
      varId = 0;
      maxX = 0;
      maxY = 0;
      ResetLayoutContext();
      firstBlock = 0;
      lastBlock = 0;
      redoSelection = 1;
      flags &= ~RELAYOUT;
      flags |= HSCROLL | VSCROLL | REDRAW_TEXT | EXTEND_LAYOUT;
   }
   if ((flags & EXTEND_LAYOUT) && pFirst != 0) {
      LayoutDoc();
      flags &= ~EXTEND_LAYOUT;
      FormBlocks();
      MapControls();
      if (redoSelection && selBegin.p && selEnd.p) {
         UpdateSelection(1);
         UpdateInsert();
      }
   }
   flags &= ~REDRAW_PENDING;
   
   
   if ((flags & (HSCROLL | VSCROLL)) != 0) {
      ComputeVirtualSize();
      flags &= ~(HSCROLL | VSCROLL);
      if (flags & REDRAW_PENDING) return;
   }
   
   hw = highlightWidth;
   if (flags & REDRAW_FOCUS) {
      if (hw > 0) {
#if 0
      unsigned long color;
      if (flags & GOT_FOCUS) {
        color = highlightColorPtr;
      } else {
        color = highlightBgColorPtr;
      }
      _DrawFocusHighlight(color);
#endif
      }
      flags &= ~REDRAW_FOCUS;
   }
   
   
   if (flags & STYLER_RUNNING) {
      goto earlyOut;
   }
   MapControls();
   
   
   clipwinW = fCanvas->GetWidth();
   clipwinH = fCanvas->GetHeight();
   if (flags & REDRAW_TEXT) {
      w = clipwinW;
      h = clipwinH;
      x = fVisible.fX;
      y = fVisible.fY;
      dirtyLeft = 0;
      dirtyTop = 0;
      flags &= ~REDRAW_TEXT;
   } else {
      if (dirtyLeft < 0) dirtyLeft = 0;
      if (dirtyRight > clipwinW) dirtyRight = clipwinW;
      if (dirtyTop < 0) dirtyTop = 0;
      if (dirtyBottom > clipwinH) dirtyBottom = clipwinH;
      w = dirtyRight - dirtyLeft;
      h = dirtyBottom - dirtyTop;
      x = fVisible.fX + dirtyLeft;
      y = fVisible.fY + dirtyTop;
   }
   
   
   if (w > 0 && h > 0) {
      GContext_t gcBg;
      TGRectangle xrec;
      
      
      gcBg = GetGC(COLOR_Background, FONT_Any);
      pixmap = gVirtualX->CreatePixmap(fCanvas->GetId(), w, h);
      xrec.fX = 0;
      xrec.fY = 0;
      xrec.fW = w;
      xrec.fH = h;
#if 0
#else
      fWhiteGC.SetTileStipXOrigin(-fVisible.fX - dirtyLeft);
      fWhiteGC.SetTileStipYOrigin(-fVisible.fY - dirtyTop);
      gVirtualX->FillRectangle(pixmap, fWhiteGC.GetGC(), 0, 0, w, h);
      UpdateBackgroundStart();  
#endif
                       
      
      for (pBlock = firstBlock; pBlock; pBlock = pBlock->bNext) {
         if (pBlock->top <= y+h && pBlock->bottom >= y-10 &&
            pBlock->left <= x+w && pBlock->right >= x-10) {
            BlockDraw(pBlock, pixmap, x, y, w, h, pixmap);
         }
      }
     
      
      gVirtualX->CopyArea(pixmap, fCanvas->GetId(),
                          gcBg, 0, 0, w, h, dirtyLeft, dirtyTop);
      gVirtualX->Update(kFALSE);
      gVirtualX->DeletePixmap(pixmap);
   }
   
   if (flags & REDRAW_IMAGES) {
      TGHtmlImage *pImage;
      TGHtmlImageMarkup *pElem;
      int top, bottom, left, right;     
      int imageTop;                     
      top = fVisible.fY;
      bottom = top + fCanvas->GetHeight();
      left = fVisible.fX;
      right = left + fCanvas->GetWidth();
      for (pImage = imageList; pImage; pImage = pImage->pNext) {
         for (pElem = pImage->pList; pElem; pElem = pElem->iNext) {
            if (pElem->redrawNeeded == 0) continue;
            imageTop = pElem->y - pElem->ascent;
            if (imageTop > bottom || imageTop + pElem->h < top
               || pElem->x > right || pElem->x + pElem->w < left) continue; 
            DrawImage(pElem, fCanvas->GetId(), left, top, right, bottom);
         }
      }
      flags &= ~(REDRAW_IMAGES | ANIMATE_IMAGES);
   }
   
earlyOut:
   dirtyTop = LARGE_NUMBER;
   dirtyLeft = LARGE_NUMBER;
   dirtyBottom = 0;
   dirtyRight = 0;
   return;
}
void TGHtml::ScheduleRedraw()
{
   
   if ((flags & REDRAW_PENDING) == 0 ) {
      if (!fIdle) fIdle = new TGIdleHandler(this);
      flags |= REDRAW_PENDING;
   }
}
Bool_t TGHtml::HandleIdleEvent(TGIdleHandler *idle) 
{
   
   if (idle != fIdle) return kFALSE;
   Redraw();
   delete fIdle;
   fIdle = NULL;
   return kTRUE;
}
void TGHtml::RedrawArea(int left, int top, int right, int bottom)
{
   
   
   
   
   
   if (bottom < 0) return;
   if (top > (int)fCanvas->GetHeight()) return;
   if (right < 0) return;
   if (left > (int)fCanvas->GetWidth()) return;
   if (dirtyTop > top) dirtyTop = top;
   if (dirtyLeft > left) dirtyLeft = left;
   if (dirtyBottom < bottom) dirtyBottom = bottom;
   if (dirtyRight < right) dirtyRight = right;
   ScheduleRedraw();
}
void TGHtml::DrawRegion(Int_t x, Int_t y, UInt_t w, UInt_t h) 
{
   
   TGView::DrawRegion(x, y, w, h);
#if 0
  RedrawArea(x, y, x + w + 1, y + h + 1);
#else
   int left = x;
   int top = y;
   int right = x + w + 1;
   int bottom = y + h + 1;
   if (bottom < 0) return;
   if (top > (int) fCanvas->GetHeight()) return;
   if (right < 0) return;
   if (left > (int)fCanvas->GetWidth()) return;
   if (dirtyTop > top) dirtyTop = top;
   if (dirtyLeft > left) dirtyLeft = left;
   if (dirtyBottom < bottom) dirtyBottom = bottom;
   if (dirtyRight < right) dirtyRight = right;
   flags |= REDRAW_PENDING;
   Redraw();
#endif
   return;
}
Bool_t TGHtml::ItemLayout()
{
   
#if 0
  flags |= RELAYOUT | VSCROLL | HSCROLL;
  Redraw(); 
#else
   nextPlaced = 0;
   
   varId = 0;
   maxX = 0;
   maxY = 0;
   ResetLayoutContext();
   firstBlock = 0;
   lastBlock = 0;
   if (pFirst != 0) {
      LayoutDoc();
      FormBlocks();
      MapControls();
      if (selBegin.p && selEnd.p) {
         UpdateSelection(1);
         UpdateInsert();
      }
   }
   ComputeVirtualSize();
   ScheduleRedraw();
#endif
   return kTRUE;
}
void TGHtml::RedrawBlock(TGHtmlBlock *p)
{
   
   if (p) {
      RedrawArea(p->left - fVisible.fX, p->top - fVisible.fY,
                 p->right - fVisible.fX + 1, p->bottom - fVisible.fY);
   }
}
void TGHtml::RedrawEverything()
{
   
   flags |= REDRAW_FOCUS | REDRAW_TEXT;
   ScheduleRedraw();
}
void TGHtml::RedrawText(int y)
{
   
   
   int clipHeight;     
  
   clipHeight = fCanvas->GetHeight();
   y -= fVisible.fY;
   if (y < clipHeight) {
      RedrawArea(0, y, LARGE_NUMBER, clipHeight);
   }
}
void TGHtml::_Clear()
{
   
   int i;
   TGHtmlElement *p, *pNext;
   fXMargin = fYMargin = 0; 
   DeleteControls();
   for (p = pFirst; p; p = pNext) {
      pNext = p->pNext;
      delete p;
   }
   pFirst = 0;
   pLast = 0;
   nToken = 0;
   if (zText) delete[] zText;
   zText = 0;
   nText = 0;
   nAlloc = 0;
   nComplete = 0;
   iPlaintext = 0;
   for (i = 0; i < N_COLOR; ++i) {
      if (apColor[i] != 0) FreeColor(apColor[i]);
      apColor[i] = 0;
      iDark[i] = 0;
      iLight[i] = 0;
   }
   if (!_exiting) {
      fgColor = AllocColor("black");
      bgColor = AllocColor("white"); 
      newLinkColor = AllocColor(DEF_HTML_UNVISITED);
      oldLinkColor = AllocColor(DEF_HTML_VISITED);
      selectionColor = AllocColor(DEF_HTML_SELECTION_COLOR);
      apColor[COLOR_Normal] = fgColor;
      apColor[COLOR_Visited] = oldLinkColor;
      apColor[COLOR_Unvisited] = newLinkColor;
      apColor[COLOR_Selection] = selectionColor;
      apColor[COLOR_Background] = bgColor;
      SetBackgroundColor(apColor[COLOR_Background]->fPixel);
      SetBackgroundPixmap(0);  
   }
   colorUsed = 0;
   while (imageList) {
      TGHtmlImage *p = imageList;
      imageList = p->pNext;
      delete p;
   }
   if (bgImage) delete bgImage;
   bgImage = 0;
   while (styleStack) {
      SHtmlStyleStack *p = styleStack;
      styleStack = p->pNext;
      delete p;
   }
   ClearGcCache();
   ResetLayoutContext();
   if (zBaseHref) delete [] zBaseHref;
   zBaseHref = 0;
   lastSized = 0;
   nextPlaced = 0;
   firstBlock = 0;
   lastBlock = 0;
   nInput = 0;
   nForm = 0;
   varId = 0;
   paraAlignment = ALIGN_None;
   rowAlignment = ALIGN_None;
   anchorFlags = 0;
   inDt = 0;
   anchorStart = 0;
   formStart = 0;
   innerList = 0;
   maxX = 0;
   maxY = 0;
#if 0  // in OXView::Clear()
  fVisible = TGPosition(0, 0);
  _virtualSize = TGDimension(0, 0);
  ScrollTTGPosition(fVisible);
#endif
   pInsBlock = 0;
   ins.p = 0;
   selBegin.p = 0;
   selEnd.p = 0;
   pSelStartBlock = 0;
   pSelEndBlock = 0;
   HasScript = 0;
   HasFrames = 0;
   _lastUri = 0;
}
Bool_t TGHtml::HandleTimer(TTimer *t)
{
   if (t == insTimer) {
      if (insTimer) delete insTimer;
      insTimer = NULL;
      FlashCursor();
      return kTRUE;
   } else {
      TGHtmlImage *pImage;
      for (pImage = imageList; pImage; pImage = pImage->pNext) {
         if (pImage->timer == t) {
            AnimateImage(pImage);
            return kTRUE;
         }
      }
   }
   return kFALSE;
}
void TGHtml::FlashCursor()
{
   
   if (pInsBlock == 0 || insOnTime <= 0 || insOffTime <= 0) return;
   RedrawBlock(pInsBlock);
   if ((flags & GOT_FOCUS) == 0) {
      insStatus = 0;
   } else if (insStatus) {
      insTimer = new TTimer(this, insOffTime);
      insStatus = 0;
   } else {
      insTimer = new TTimer(this, insOnTime);
      insStatus = 1;
   }
}
GContext_t TGHtml::GetGC(int color, int font)
{
   
   
   
   
   int i, j;
   GcCache *p = aGcCache;
   GCValues_t gcValues;
   TGFont *xfont;
   
   if (color < 0 || color >= N_COLOR) color = 0;
   if (font < FONT_Any || font >= N_FONT) font = FONT_Default;
   for (i = 0; i < N_CACHE_GC; i++, p++) {
      if (p->index == 0) continue;
      if ((font < 0 || p->font == font) && p->color == color) {
         if (p->index > 1) {
            for (j = 0; j < N_CACHE_GC; j++) {
               if (aGcCache[j].index && aGcCache[j].index < p->index ) {
                  aGcCache[j].index++;
               }
            }
            p->index = 1;
         }
         return aGcCache[i].gc;
      }
   }
   
   p = aGcCache;
   for (i = 0; i < N_CACHE_GC; i++, p++) {
      if (p->index == 0 || p->index == N_CACHE_GC) break;
   }
   if (i >= N_CACHE_GC) {  
      p = aGcCache;
      for (i = 0; i < N_CACHE_GC && i < GcNextToFree; ++i, ++p) {}
         GcNextToFree = (GcNextToFree + 1) % N_CACHE_GC;
         gVirtualX->DeleteGC(p->gc);
   }
   gcValues.fForeground = apColor[color]->fPixel;
   gcValues.fGraphicsExposures = kTRUE;
   gcValues.fMask = kGCForeground | kGCGraphicsExposures;
   if (font < 0) font = FONT_Default;
   xfont = GetFont(font);
   if (xfont) {
      gcValues.fFont = xfont->GetFontHandle();
      gcValues.fMask |= kGCFont;
   }
   p->gc = gVirtualX->CreateGC(fId, &gcValues);
   if (p->index == 0) p->index = N_CACHE_GC + 1;
   for (j = 0; j < N_CACHE_GC; j++) {
      if (aGcCache[j].index && aGcCache[j].index < p->index) {
         aGcCache[j].index++;
      }
   }
   p->index = 1;
   p->font = font;
   p->color = color;
   return p->gc;
}
GContext_t TGHtml::GetAnyGC()
{
   
   
   int i;
   GcCache *p = aGcCache;
   for (i = 0; i < N_CACHE_GC; i++, p++) {
      if (p->index) return p->gc;
   }
   return GetGC(COLOR_Normal, FONT_Default);
}
Bool_t TGHtml::HandleFocusChange(Event_t *event)
{
   
   if (event->fType == kFocusIn) {
      flags |= GOT_FOCUS | REDRAW_FOCUS;
      ScheduleRedraw();
      UpdateInsert();
   } else {  
      flags &= ~GOT_FOCUS;
      flags |= REDRAW_FOCUS;
      ScheduleRedraw();
  }
  return kTRUE;
}
Bool_t TGHtml::HandleButton(Event_t *event)
{
   
   int amount, ch;
   ch = fCanvas->GetHeight();
   amount = fScrollVal.fY * TMath::Max(ch/6, 1);
   if ((event->fType == kButtonPress) && (event->fCode == kButton1)) {
      int x = event->fX + fVisible.fX;
      int y = event->fY + fVisible.fY;
      char *uri = GetHref(x, y);
#if 0  // insertion cursor test
    char ix[20];
    sprintf(ix, "begin");
    SetInsert(ix);
#endif
      if (uri) {
         uri = ResolveUri(uri);
         if (uri) {
            MouseDown(uri);
            
         }
      }
   } else if (event->fCode == kButton4) {
      ScrollToPosition(TGLongPosition(fVisible.fX, fVisible.fY / fScrollVal.fY - amount));
   } else if (event->fCode == kButton5) {
      ScrollToPosition(TGLongPosition(fVisible.fX, fVisible.fY / fScrollVal.fY + amount));
   } else {
      return TGView::HandleButton(event);
  }
  return kTRUE;
}
Bool_t TGHtml::HandleMotion(Event_t *event)
{
   
   int x = event->fX + fVisible.fX;
   int y = event->fY + fVisible.fY;
   char *uri = GetHref(x, y);
   if (uri) {
      gVirtualX->SetCursor(fId, gVirtualX->CreateCursor(kHand));
   } else {
      gVirtualX->SetCursor(fId, gVirtualX->CreateCursor(kPointer));
   }
   if (uri != _lastUri) {
      _lastUri = uri;
      if (uri) uri = ResolveUri(uri);
      MouseOver(uri);
      
   }
   return kTRUE;
}
TGFont *TGHtml::GetFont(int iFont)
{
   
   
   
   TGFont *toFree = 0;
   if (iFont < 0) iFont = 0;
   if (iFont >= N_FONT) { iFont = N_FONT - 1; CANT_HAPPEN; }
   
   
   
   
   if (!FontIsValid(iFont) && aFont[iFont] != 0) {
      toFree = aFont[iFont];
      aFont[iFont] = 0;
   }
   
   
   if (aFont[iFont] == 0) {
      char name[200];         
      char *familyStr = "";
      int iFamily;
      int iSize;
      int size;
      iFamily = FontFamily(iFont) >> 3;
      iSize = FontSize(iFont) + 1;
      switch (iFamily) {
#ifdef TIMES
         case 0:  familyStr = "times -%d";                 break;
         case 1:  familyStr = "times -%d bold";            break;
         case 2:  familyStr = "times -%d italic";          break;
         case 3:  familyStr = "times -%d bold italic";     break;
         case 4:  familyStr = "courier -%d";               break;
         case 5:  familyStr = "courier -%d bold";          break;
         case 6:  familyStr = "courier -%d italic";        break;
         case 7:  familyStr = "courier -%d bold italic";   break;
         default: familyStr = "times -16";                 CANT_HAPPEN;
#else
         case 0:  familyStr = "helvetica -%d";             break;
         case 1:  familyStr = "helvetica -%d bold";        break;
         case 2:  familyStr = "helvetica -%d italic";      break;
         case 3:  familyStr = "helvetica -%d bold italic"; break;
         case 4:  familyStr = "courier -%d";               break;
         case 5:  familyStr = "courier -%d bold";          break;
         case 6:  familyStr = "courier -%d italic";        break;
         case 7:  familyStr = "courier -%d bold italic";   break;
         default: familyStr = "helvetica -14";             CANT_HAPPEN;
#endif
      }
#if 0
    switch (iSize) {
      case 1:  size = 6+finc;   break;
      case 2:  size = 10+finc;  break;
      case 3:  size = 12+finc;  break;
      case 4:  size = 14+finc;  break;
      case 5:  size = 20+finc;  break;
      case 6:  size = 24+finc;  break;
      case 7:  size = 30+finc;  break;
      default: size = 14+finc;  CANT_HAPPEN;
    }
#else
      switch (iSize) {
         case 1:  size = 8;   break;
         case 2:  size = 10;  break;
         case 3:  size = 12;  break;
         case 4:  size = 14;  break;
         case 5:  size = 16;  break;
         case 6:  size = 18;  break;
         case 7:  size = 24;  break;
         default: size = 14;  CANT_HAPPEN;
      }
#endif
#ifdef TIMES
      if (iFamily < 4) size += 2;
#endif
      sprintf(name, familyStr, size);
      
      aFont[iFont] = fClient->GetFont(name);\
      if (aFont[iFont] == 0) {
         fprintf(stderr, "TGHtml: could not get font \"%s\", trying fixed\n",
                      name);
         aFont[iFont] = fClient->GetFont("fixed");
      }
      if (aFont[iFont]==0 ){
         fprintf(stderr, "TGHtml: could not get font \"fixed\", trying "
                      "\"helvetica -12\"\n");
         aFont[iFont] = fClient->GetFont("helvetica -12");
      }
      FontSetValid(iFont);
   }
   
   if (toFree) fClient->FreeFont(toFree);
   return aFont[iFont];
}
int TGHtml::InArea(TGHtmlMapArea *p, int left, int top, int x, int y)
{
   
   int *ip = p->coords;
   if (!ip) return 0;
   if (p->mType == HTML_MAP_RECT) {
      return ((left + ip[0]) <= x && (left + ip[2]) >= x &&
               (top  + ip[1]) <= y && (top  + ip[3]) >= y);
   } else if (p->mType == HTML_MAP_CIRCLE) {
      int dx = left + ip[0] - x;
      int dy = top + ip[1] - y;
      return (dx * dx + dy * dy <= ip[2] * ip[2]);
   }
   return 0;
}
TGHtmlElement *TGHtml::GetMap(char *name)
{
   
   TGHtmlElement *p = pFirst;
   char *z, *zb;
   while (p) {
      if (p->type == Html_MAP) {
         z = p->MarkupArg("name", 0);
         zb = p->MarkupArg("shape", 0);
         if (zb && *zb != 'r') return 0;
         if (z && !strcmp(z, name)) return p;
      }
      p = p->pNext;
  }
  return 0;
}
float TGHtml::colorDistance(ColorStruct_t *pA, ColorStruct_t *pB)
{
   
   float x, y, z;
   x = 0.30 * (pA->fRed - pB->fRed);
   y = 0.61 * (pA->fGreen - pB->fGreen);
   z = 0.11 * (pA->fBlue - pB->fBlue);
   return x*x + y*y + z*z;
}
int TGHtml::GetColorByName(char *zColor)
{
   
   
   
   ColorStruct_t *pNew;
   int iColor;
   const char *name;  
   int i, n;
   char zAltColor[16];
   
   
   
   n = strlen(zColor);
   if (n == 6 || n == 3 || n == 9 || n == 12) {
      for (i = 0; i < n; i++) {
         if (!isxdigit(zColor[i])) break;
      }
      if (i == n) {
         sprintf(zAltColor, "#%s", zColor);
      } else {
         strcpy(zAltColor, zColor);
      }
      name = GetUid(zAltColor);
   } else {
      name = GetUid(zColor);
   }
   pNew = AllocColor(name);
   if (pNew == 0) {
      return 0;      
   }
   iColor = GetColorByValue(pNew);
   FreeColor(pNew);
   return iColor;
}
#define MAX_COLOR    65535
#define MAX(A,B)     ((A)<(B)?(B):(A))
#define MIN(A,B)     ((A)<(B)?(A):(B))
int TGHtml::isDarkColor(ColorStruct_t *p)
{
   
   
   float x, y, z;
   x = 0.50 * p->fRed;
   y = 1.00 * p->fGreen;
   z = 0.28 * p->fBlue;
   return (x*x + y*y + z*z) < (0.05 * MAX_COLOR * MAX_COLOR);
}
int TGHtml::GetDarkShadowColor(int iBgColor)
{
   
   
   if (iDark[iBgColor] == 0) {
      ColorStruct_t *pRef, val;
      pRef = apColor[iBgColor];
      if (isDarkColor(pRef)) {
         int t1, t2;
         t1 = (int) MIN(MAX_COLOR, pRef->fRed * 1.2);
         t2 = (pRef->fRed * 3 + MAX_COLOR) / 4;
         val.fRed = MAX(t1, t2);
         t1 = (int) MIN(MAX_COLOR, pRef->fGreen * 1.2);
         t2 = (pRef->fGreen * 3 + MAX_COLOR) / 4;
         val.fGreen = MAX(t1, t2);
         t1 = (int) MIN(MAX_COLOR, pRef->fBlue * 1.2);
         t2 = (pRef->fBlue * 3 + MAX_COLOR) / 4;
         val.fBlue = MAX(t1, t2);
      } else {
         val.fRed = (unsigned short) (pRef->fRed * 0.6);
         val.fGreen = (unsigned short) (pRef->fGreen * 0.6);
         val.fBlue = (unsigned short) (pRef->fBlue * 0.6);
      }
      iDark[iBgColor] = GetColorByValue(&val) + 1;
   }
   return iDark[iBgColor] - 1;
}
int TGHtml::isLightColor(ColorStruct_t *p)
{
   
   
   return p->fGreen >= 0.85 * MAX_COLOR;
}
int TGHtml::GetLightShadowColor(int iBgColor)
{
   
   
   if (iLight[iBgColor] == 0) {
      ColorStruct_t *pRef, val;
      pRef = apColor[iBgColor];
      if (isLightColor(pRef)) {
         val.fRed = (unsigned short) (pRef->fRed * 0.9);
         val.fGreen = (unsigned short) (pRef->fGreen * 0.9);
         val.fBlue = (unsigned short) (pRef->fBlue * 0.9);
      } else {
         int t1, t2;
         t1 = (int) MIN(MAX_COLOR, pRef->fGreen * 1.4);
         t2 = (pRef->fGreen + MAX_COLOR) / 2;
         val.fGreen = MAX(t1, t2);
         t1 = (int) MIN(MAX_COLOR, pRef->fRed * 1.4);
         t2 = (pRef->fRed + MAX_COLOR) / 2;
         val.fRed = MAX(t1, t2);
         t1 = (int) MIN(MAX_COLOR, pRef->fBlue * 1.4);
         t2 = (pRef->fBlue + MAX_COLOR) / 2;
         val.fBlue = MAX(t1, t2);
      }
      iLight[iBgColor] = GetColorByValue(&val) + 1;
   }
   return iLight[iBgColor] - 1;
}
int TGHtml::GetColorByValue(ColorStruct_t *pRef)
{
   
   
   int i;
   float dist;
   float closestDist;
   int closest;
   int r, g, b;
# define COLOR_MASK  0xf800
   
   r = pRef->fRed & COLOR_MASK;
   g = pRef->fGreen & COLOR_MASK;
   b = pRef->fBlue & COLOR_MASK;
   for (i = 0; i < N_COLOR; i++) {
      ColorStruct_t *p = apColor[i];
      if (p && 
         ((p->fRed & COLOR_MASK) == r) && 
         ((p->fGreen & COLOR_MASK) == g) &&
         ((p->fBlue & COLOR_MASK) == b)) {
         colorUsed |= (1<<i);
         return i;
      }
   }
   
   for (i = N_PREDEFINED_COLOR; i < N_COLOR; i++) {
      if (apColor[i] == 0) {
         apColor[i] = AllocColorByValue(pRef);
         colorUsed |= (1<<i);
         return i;
      }
   }
   
   
   for (i = N_PREDEFINED_COLOR; i < N_COLOR; i++) {
      if (((colorUsed >> i) & 1) == 0) {
         FreeColor(apColor[i]);
         apColor[i] = AllocColorByValue(pRef);
         colorUsed |= (1<<i);
         return i;
      }
   }
   
   
   closest = 0;
   closestDist = colorDistance(pRef, apColor[0]);
   for (i = 1; i < N_COLOR; i++) {
      dist = colorDistance(pRef, apColor[i]);
      if (dist < closestDist) {
         closestDist = dist;
         closest = i;
      }
   }
   return closest;
}
char *TGHtml::GetHref(int x, int y, char **target)
{
   
   
   
   TGHtmlBlock *pBlock;
   TGHtmlElement *pElem;
   for (pBlock = firstBlock; pBlock; pBlock = pBlock->bNext) {
      if (pBlock->top > y || pBlock->bottom < y ||
          pBlock->left > x || pBlock->right < x) continue;
      pElem = pBlock->pNext;
      if (pElem->type == Html_IMG) {
         TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) pElem;
         if (image->pMap) {
            pElem = image->pMap->pNext;
            while (pElem && pElem->type != Html_EndMAP) {
               if (pElem->type == Html_AREA) {
                  if (InArea((TGHtmlMapArea *) pElem, pBlock->left, pBlock->top, x, y)) {
                     if (target) *target = pElem->MarkupArg("target", 0);
                     return pElem->MarkupArg("href", 0);
                  }
               }
               pElem = pElem->pNext;
            }
            continue;
         }
      }
      if ((pElem->style.flags & STY_Anchor) == 0) continue;
      switch (pElem->type) {
         case Html_Text:
         case Html_Space:
         case Html_IMG:
            while (pElem && pElem->type != Html_A) pElem = pElem->pPrev;
            if (pElem == 0 || pElem->type != Html_A) break;
            if (target) *target = pElem->MarkupArg("target", 0);
            return pElem->MarkupArg("href", 0);
            default:
               break;
      }
  }
  return 0;
}
int TGHtml::ElementCoords(TGHtmlElement *p, int , int pct, int *coords)
{
   
   TGHtmlBlock *pBlock;
   while (p && p->type != Html_Block) p = p->pPrev;
   if (!p) return 1;
   pBlock = (TGHtmlBlock *) p;
   if (pct) {
      TGHtmlElement *pEnd = pLast;
      TGHtmlBlock *pb2;
      while (pEnd && pEnd->type != Html_Block) pEnd = pEnd->pPrev;
      pb2 = (TGHtmlBlock *) pEnd;
#define HGCo(dir) pb2->dir ? pBlock->dir * 100 / pb2->dir : 0
      coords[0] = HGCo(left);
      coords[1] = HGCo(top);
      coords[3] = HGCo(right);
      coords[4] = HGCo(bottom);
   } else {
      coords[0] = pBlock->left;
      coords[1] = pBlock->top;
      coords[2] = pBlock->right;
      coords[3] = pBlock->bottom;
   }
   return 0;
}
TGHtmlElement *TGHtml::AttrElem(char *name, char *value)
{
   TGHtmlElement *p;
   char *z;
   for (p = pFirst; p; p = p->pNext) {
      if (p->type != Html_A) continue;
      z = p->MarkupArg(name, 0);
      if (z && (strcmp(z, value) == 0)) return p;
   }
   return 0;
}
void TGHtml::UpdateSelection(int forceUpdate)
{
   
   
   
   
   
   
   TGHtmlBlock *pBlock;
   int index;
   int needUpdate = forceUpdate;
   int temp;
   if (selEnd.p == 0) selBegin.p = 0;
   IndexToBlockIndex(selBegin, &pBlock, &index);
   if (needUpdate || pBlock != pSelStartBlock) {
      needUpdate = 1;
      RedrawBlock(pSelStartBlock);
      pSelStartBlock = pBlock;
      selStartIndex = index;
   } else if (index != selStartIndex) {
      RedrawBlock(pBlock);
      selStartIndex = index;
   }
   if (selBegin.p == 0) selEnd.p = 0;
   IndexToBlockIndex(selEnd, &pBlock, &index);
   if (needUpdate || pBlock != pSelEndBlock) {
      needUpdate = 1;
      RedrawBlock(pSelEndBlock);
      pSelEndBlock = pBlock;
      selEndIndex = index;
   } else if (index != selEndIndex) {
      RedrawBlock(pBlock);
      selEndIndex = index;
   }
  if (pSelStartBlock && pSelStartBlock == pSelEndBlock &&
      selStartIndex > selEndIndex) {
    temp = selStartIndex;
    selStartIndex = selEndIndex;
    selEndIndex = temp;
  }
  if (needUpdate) {
    flags |= ANIMATE_IMAGES;
    UpdateSelectionDisplay();
  }
}
void TGHtml::UpdateSelectionDisplay()
{
   
   
   
   
   
   int selected = 0;
   SHtmlIndex tempIndex;
   TGHtmlBlock *pTempBlock;
   int temp;
   TGHtmlBlock *p;
   for (p = firstBlock; p; p = p->bNext) {
      if (p == pSelStartBlock) {
         selected = 1;
         RedrawBlock(p);
      } else if (!selected && p == pSelEndBlock) {
         selected = 1;
         tempIndex = selBegin;
         selBegin = selEnd;
         selEnd = tempIndex;
         pTempBlock = pSelStartBlock;
         pSelStartBlock = pSelEndBlock;
         pSelEndBlock = pTempBlock;
         temp = selStartIndex;
         selStartIndex = selEndIndex;
         selEndIndex = temp;
         RedrawBlock(p);
      }
      if (p->flags & HTML_Selected) {
         if (!selected) {
            p->flags &= ~HTML_Selected;
            RedrawBlock(p);
         }
      } else {
         if (selected) {
            p->flags |= HTML_Selected;
            RedrawBlock(p);
         }
      }
      if (p == pSelEndBlock) {
         selected = 0;
         RedrawBlock(p);
      }
   }
}
void TGHtml::LostSelection()
{
   
   if (exportSelection) {
      
      pSelStartBlock = 0;
      pSelEndBlock = 0;
      selBegin.p = 0;
      selEnd.p = 0;
      UpdateSelectionDisplay();
   }
}
int TGHtml::SelectionSet(const char *startIx, const char *endIx)
{
   
   SHtmlIndex sBegin, sEnd;
   int bi, ei;
   if (GetIndex(startIx, &sBegin.p, &sBegin.i)) {
      
      return kFALSE;
   }
   if (GetIndex(endIx, &sEnd.p, &sEnd.i)) {
      
      return kFALSE;
   }
   bi = TokenNumber(sBegin.p);
   ei = TokenNumber(sEnd.p);
   if (!(sBegin.p && sEnd.p)) return kTRUE;
   if (bi < ei || (bi == ei && sBegin.i <= sEnd.i)) {
      selBegin = sBegin;
      selEnd = sEnd;
   } else {
      selBegin = sEnd;
      selEnd = sBegin;
   }
   UpdateSelection(0);
   if (exportSelection) {
      
      
      
   }
   return kTRUE;
}
void TGHtml::UpdateInsert()
{
   
   
   IndexToBlockIndex(ins, &pInsBlock, &insIndex);
   RedrawBlock(pInsBlock);
   if (insTimer == 0) {
      insStatus = 0;
      FlashCursor();
   }
}
int TGHtml::SetInsert(const char *insIx)
{
   
   SHtmlIndex i;
   if (!insIx) {
      RedrawBlock(pInsBlock);
      insStatus = 0;
      pInsBlock = 0;
      ins.p = 0;
   } else {
      if (GetIndex(insIx, &i.p, &i.i)) {
         
         return kFALSE;
      }
      RedrawBlock(pInsBlock);
      ins = i;
      UpdateInsert();
   }
   return kTRUE;
}
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.