// $Id: TGHtml.cxx,v 1.4 2007/05/07 15:19:07 brun Exp $
// Author: Valeriy Onuchin 03/05/2007
/*************************************************************************
* Copyright (C) 1995-2001, Rene Brun, Fons Rademakers and Reiner Rohlfs *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/
/**************************************************************************
HTML widget for xclass. Based on tkhtml 1.28
Copyright (C) 1997-2000 D. Richard Hipp
Copyright (C) 2002-2003 Hector Peraza.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**************************************************************************/
#include
#include
#include
#include "TSystem.h"
#include "TGHtml.h"
#include "THashTable.h"
#include "TObjString.h"
#include "TGIdleHandler.h"
#include "TImage.h"
#include "TGScrollBar.h"
#include "TGTextEntry.h"
#include "TGText.h"
#include "Riostream.h"
#include "TGComboBox.h"
#include "TGListBox.h"
//_____________________________________________________________________________
//
// TGHtml
//
// The ROOT HTML widget. A derivate of TGView.
//_____________________________________________________________________________
ClassImp(TGHtml)
int HtmlTraceMask = 0; //HtmlTrace_Table1 | HtmlTrace_Table4;
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)
{
// HTML Widget constructor.
int i;
fExiting = 0;
fPFirst = 0;
fPLast = 0;
fNToken = 0;
fLastSized = 0;
fNextPlaced = 0;
fFirstBlock = 0;
fLastBlock = 0;
fFirstInput = 0;
fLastInput = 0;
fNInput = 0;
fNForm = 0;
fVarId = 0; // do we need this??
fInputIdx = 0;
fRadioIdx = 0;
fSelBegin.fI = 0;
fSelBegin.fP = 0;
fSelEnd.fI = 0;
fSelEnd.fP = 0;
fPSelStartBlock = 0;
fPSelEndBlock = 0;
fInsOnTime = DEF_HTML_INSERT_ON_TIME;
fInsOffTime = DEF_HTML_INSERT_OFF_TIME;
fInsStatus = 0;
fInsTimer = 0;
fIns.fI = 0;
fIns.fP = 0;
fPInsBlock = 0;
fInsIndex = 0;
fZText = 0;
fNText = 0;
fNAlloc = 0;
fNComplete = 0;
fICol = 0;
fIPlaintext = 0;
fPScript = 0;
fIdle = 0;
fStyleStack = 0;
fParaAlignment = ALIGN_None;
fRowAlignment = ALIGN_None;
fAnchorFlags = 0;
fInDt = 0;
fInTr = 0;
fInTd = 0;
fAnchorStart = 0;
fFormStart = 0;
fFormElemStart = 0;
fFormElemLast = 0;
fLoEndPtr = 0;
fLoFormStart = 0;
fInnerList = 0;
ResetLayoutContext();
fHighlightWidth = 0;
fHighlightBgColorPtr = 0;
fHighlightColorPtr = 0;
for (i = 0; i < N_FONT; ++i) fAFont[i] = 0;
memset(fFontValid, 0, sizeof(fFontValid));
for (i = 0; i < N_COLOR; ++i) {
fApColor[i] = 0;
fIDark[i] = 0;
fILight[i] = 0;
}
fFgColor = AllocColor("black");
fBgColor = AllocColor("white"); //AllocColor("#c0c0c0");
fNewLinkColor = AllocColor(DEF_HTML_UNVISITED);
fOldLinkColor = AllocColor(DEF_HTML_VISITED);
fSelectionColor = AllocColor(DEF_HTML_SELECTION_COLOR);
fApColor[COLOR_Normal] = fFgColor;
fApColor[COLOR_Visited] = fOldLinkColor;
fApColor[COLOR_Unvisited] = fNewLinkColor;
fApColor[COLOR_Selection] = fSelectionColor;
fApColor[COLOR_Background] = fBgColor;
fBgImage = 0;
SetBackgroundColor(fApColor[COLOR_Background]->fPixel);
SetBackgroundPixmap(0); // force usage of solid color
fColorUsed = 0;
for (i = 0; i < N_CACHE_GC; ++i) {
fAGcCache[i].fIndex = 0;
fAGcCache[i].fColor = 0;
fAGcCache[i].fFont = 0;
fAGcCache[i].fGc = 0;
}
fLastGC = 0;
fSelEndIndex =0;
fSelStartIndex = 0;
fGcNextToFree = 0;
fImageList = 0;
fZBaseHref = 0;
fInnerList = 0;
fFormPadding = 5;
fOverrideFonts = 0;
fOverrideColors = 0;
fHasScript = 0;
fHasFrames = 0;
fAddEndTags = 0;
fTableBorderMin = 0;
fVarind = 0;
fIdind = 0;
fInParse = 0;
fZGoto = 0;
fExts = 0;
fUnderlineLinks = kTRUE;
fExportSelection = DEF_HTML_EXPORT_SEL;
fTableRelief = HTML_RELIEF_RAISED;
fRuleRelief = HTML_RELIEF_SUNKEN;
fRulePadding = 5;
fZBase = 0;
fZBaseHref = 0;
fCursor = kPointer;
fMaxX = 0;
fMaxY = 0;
fXMargin = fYMargin = 0; //HTML_INDENT/4;
fFlags = RESIZE_ELEMENTS | RELAYOUT;
fDirtyLeft = LARGE_NUMBER;
fDirtyRight = 0;
fDirtyTop = LARGE_NUMBER;
fDirtyBottom = 0;
fVsb->SetAccelerated();
fHsb->SetAccelerated();
fLastUri = 0;
AddInput(kExposureMask | kFocusChangeMask);
AddInput(kButtonPressMask | kButtonReleaseMask | kPointerMotionMask);
fUidTable = new THashTable(100);
}
//______________________________________________________________________________
TGHtml::~TGHtml()
{
// HTML widget destructor.
int i;
fExiting = 1;
HClear();
for (i = 0; i < N_FONT; i++) {
if (fAFont[i] != 0) fClient->FreeFont(fAFont[i]);
}
if (fInsTimer) delete fInsTimer;
if (fIdle) delete fIdle;
// TODO: should also free colors!
}
//______________________________________________________________________________
void TGHtml::UpdateBackgroundStart()
{
// Start background update.
//GCValues_t gcv;
//unsigned int mask = GCTileStipXOrigin | GCTileStipYOrigin;
//
//gcv.ts_x_origin = -fVvisible.fX;
//gcv.ts_y_origin = -fVisibleStart.fY;
//XChangeGC(GetDisplay(), _backGC, mask, &gcv);
}
//______________________________________________________________________________
void TGHtml::FreeColor(ColorStruct_t *color)
{
// Free system color.
gVirtualX->FreeColor(gClient->GetDefaultColormap(), color->fPixel);
delete color;
}
//______________________________________________________________________________
ColorStruct_t *TGHtml::AllocColor(const char *name)
{
// Allocate system color by name.
ColorStruct_t *color = new ColorStruct_t;
color->fPixel = 0;
if (gVirtualX->ParseColor(fClient->GetDefaultColormap(), name, *color)) {
if (!gVirtualX->AllocColor(fClient->GetDefaultColormap(), *color)) {
// force allocation of pixel 0
gVirtualX->QueryColor(fClient->GetDefaultColormap(), *color);
gVirtualX->AllocColor(fClient->GetDefaultColormap(), *color);
}
}
return color;
}
//______________________________________________________________________________
ColorStruct_t *TGHtml::AllocColorByValue(ColorStruct_t *color)
{
// Allocate system color by value.
ColorStruct_t *c = new ColorStruct_t;
*c = *color;
if (!gVirtualX->AllocColor(gClient->GetDefaultColormap(), *c)) {
// force allocation of pixel 0
c->fPixel = 0;
gVirtualX->QueryColor(gClient->GetDefaultColormap(), *c);
gVirtualX->AllocColor(gClient->GetDefaultColormap(), *c);
}
return c;
}
//______________________________________________________________________________
void TGHtml::Clear(Option_t *)
{
// Erase all HTML from this widget and clear the screen. This is
// typically done before loading a new document.
HClear();
TGView::Clear();
fFlags |= REDRAW_TEXT | VSCROLL | HSCROLL;
ScheduleRedraw();
}
//______________________________________________________________________________
int TGHtml::ParseText(char *text, const char *index)
{
// Appends (or insert at the specified position) the given HTML text to the
// end of any HTML text that may have been inserted by prior calls to this
// command. Then it runs the tokenizer, parser and layout engine as far as
// possible with the text that is available. The display is updated
// appropriately.
SHtmlIndex_t iStart;
TGHtmlElement *savePtr=0;
iStart.fP = 0;
iStart.fI = 0;
fLoEndPtr = fPLast;
if (index) {
int rc = GetIndex(index, &iStart.fP, &iStart.fI);
if (rc != 0) return kFALSE; // malformed index
if (iStart.fP) {
savePtr = iStart.fP->fPNext;
fPLast = iStart.fP;
iStart.fP->fPNext = 0;
}
}
TokenizerAppend(text);
if (fLoEndPtr) {
fFormStart = fLoFormStart;
if (iStart.fP && savePtr) {
AddStyle(fLoEndPtr);
fPLast->fPNext = savePtr;
savePtr->fPPrev = fPLast;
fPLast = fLoEndPtr;
fFlags |= REDRAW_TEXT | RELAYOUT;
ScheduleRedraw();
} else if (fLoEndPtr->fPNext) {
AddStyle(fLoEndPtr->fPNext);
}
} else if (fPFirst) {
fParaAlignment = ALIGN_None;
fRowAlignment = ALIGN_None;
fAnchorFlags = 0;
fInDt = 0;
fAnchorStart = 0;
fFormStart = 0;
fInnerList = 0;
fNInput = 0;
AddStyle(fPFirst);
}
#if 1
fLoEndPtr = fPLast;
fLoFormStart = fFormStart;
#endif
fFlags |= EXTEND_LAYOUT;
ScheduleRedraw();
return kTRUE;
}
//______________________________________________________________________________
void TGHtml::SetTableRelief(int relief)
{
// Sets relief mode of html table.
if (fTableRelief != relief) {
fTableRelief = relief;
fFlags |= RELAYOUT;
RedrawEverything();
}
}
//______________________________________________________________________________
void TGHtml::SetRuleRelief(int relief)
{
// Sets relief mode of html rule.
if (fRuleRelief != relief) {
fRuleRelief = relief;
fFlags |= RELAYOUT;
RedrawEverything();
}
}
//______________________________________________________________________________
void TGHtml::UnderlineLinks(int onoff)
{
// Set/reset html links underline.
if (fUnderlineLinks != onoff) {
fUnderlineLinks = onoff;
// fFlags |= RESIZE_ELEMENTS | RELAYOUT;
// AddStyle(fPFirst);
TGHtmlElement *p;
SHtmlStyle_t style = GetCurrentStyle();
for (p = fPFirst; p; p = p->fPNext) {
if (p->fType == Html_A) {
if (fAnchorStart) {
style = PopStyleStack(Html_EndA);
fAnchorStart = 0;
fAnchorFlags = 0;
}
const char *z = p->MarkupArg("href", 0);
if (z) {
style.fColor = GetLinkColor(z);
if (fUnderlineLinks) style.fFlags |= STY_Underline;
fAnchorFlags |= STY_Anchor;
PushStyleStack(Html_EndA, style);
fAnchorStart = (TGHtmlAnchor *) p;
}
} else if (p->fType == Html_EndA) {
if (fAnchorStart) {
((TGHtmlRef *)p)->fPOther = fAnchorStart;
style = PopStyleStack(Html_EndA);
fAnchorStart = 0;
fAnchorFlags = 0;
}
}
p->fStyle.fFlags &= ~STY_Underline;
p->fStyle.fFlags |= (style.fFlags & STY_Underline);
}
RedrawEverything();
}
}
//______________________________________________________________________________
void TGHtml::SetBaseUri(const char *uri)
{
// Sets base URI.
if (fZBase) delete[] fZBase;
fZBase = 0;
if (uri) fZBase = StrDup(uri);
}
//______________________________________________________________________________
int TGHtml::GotoAnchor(const char *name)
{
// Go to anchor position.
const char *z;
TGHtmlElement *p;
for (p = fPFirst; p; p = p->fPNext) {
if (p->fType == Html_A) {
z = p->MarkupArg("name", 0);
if (z && strcmp(z, name) == 0) {
ScrollToPosition(TGLongPosition(fVisible.fX, ((TGHtmlAnchor *)p)->fY));
return kTRUE;
}
}
}
return kFALSE;
}
//______________________________________________________________________________
const char *TGHtml::GetUid(const char *string)
{
// Given a string, this procedure returns a unique identifier for the
// string.
//
// This procedure returns a pointer to a new char string corresponding to
// the "string" argument. The new string has a value identical to string
// (strcmp will return 0), but it's guaranteed that any other calls to this
// procedure with a string equal to "string" will return exactly the same
// result (i.e. can compare pointer *values* directly, without having to
// call strcmp on what they point to).
//int dummy;
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()
{
// Computes virtual size of html area.
fVirtualSize = TGDimension(fMaxX, fMaxY);
}
//______________________________________________________________________________
void TGHtml::ClearGcCache()
{
// Clear the cache of GCs
int i;
for (i = 0; i < N_CACHE_GC; i++) {
if (fAGcCache[i].fIndex) {
gVirtualX->DeleteGC(fAGcCache[i].fGc);
fAGcCache[i].fIndex = 0;
}
}
fGcNextToFree = 0;
}
//______________________________________________________________________________
void TGHtml::ResetLayoutContext()
{
// Reset the main layout context in the main widget. This happens
// before we redo the layout, or just before deleting the widget.
fLayoutContext.Reset();
}
//______________________________________________________________________________
void TGHtml::Redraw()
{
// This routine is invoked in order to redraw all or part of the HTML
// widget. This might happen because the display has changed, or in
// response to an expose event. In all cases, though, this routine
// is called by an idle handler.
Pixmap_t pixmap; // The buffer on which to render HTML
int x, y, w, h; // Virtual canvas coordinates of area to draw
int hw; // highlight thickness
int clipwinH, clipwinW; // Width and height of the clipping window
TGHtmlBlock *pBlock; // For looping over blocks to be drawn
int redoSelection = 0; // kTRUE to recompute the selection
// Don't do anything if we are in the middle of a parse.
if (fInParse) {
fFlags &= ~REDRAW_PENDING;
return;
}
// Recompute the layout, if necessary or requested.
//
// Calling LayoutDoc() is tricky because LayoutDoc() may invoke one
// or more user-overriden methods, and these methods could, in theory,
// do nasty things. So we have to take precautions:
//
// * Do not remove the REDRAW_PENDING flag until after LayoutDoc()
// has been called, to prevent a recursive call to Redraw().
if ((fFlags & RESIZE_ELEMENTS) != 0 && (fFlags & STYLER_RUNNING) == 0) {
TGHtmlImage *pImage;
for (pImage = fImageList; pImage; pImage = pImage->fPNext) {
pImage->fPList = 0;
}
fLastSized = 0;
fFlags &= ~RESIZE_ELEMENTS;
fFlags |= RELAYOUT;
}
// We used to make a distinction between RELAYOUT and EXTEND_LAYOUT.
// RELAYOUT would be used when the widget was resized, but the
// less compute-intensive EXTEND_LAYOUT would be used when new
// text was appended.
//
// Unfortunately, EXTEND_LAYOUT has some problem that arise when
// tables are used. The quick fix is to make an EXTEND_LAYOUT do
// a complete RELAYOUT. Someday, we need to fix EXTEND_LAYOUT so
// that it works right...
if ((fFlags & (RELAYOUT | EXTEND_LAYOUT)) != 0
&& (fFlags & STYLER_RUNNING) == 0) {
fNextPlaced = 0;
//fNInput = 0;
fVarId = 0;
fMaxX = 0;
fMaxY = 0;
ResetLayoutContext();
fFirstBlock = 0;
fLastBlock = 0;
redoSelection = 1;
fFlags &= ~RELAYOUT;
fFlags |= HSCROLL | VSCROLL | REDRAW_TEXT | EXTEND_LAYOUT;
}
if ((fFlags & EXTEND_LAYOUT) && fPFirst != 0) {
LayoutDoc();
fFlags &= ~EXTEND_LAYOUT;
FormBlocks();
MapControls();
if (redoSelection && fSelBegin.fP && fSelEnd.fP) {
UpdateSelection(1);
UpdateInsert();
}
}
fFlags &= ~REDRAW_PENDING;
// No need to do any actual drawing if we aren't mapped
//// if (!IsMapped()) return;
// Update the scrollbars.
if ((fFlags & (HSCROLL | VSCROLL)) != 0) {
ComputeVirtualSize();
fFlags &= ~(HSCROLL | VSCROLL);
if (fFlags & REDRAW_PENDING) return;
}
// Redraw the focus highlight, if requested
hw = fHighlightWidth;
if (fFlags & REDRAW_FOCUS) {
if (hw > 0) {
#if 0
unsigned long color;
if (fFlags & GOT_FOCUS) {
color = highlightColorPtr;
} else {
color = highlightBgColorPtr;
}
_DrawFocusHighlight(color);
#endif
}
fFlags &= ~REDRAW_FOCUS;
}
// If the styler is in a callback, abort further processing.
// TODO: check this!
if (fFlags & STYLER_RUNNING) {
goto earlyOut;
}
MapControls();
// Compute the virtual canvas coordinates corresponding to the
// dirty region of the clipping window.
clipwinW = fCanvas->GetWidth();
clipwinH = fCanvas->GetHeight();
if (fFlags & REDRAW_TEXT) {
w = clipwinW;
h = clipwinH;
x = fVisible.fX;
y = fVisible.fY;
fDirtyLeft = 0;
fDirtyTop = 0;
fFlags &= ~REDRAW_TEXT;
} else {
if (fDirtyLeft < 0) fDirtyLeft = 0;
if (fDirtyRight > clipwinW) fDirtyRight = clipwinW;
if (fDirtyTop < 0) fDirtyTop = 0;
if (fDirtyBottom > clipwinH) fDirtyBottom = clipwinH;
w = fDirtyRight - fDirtyLeft;
h = fDirtyBottom - fDirtyTop;
x = fVisible.fX + fDirtyLeft;
y = fVisible.fY + fDirtyTop;
}
// Skip the rest of the drawing process if the area to be refreshed is
// less than zero
if (w > 0 && h > 0) {
GContext_t gcBg;
TGRectangle xrec;
// printf("Redraw %dx%d at %d,%d\n", w, h, x, y);
// Allocate and clear a pixmap upon which to draw
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
//old-- XFillRectangles(GetDisplay(), pixmap, gcBg, &xrec, 1);
//new-- if (fBgImage)
// BGDraw(fVisible.fX, fVisible.fY, w, h, fBgImage);
#else
fWhiteGC.SetTileStipXOrigin(-fVisible.fX - fDirtyLeft);
fWhiteGC.SetTileStipYOrigin(-fVisible.fY - fDirtyTop);
gVirtualX->FillRectangle(pixmap, fWhiteGC.GetGC(), 0, 0, w, h);
UpdateBackgroundStart(); // back to original
#endif
// Render all visible HTML onto the pixmap
for (pBlock = fFirstBlock; pBlock; pBlock = pBlock->fBNext) {
if (pBlock->fTop <= y+h && pBlock->fBottom >= y-10 &&
pBlock->fLeft <= x+w && pBlock->fRight >= x-10) {
BlockDraw(pBlock, pixmap, x, y, w, h, pixmap);
}
}
// Finally, copy the pixmap onto the window and delete the pixmap
gVirtualX->CopyArea(pixmap, fCanvas->GetId(),
gcBg, 0, 0, w, h, fDirtyLeft, fDirtyTop);
gVirtualX->Update(kFALSE);
gVirtualX->DeletePixmap(pixmap);
// XFlush(GetDisplay());
}
// Redraw images, if requested
if (fFlags & REDRAW_IMAGES) {
TGHtmlImage *pImage;
TGHtmlImageMarkup *pElem;
int top, bottom, left, right; // Coordinates of the clipping window
int imageTop; // Top edge of image
top = fVisible.fY;
bottom = top + fCanvas->GetHeight();
left = fVisible.fX;
right = left + fCanvas->GetWidth();
for (pImage = fImageList; pImage; pImage = pImage->fPNext) {
for (pElem = pImage->fPList; pElem; pElem = pElem->fINext) {
if (pElem->fRedrawNeeded == 0) continue;
imageTop = pElem->fY - pElem->fAscent;
if (imageTop > bottom || imageTop + pElem->fH < top
|| pElem->fX > right || pElem->fX + pElem->fW < left) continue;
DrawImage(pElem, fCanvas->GetId(), left, top, right, bottom);
}
}
fFlags &= ~(REDRAW_IMAGES | ANIMATE_IMAGES);
}
// Set the dirty region to the empty set.
earlyOut:
fDirtyTop = LARGE_NUMBER;
fDirtyLeft = LARGE_NUMBER;
fDirtyBottom = 0;
fDirtyRight = 0;
return;
}
//______________________________________________________________________________
void TGHtml::ScheduleRedraw()
{
// Make sure that a call to the Redraw() routine has been queued.
if ((fFlags & REDRAW_PENDING) == 0 /*&& IsMapped()*/) {
if (!fIdle) fIdle = new TGIdleHandler(this);
fFlags |= REDRAW_PENDING;
}
}
//______________________________________________________________________________
Bool_t TGHtml::HandleIdleEvent(TGIdleHandler *idle)
{
// Handles idle event.
if (idle != fIdle) return kFALSE;
Redraw();
delete fIdle;
fIdle = NULL;
return kTRUE;
}
//______________________________________________________________________________
void TGHtml::RedrawArea(int left, int top, int right, int bottom)
{
// If any part of the screen needs to be redrawn, then call this routine
// with the values of a box (in window coordinates) that needs to be
// redrawn. This routine will schedule an idle handler to do the redraw.
//
// The box coordinates are relative to the clipping window (fCanvas).
if (bottom < 0) return;
if (top > (int)fCanvas->GetHeight()) return;
if (right < 0) return;
if (left > (int)fCanvas->GetWidth()) return;
if (fDirtyTop > top) fDirtyTop = top;
if (fDirtyLeft > left) fDirtyLeft = left;
if (fDirtyBottom < bottom) fDirtyBottom = bottom;
if (fDirtyRight < right) fDirtyRight = right;
ScheduleRedraw();
}
//______________________________________________________________________________
void TGHtml::DrawRegion(Int_t x, Int_t y, UInt_t w, UInt_t h)
{
// Draw region defined by [x,y] [w,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 (fDirtyTop > top) fDirtyTop = top;
if (fDirtyLeft > left) fDirtyLeft = left;
if (fDirtyBottom < bottom) fDirtyBottom = bottom;
if (fDirtyRight < right) fDirtyRight = right;
fFlags |= REDRAW_PENDING;
Redraw();
#endif
return;
}
//______________________________________________________________________________
Bool_t TGHtml::ItemLayout()
{
// Layout html widget.
#if 0
fFlags |= RELAYOUT | VSCROLL | HSCROLL;
Redraw(); //RedrawEverything();
#else
fNextPlaced = 0;
//fNInput = 0;
fVarId = 0;
fMaxX = 0;
fMaxY = 0;
ResetLayoutContext();
fFirstBlock = 0;
fLastBlock = 0;
if (fPFirst != 0) {
LayoutDoc();
FormBlocks();
MapControls();
if (fSelBegin.fP && fSelEnd.fP) {
UpdateSelection(1);
UpdateInsert();
}
}
ComputeVirtualSize();
ScheduleRedraw();
#endif
return kTRUE;
}
//______________________________________________________________________________
void TGHtml::RedrawBlock(TGHtmlBlock *p)
{
// Redraw the TGHtmlBlock given.
if (p) {
RedrawArea(p->fLeft - fVisible.fX, p->fTop - fVisible.fY,
p->fRight - fVisible.fX + 1, p->fBottom - fVisible.fY);
}
}
//______________________________________________________________________________
void TGHtml::RedrawEverything()
{
// Call this routine to force the entire widget to be redrawn.
fFlags |= REDRAW_FOCUS | REDRAW_TEXT;
ScheduleRedraw();
}
//______________________________________________________________________________
void TGHtml::RedrawText(int y)
{
// Call this routine to cause all of the rendered HTML at the
// virtual canvas coordinate of Y and beyond to be redrawn.
int clipHeight; // Height of the clipping window
clipHeight = fCanvas->GetHeight();
y -= fVisible.fY;
if (y < clipHeight) {
RedrawArea(0, y, LARGE_NUMBER, clipHeight);
}
}
//______________________________________________________________________________
void TGHtml::HClear()
{
// Erase all data from the HTML widget. Bring it back to an empty screen.
int i;
TGHtmlElement *p, *fPNext;
fXMargin = fYMargin = 0; //HTML_INDENT/4;
DeleteControls();
for (p = fPFirst; p; p = fPNext) {
fPNext = p->fPNext;
delete p;
}
fPFirst = 0;
fPLast = 0;
fNToken = 0;
if (fZText) delete[] fZText;
fZText = 0;
fNText = 0;
fNAlloc = 0;
fNComplete = 0;
fIPlaintext = 0;
for (i = 0; i < N_COLOR; ++i) {
if (fApColor[i] != 0) FreeColor(fApColor[i]);
fApColor[i] = 0;
fIDark[i] = 0;
fILight[i] = 0;
}
if (!fExiting) {
fFgColor = AllocColor("black");
fBgColor = AllocColor("white"); //AllocColor("#c0c0c0");
fNewLinkColor = AllocColor(DEF_HTML_UNVISITED);
fOldLinkColor = AllocColor(DEF_HTML_VISITED);
fSelectionColor = AllocColor(DEF_HTML_SELECTION_COLOR);
fApColor[COLOR_Normal] = fFgColor;
fApColor[COLOR_Visited] = fOldLinkColor;
fApColor[COLOR_Unvisited] = fNewLinkColor;
fApColor[COLOR_Selection] = fSelectionColor;
fApColor[COLOR_Background] = fBgColor;
SetBackgroundColor(fApColor[COLOR_Background]->fPixel);
SetBackgroundPixmap(0); // use solid color
}
fColorUsed = 0;
while (fImageList) {
TGHtmlImage *p2 = fImageList;
fImageList = p2->fPNext;
delete p2;
}
if (fBgImage) delete fBgImage;
fBgImage = 0;
while (fStyleStack) {
SHtmlStyleStack_t *p2 = fStyleStack;
fStyleStack = p2->fPNext;
delete p2;
}
ClearGcCache();
ResetLayoutContext();
// if (fZBase) delete[] fZBase;
// fZBase = 0;
if (fZBaseHref) delete [] fZBaseHref;
fZBaseHref = 0;
fLastSized = 0;
fNextPlaced = 0;
fFirstBlock = 0;
fLastBlock = 0;
fNInput = 0;
fNForm = 0;
fVarId = 0;
fParaAlignment = ALIGN_None;
fRowAlignment = ALIGN_None;
fAnchorFlags = 0;
fInDt = 0;
fAnchorStart = 0;
fFormStart = 0;
fInnerList = 0;
fMaxX = 0;
fMaxY = 0;
#if 0 // in OXView::Clear()
fVisible = TGPosition(0, 0);
_virtualSize = TGDimension(0, 0);
ScrollTTGPosition(fVisible);
#endif
fPInsBlock = 0;
fIns.fP = 0;
fSelBegin.fP = 0;
fSelEnd.fP = 0;
fPSelStartBlock = 0;
fPSelEndBlock = 0;
fHasScript = 0;
fHasFrames = 0;
fLastUri = 0;
}
//______________________________________________________________________________
Bool_t TGHtml::HandleTimer(TTimer *t)
{
// Handle timer event.
if (t == fInsTimer) {
if (fInsTimer) delete fInsTimer;
fInsTimer = NULL;
FlashCursor();
return kTRUE;
} else {
TGHtmlImage *pImage;
for (pImage = fImageList; pImage; pImage = pImage->fPNext) {
if (pImage->fTimer == t) {
AnimateImage(pImage);
return kTRUE;
}
}
}
return kFALSE;
}
//______________________________________________________________________________
void TGHtml::FlashCursor()
{
// Flash the insertion cursor.
if (fPInsBlock == 0 || fInsOnTime <= 0 || fInsOffTime <= 0) return;
RedrawBlock(fPInsBlock);
if ((fFlags & GOT_FOCUS) == 0) {
fInsStatus = 0;
} else if (fInsStatus) {
fInsTimer = new TTimer(this, fInsOffTime);
fInsStatus = 0;
} else {
fInsTimer = new TTimer(this, fInsOnTime);
fInsStatus = 1;
}
}
//______________________________________________________________________________
GContext_t TGHtml::GetGC(int color, int font)
{
// Return a GC from the cache. As many as N_CACHE_GCs are kept valid
// at any one time. They are replaced using an LRU algorithm.
//
// A value of FONT_Any (-1) for the font means "don't care".
int i, j;
GcCache_t *p = fAGcCache;
GCValues_t gcValues;
TGFont *xfont;
// Check for an existing GC.
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->fIndex == 0) continue;
if ((font < 0 || p->fFont == font) && p->fColor == color) {
if (p->fIndex > 1) {
for (j = 0; j < N_CACHE_GC; j++) {
if (fAGcCache[j].fIndex && fAGcCache[j].fIndex < p->fIndex ) {
fAGcCache[j].fIndex++;
}
}
p->fIndex = 1;
}
return fAGcCache[i].fGc;
}
}
// No GC matches. Find a place to allocate a new GC.
p = fAGcCache;
for (i = 0; i < N_CACHE_GC; i++, p++) {
if (p->fIndex == 0 || p->fIndex == N_CACHE_GC) break;
}
if (i >= N_CACHE_GC) { // No slot, so free one (round-robin)
p = fAGcCache;
for (i = 0; i < N_CACHE_GC && i < fGcNextToFree; ++i, ++p) {}
fGcNextToFree = (fGcNextToFree + 1) % N_CACHE_GC;
gVirtualX->DeleteGC(p->fGc);
}
gcValues.fForeground = fApColor[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->fGc = gVirtualX->CreateGC(fId, &gcValues);
if (p->fIndex == 0) p->fIndex = N_CACHE_GC + 1;
for (j = 0; j < N_CACHE_GC; j++) {
if (fAGcCache[j].fIndex && fAGcCache[j].fIndex < p->fIndex) {
fAGcCache[j].fIndex++;
}
}
p->fIndex = 1;
p->fFont = font;
p->fColor = color;
return p->fGc;
}
//______________________________________________________________________________
GContext_t TGHtml::GetAnyGC()
{
// Retrieve any valid GC. The font and color don't matter since the
// GC will only be used for copying.
int i;
GcCache_t *p = fAGcCache;
for (i = 0; i < N_CACHE_GC; i++, p++) {
if (p->fIndex) return p->fGc;
}
return GetGC(COLOR_Normal, FONT_Default);
}
//______________________________________________________________________________
Bool_t TGHtml::HandleFocusChange(Event_t *event)
{
// Handle focus change event.
if (event->fType == kFocusIn) {
fFlags |= GOT_FOCUS | REDRAW_FOCUS;
ScheduleRedraw();
UpdateInsert();
} else { // FocusOut
fFlags &= ~GOT_FOCUS;
fFlags |= REDRAW_FOCUS;
ScheduleRedraw();
}
return kTRUE;
}
//______________________________________________________________________________
TGHtmlInput *TGHtml::GetInputElement(int x, int y)
{
// This routine searchs for a hyperlink beneath the coordinates x,y
// and returns a pointer to the HREF for that hyperlink. The text
// is held in one of the markup argv[] fields of the markup.
TGHtmlInput *p; // For looping over all controls
int vx, vy, vw, vh; // Part of the virtual canvas that is visible
vx = fVisible.fX;
vy = fVisible.fY;
vw = fCanvas->GetWidth();
vh = fCanvas->GetHeight();
for (p = fFirstInput; p; p = p->fINext) {
if (p->fFrame == 0) continue;
if (p->fY < vy + vh && p->fY + p->fH > vy &&
p->fX < vx + vw && p->fX + p->fW > vx) {
if ((x > p->fX) && (y > p->fY) && (x < (p->fX + p->fW)) &&
(y < (p->fY + p->fH)) ) {
return p;
}
}
}
return 0;
}
//______________________________________________________________________________
Bool_t TGHtml::HandleHtmlInput(TGHtmlInput *pr, Event_t *event)
{
// Handle html input (button, checkbox, ...) event.
Window_t childdum;
Event_t eventSt;
eventSt.fType = event->fType;
eventSt.fWindow = event->fWindow;
eventSt.fTime = event->fTime;
eventSt.fX = 2;
eventSt.fY = 2;
eventSt.fXRoot = event->fXRoot;
eventSt.fYRoot = event->fYRoot;
eventSt.fCode = event->fCode;
eventSt.fState = event->fState;
eventSt.fWidth = event->fWidth;
eventSt.fHeight = event->fHeight;
eventSt.fCount = event->fCount;
eventSt.fSendEvent = event->fSendEvent;
eventSt.fHandle = event->fHandle;
eventSt.fFormat = event->fFormat;
eventSt.fUser[0] = event->fUser[0];
eventSt.fUser[1] = event->fUser[1];
eventSt.fUser[2] = event->fUser[2];
eventSt.fUser[3] = event->fUser[3];
eventSt.fUser[4] = event->fUser[4];
gVirtualX->TranslateCoordinates(GetId(), pr->fFrame->GetId(),
event->fX, event->fY, eventSt.fX,
eventSt.fY, childdum);
const char *name = pr->MarkupArg("name", 0);
const char *val = pr->MarkupArg("value", 0);
switch (pr->fItype) {
case INPUT_TYPE_Submit:
case INPUT_TYPE_Button: {
TGButton *b = (TGButton *) pr->fFrame;
Bool_t was = !b->IsDown();
b->HandleButton(&eventSt);
Bool_t now = !b->IsDown();
if (!was && now) {
if (pr->fItype == INPUT_TYPE_Submit)
SubmitClicked(val); // emit SubmitClicked
else
ButtonClicked(name, val); // emit ButtonClicked
}
break;
}
case INPUT_TYPE_Radio: {
TGRadioButton *rb = (TGRadioButton *) pr->fFrame;
Bool_t was = !rb->IsDown();
rb->HandleButton(&eventSt);
Bool_t now = !rb->IsDown();
if ((!was && now) || (was && !now)) {
HandleRadioButton(pr);
RadioChanged(name, val); // emit RadioChanged
}
break;
}
case INPUT_TYPE_Checkbox: {
TGCheckButton *cb = (TGCheckButton *) pr->fFrame;
Bool_t was = !cb->IsDown();
cb->HandleButton(&eventSt);
Bool_t now = !cb->IsDown();
if ((!was && now) || (was && !now))
CheckToggled(name, !now, val); // emit CheckToggled
break;
}
case INPUT_TYPE_Text:
case INPUT_TYPE_Password: {
TGTextEntry *te = (TGTextEntry *) pr->fFrame;
te->SetFocus();
break;
}
case INPUT_TYPE_Select: {
RemoveInput(kButtonPressMask | kButtonReleaseMask | kPointerMotionMask);
eventSt.fUser[0] = childdum;
if (pr->fFrame->InheritsFrom("TGComboBox"))
((TGComboBox *)pr->fFrame)->HandleButton(&eventSt);
else if (pr->fFrame->InheritsFrom("TGListBox"))
((TGListBox *)pr->fFrame)->HandleButton(&eventSt);
InputSelected(name, val); // emit InputSelected
AddInput(kButtonPressMask | kButtonReleaseMask | kPointerMotionMask);
break;
}
default:
break;
}
return kTRUE;
}
//______________________________________________________________________________
Bool_t TGHtml::HandleRadioButton(TGHtmlInput *p)
{
// Handle radio button event.
TGHtmlInput *pr;
for (pr = fFirstInput; pr; pr = pr->fINext) {
if ((pr->fPForm == p->fPForm) && (pr->fItype == INPUT_TYPE_Radio)) {
if (pr != p) {
if (strcmp(pr->MarkupArg("name", ""), p->MarkupArg("name", "")) == 0) {
((TGRadioButton *)pr->fFrame)->SetState(kButtonUp);
}
}
}
}
return kTRUE;
}
//______________________________________________________________________________
void TGHtml::ButtonClicked(const char *name, const char *val)
{
// Emit ButtonClicked() signal.
Long_t args[2];
args[0] = (Long_t)name;
args[1] = (Long_t)val;
Emit("ButtonClicked(char*,char*)", args);
}
//______________________________________________________________________________
void TGHtml::CheckToggled(const char *name, Bool_t on, const char *val)
{
// Emit CheckToggled() signal.
Long_t args[3];
args[0] = (Long_t)name;
args[1] = on;
args[2] = (Long_t)val;
Emit("CheckToggled(char*,Bool_t,char*)", args);
}
//______________________________________________________________________________
void TGHtml::RadioChanged(const char *name, const char *val)
{
// Emit RadioChanged() signal.
Long_t args[2];
args[0] = (Long_t)name;
args[1] = (Long_t)val;
Emit("RadioChanged(char*,char*)", args);
}
//______________________________________________________________________________
void TGHtml::InputSelected(const char *name, const char *val)
{
// Emit Selected() signal.
Long_t args[2];
args[0] = (Long_t)name;
args[1] = (Long_t)val;
Emit("InputSelected(char*,char*)", args);
}
//______________________________________________________________________________
void TGHtml::SubmitClicked(const char *val)
{
// Emit SubmitClicked() signal.
Emit("SubmitClicked(char*)", val);
}
//______________________________________________________________________________
Bool_t TGHtml::HandleButton(Event_t *event)
{
// Handle mouse button event.
int amount, ch;
ch = fCanvas->GetHeight();
amount = fScrollVal.fY * TMath::Max(ch/6, 1);
int ix = event->fX + fVisible.fX;
int iy = event->fY + fVisible.fY;
TGHtmlInput *pr = GetInputElement(ix, iy);
if (pr) {
HandleHtmlInput(pr, event);
}
if ((event->fType == kButtonPress) && (event->fCode == kButton1)) {
int x = event->fX + fVisible.fX;
int y = event->fY + fVisible.fY;
const 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);
//!!delete[] 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)
{
// handle mouse motion events
int x = event->fX + fVisible.fX;
int y = event->fY + fVisible.fY;
const char *uri = GetHref(x, y);
if (uri) {
gVirtualX->SetCursor(fId, gVirtualX->CreateCursor(kHand));
} else {
gVirtualX->SetCursor(fId, gVirtualX->CreateCursor(kPointer));
}
if (uri != fLastUri) {
fLastUri = uri;
if (uri) uri = ResolveUri(uri);
MouseOver(uri);
//!!if (uri) delete [] uri;
}
return kTRUE;
}
//______________________________________________________________________________
TGFont *TGHtml::GetFont(int iFont)
{
// The rendering and layout routines should call this routine in order to
// get a font structure. The iFont parameter specifies which of the N_FONT
// fonts should be obtained. The font is allocated if necessary.
TGFont *toFree = 0;
if (iFont < 0) iFont = 0;
if (iFont >= N_FONT) { iFont = N_FONT - 1; CANT_HAPPEN; }
// If the font has previously been allocated, but the "fontValid" bitmap
// shows it is no longer valid, then mark it for freeing later. We use
// a policy of allocate-before-free because xclass' font cache operates
// much more efficiently that way.
if (!FontIsValid(iFont) && fAFont[iFont] != 0) {
toFree = fAFont[iFont];
fAFont[iFont] = 0;
}
// If we need to allocate a font, first construct the font name then
// allocate it.
if (fAFont[iFont] == 0) {
char name[200]; // Name of the font
const char *familyStr = "";
int iFamily;
int iSize;
int size;
iFamily = FontFamily(iFont) >> 3;
iSize = FontSize(iFont) + 1;
switch (iFamily) {
//#define TIMES
#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/*8*/; break;
case 2: size = 10+finc/*10*/; break;
case 3: size = 12+finc/*12*/; break;
case 4: size = 14+finc/*14*/; break;
case 5: size = 20+finc/*16*/; break;
case 6: size = 24+finc/*18*/; break;
case 7: size = 30+finc/*24*/; break;
default: size = 14+finc/*14*/; 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
snprintf(name, 199, familyStr, size);
// Get the named font
fAFont[iFont] = fClient->GetFont(name);
if (fAFont[iFont] == 0) {
fprintf(stderr, "TGHtml: could not get font \"%s\", trying fixed\n",
name);
fAFont[iFont] = fClient->GetFont("fixed");
}
if (fAFont[iFont] == 0 ){
fprintf(stderr, "TGHtml: could not get font \"fixed\", trying "
"\"helvetica -12\"\n");
fAFont[iFont] = fClient->GetFont("helvetica -12");
}
FontSetValid(iFont);
}
// Free the expired font, if any.
if (toFree) fClient->FreeFont(toFree);
return fAFont[iFont];
}
//______________________________________________________________________________
int TGHtml::InArea(TGHtmlMapArea *p, int left, int top, int x, int y)
{
// Only support rect and circles for now
int *ip = p->fCoords;
if (!ip) return 0;
if (p->fMType == HTML_MAP_RECT) {
return ((left + ip[0]) <= x && (left + ip[2]) >= x &&
(top + ip[1]) <= y && (top + ip[3]) >= y);
} else if (p->fMType == 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(const char *name)
{
// Returns html map element.
TGHtmlElement *p = fPFirst;
const char *z, *zb;
while (p) {
if (p->fType == 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->fPNext;
}
return 0;
}
//______________________________________________________________________________
float TGHtml::ColorDistance(ColorStruct_t *pA, ColorStruct_t *pB)
{
// Compute the squared distance between two colors
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(const char *zColor)
{
// This routine returns an index between 0 and N_COLOR-1 which indicates
// which ColorStruct_t structure in the fApColor[] array should be used to describe
// the color specified by the given name.
ColorStruct_t *pNew;
int iColor;
const char *name; // unique!
int i, n;
char zAltColor[16];
// Netscape accepts color names that are just HEX values, without
// the # up front. This isn't valid HTML, but we support it for
// compatibility.
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) {
snprintf(zAltColor, 15, "#%s", zColor);
} else {
strlcpy(zAltColor, zColor, sizeof(zAltColor));
}
name = GetUid(zAltColor);
} else {
name = GetUid(zColor);
}
pNew = AllocColor(name);
if (pNew == 0) {
return 0; // Color 0 is always the default
}
iColor = GetColorByValue(pNew);
FreeColor(pNew);
return iColor;
}
// Macros used in the computation of appropriate shadow colors.
#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)
{
// Check to see if the given color is too dark to be easily distinguished
// from black.
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)
{
// Given that the background color is iBgColor, figure out an
// appropriate color for the dark part of a 3D shadow.
if (fIDark[iBgColor] == 0) {
ColorStruct_t *pRef, val;
val.fMask = kDoRed | kDoGreen | kDoBlue;
val.fPixel = 0;
pRef = fApColor[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);
}
fIDark[iBgColor] = GetColorByValue(&val) + 1;
}
return fIDark[iBgColor] - 1;
}
//______________________________________________________________________________
int TGHtml::IsLightColor(ColorStruct_t *p)
{
// Check to see if the given color is too light to be easily distinguished
// from white.
return p->fGreen >= 0.85 * MAX_COLOR;
}
//______________________________________________________________________________
int TGHtml::GetLightShadowColor(int iBgColor)
{
// Given that the background color is iBgColor, figure out an
// appropriate color for the bright part of the 3D shadow.
if (fILight[iBgColor] == 0) {
ColorStruct_t *pRef, val;
val.fMask = kDoRed | kDoGreen | kDoBlue;
val.fPixel = 0;
pRef = fApColor[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);
}
fILight[iBgColor] = GetColorByValue(&val) + 1;
}
return fILight[iBgColor] - 1;
}
//______________________________________________________________________________
int TGHtml::GetColorByValue(ColorStruct_t *pRef)
{
// Find a color integer for the color whose color components
// are given by pRef.
int i;
float dist;
float closestDist;
int closest;
int r, g, b;
# define COLOR_MASK 0xf800
// Search for an exact match
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 = fApColor[i];
if (p &&
((p->fRed & COLOR_MASK) == r) &&
((p->fGreen & COLOR_MASK) == g) &&
((p->fBlue & COLOR_MASK) == b)) {
fColorUsed |= (1<> i) & 1) == 0) {
FreeColor(fApColor[i]);
fApColor[i] = AllocColorByValue(pRef);
fColorUsed |= (1< markup.
TGHtmlBlock *pBlock;
TGHtmlElement *pElem;
for (pBlock = fFirstBlock; pBlock; pBlock = pBlock->fBNext) {
if (pBlock->fTop > y || pBlock->fBottom < y ||
pBlock->fLeft > x || pBlock->fRight < x) continue;
pElem = pBlock->fPNext;
if (pElem->fType == Html_IMG) {
TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) pElem;
if (image->fPMap) {
pElem = image->fPMap->fPNext;
while (pElem && pElem->fType != Html_EndMAP) {
if (pElem->fType == Html_AREA) {
if (InArea((TGHtmlMapArea *) pElem, pBlock->fLeft, pBlock->fTop, x, y)) {
if (target) *target = pElem->MarkupArg("target", 0);
return pElem->MarkupArg("href", 0);
}
}
pElem = pElem->fPNext;
}
continue;
}
}
if ((pElem->fStyle.fFlags & STY_Anchor) == 0) continue;
switch (pElem->fType) {
case Html_Text:
case Html_Space:
case Html_IMG:
while (pElem && pElem->fType != Html_A) pElem = pElem->fPPrev;
if (pElem == 0 || pElem->fType != 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 /*i*/, int pct, int *coords)
{
// Return coordinates of item
TGHtmlBlock *pBlock;
while (p && p->fType != Html_Block) p = p->fPPrev;
if (!p) return 1;
pBlock = (TGHtmlBlock *) p;
if (pct) {
TGHtmlElement *pEnd = fPLast;
TGHtmlBlock *pb2;
while (pEnd && pEnd->fType != Html_Block) pEnd = pEnd->fPPrev;
pb2 = (TGHtmlBlock *) pEnd;
#define HGCo(dir) (pb2 && pb2->dir) ? pBlock->dir * 100 / pb2->dir : 0
coords[0] = HGCo(fLeft);
coords[1] = HGCo(fTop);
coords[3] = HGCo(fRight);
coords[4] = HGCo(fBottom);
} else {
coords[0] = pBlock->fLeft;
coords[1] = pBlock->fTop;
coords[2] = pBlock->fRight;
coords[3] = pBlock->fBottom;
}
return 0;
}
//______________________________________________________________________________
TGHtmlElement *TGHtml::AttrElem(const char *name, char *value)
{
// Returns html element matching attribute name and value.
TGHtmlElement *p;
const char *z;
for (p = fPFirst; p; p = p->fPNext) {
if (p->fType != Html_A) continue;
z = p->MarkupArg(name, 0);
if (z && (strcmp(z, value) == 0)) return p;
}
return 0;
}
//______________________________________________________________________________
void TGHtml::UpdateSelection(int forceUpdate)
{
// Given the selection end-points in fSelBegin and fSelEnd, recompute
// pSelBeginBlock and fPSelEndBlock, then call UpdateSelectionDisplay()
// to update the display.
//
// This routine should be called whenever the selection changes or
// whenever the set of TGHtmlBlock structures change.
TGHtmlBlock *pBlock;
int index;
int needUpdate = forceUpdate;
int temp;
if (fSelEnd.fP == 0) fSelBegin.fP = 0;
IndexToBlockIndex(fSelBegin, &pBlock, &index);
if (needUpdate || pBlock != fPSelStartBlock) {
needUpdate = 1;
RedrawBlock(fPSelStartBlock);
fPSelStartBlock = pBlock;
fSelStartIndex = index;
} else if (index != fSelStartIndex) {
RedrawBlock(pBlock);
fSelStartIndex = index;
}
if (fSelBegin.fP == 0) fSelEnd.fP = 0;
IndexToBlockIndex(fSelEnd, &pBlock, &index);
if (needUpdate || pBlock != fPSelEndBlock) {
needUpdate = 1;
RedrawBlock(fPSelEndBlock);
fPSelEndBlock = pBlock;
fSelEndIndex = index;
} else if (index != fSelEndIndex) {
RedrawBlock(pBlock);
fSelEndIndex = index;
}
if (fPSelStartBlock && fPSelStartBlock == fPSelEndBlock &&
fSelStartIndex > fSelEndIndex) {
temp = fSelStartIndex;
fSelStartIndex = fSelEndIndex;
fSelEndIndex = temp;
}
if (needUpdate) {
fFlags |= ANIMATE_IMAGES;
UpdateSelectionDisplay();
}
}
//______________________________________________________________________________
void TGHtml::UpdateSelectionDisplay()
{
// The fPSelStartBlock and fPSelEndBlock values have been changed.
// This routine's job is to loop over all TGHtmlBlocks and either
// set or clear the HTML_Selected bits in the .fFlags field
// as appropriate. For every TGHtmlBlock where the bit changes,
// mark that block for redrawing.
int selected = 0;
SHtmlIndex_t tempIndex;
TGHtmlBlock *pTempBlock;
int temp;
TGHtmlBlock *p;
for (p = fFirstBlock; p; p = p->fBNext) {
if (p == fPSelStartBlock) {
selected = 1;
RedrawBlock(p);
} else if (!selected && p == fPSelEndBlock) {
selected = 1;
tempIndex = fSelBegin;
fSelBegin = fSelEnd;
fSelEnd = tempIndex;
pTempBlock = fPSelStartBlock;
fPSelStartBlock = fPSelEndBlock;
fPSelEndBlock = pTempBlock;
temp = fSelStartIndex;
fSelStartIndex = fSelEndIndex;
fSelEndIndex = temp;
RedrawBlock(p);
}
if (p->fFlags & HTML_Selected) {
if (!selected) {
p->fFlags &= ~HTML_Selected;
RedrawBlock(p);
}
} else {
if (selected) {
p->fFlags |= HTML_Selected;
RedrawBlock(p);
}
}
if (p == fPSelEndBlock) {
selected = 0;
RedrawBlock(p);
}
}
}
//______________________________________________________________________________
void TGHtml::LostSelection()
{
// Clear selection.
if (fExportSelection) {
// clear selection
fPSelStartBlock = 0;
fPSelEndBlock = 0;
fSelBegin.fP = 0;
fSelEnd.fP = 0;
UpdateSelectionDisplay();
}
}
//______________________________________________________________________________
int TGHtml::SelectionSet(const char *startIx, const char *endIx)
{
// Set selection.
SHtmlIndex_t sBegin, sEnd;
int bi, ei;
if (GetIndex(startIx, &sBegin.fP, &sBegin.fI)) {
// malformed start index
return kFALSE;
}
if (GetIndex(endIx, &sEnd.fP, &sEnd.fI)) {
// malformed end index
return kFALSE;
}
bi = TokenNumber(sBegin.fP);
ei = TokenNumber(sEnd.fP);
if (!(sBegin.fP && sEnd.fP)) return kTRUE;
if (bi < ei || (bi == ei && sBegin.fI <= sEnd.fI)) {
fSelBegin = sBegin;
fSelEnd = sEnd;
} else {
fSelBegin = sEnd;
fSelEnd = sBegin;
}
UpdateSelection(0);
if (fExportSelection) {
// TODO:
// get selection ownership ... fId, XA_PRIMARY
// selection lost handler must directly call LostSelection()
}
return kTRUE;
}
//______________________________________________________________________________
void TGHtml::UpdateInsert()
{
// Recompute the position of the insertion cursor based on the
// position in fIns.
IndexToBlockIndex(fIns, &fPInsBlock, &fInsIndex);
RedrawBlock(fPInsBlock);
if (fInsTimer == 0) {
fInsStatus = 0;
FlashCursor();
}
}
//______________________________________________________________________________
int TGHtml::SetInsert(const char *insIx)
{
// Set the position of the insertion cursor.
SHtmlIndex_t i;
if (!insIx) {
RedrawBlock(fPInsBlock);
fInsStatus = 0;
fPInsBlock = 0;
fIns.fP = 0;
} else {
if (GetIndex(insIx, &i.fP, &i.fI)) {
// malformed index
return kFALSE;
}
RedrawBlock(fPInsBlock);
fIns = i;
UpdateInsert();
}
return kTRUE;
}
//______________________________________________________________________________
void TGHtml::SavePrimitive(ostream &out, Option_t *option /*= ""*/)
{
// Save a html widget as a C++ statement(s) on output stream out.
out << " TGHtml *";
out << GetName() << " = new TGHtml(" << fParent->GetName()
<< "," << GetWidth() << "," << GetHeight()
<< ");"<< endl;
if (option && strstr(option, "keep_names"))
out << " " << GetName() << "->SetName(\"" << GetName() << "\");" << endl;
if (fCanvas->GetBackground() != TGFrame::GetWhitePixel()) {
out << " " << GetName() << "->ChangeBackground(" << fCanvas->GetBackground() << ");" << endl;
}
TString fn;
TGText txt(GetText());
fn.Form("Html%s.htm", GetName()+5);
txt.Save(fn.Data());
out << " " << "FILE *f = fopen(\"" << fn.Data() << "\", \"r\");" << endl;
out << " " << "if (f) {" << endl;
out << " " << GetName() << "->Clear();" << endl;
out << " " << GetName() << "->Layout();" << endl;
out << " " << GetName() << "->SetBaseUri(\"\");" << endl;
out << " " << "char *buf = (char *)calloc(4096, sizeof(char));" << endl;
out << " " << "while (fgets(buf, 4096, f)) {" << endl;
out << " " << GetName() << "->ParseText(buf);" << endl;
out << " " << "}" << endl;
out << " " << "free(buf);" << endl;
out << " " << "fclose(f);" << endl;
out << " " << "}" << endl;
out << " " << GetName() << "->Layout();" << endl;
}