Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGHtml.cxx
Go to the documentation of this file.
1 // $Id: TGHtml.cxx,v 1.4 2007/05/07 15:19:07 brun Exp $
2 // Author: Valeriy Onuchin 03/05/2007
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2001, Rene Brun, Fons Rademakers and Reiner Rohlfs *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /**************************************************************************
13 
14  HTML widget for xclass. Based on tkhtml 1.28
15  Copyright (C) 1997-2000 D. Richard Hipp <drh@acm.org>
16  Copyright (C) 2002-2003 Hector Peraza.
17 
18  This library is free software; you can redistribute it and/or
19  modify it under the terms of the GNU Library General Public
20  License as published by the Free Software Foundation; either
21  version 2 of the License, or (at your option) any later version.
22 
23  This library is distributed in the hope that it will be useful,
24  but WITHOUT ANY WARRANTY; without even the implied warranty of
25  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26  Library General Public License for more details.
27 
28  You should have received a copy of the GNU Library General Public
29  License along with this library; if not, write to the Free
30  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 
32 **************************************************************************/
33 
34 #include <ctype.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #include "TSystem.h"
39 #include "TGHtml.h"
40 #include "THashTable.h"
41 #include "TObjString.h"
42 #include "TGIdleHandler.h"
43 #include "TImage.h"
44 #include "TGScrollBar.h"
45 #include "TGTextEntry.h"
46 #include "TGText.h"
47 #include "Riostream.h"
48 #include "TGComboBox.h"
49 #include "TGListBox.h"
50 
51 //_____________________________________________________________________________
52 //
53 // TGHtml
54 //
55 // The ROOT HTML widget. A derivate of TGView.
56 //_____________________________________________________________________________
57 
58 ClassImp(TGHtml);
59 
60 int HtmlTraceMask = 0; //HtmlTrace_Table1 | HtmlTrace_Table4;
61 int HtmlDepth = 0;
62 
63 #define DEF_FRAME_BG_COLOR "#c0c0c0"
64 #define DEF_FRAME_CURSOR ""
65 #define DEF_BUTTON_FG "black"
66 #define DEF_BUTTON_HIGHLIGHT_BG "#d9d9d9"
67 #define DEF_BUTTON_HIGHLIGHT "black"
68 
69 
70 ////////////////////////////////////////////////////////////////////////////////
71 /// HTML Widget constructor.
72 
73 TGHtml::TGHtml(const TGWindow *p, int w, int h, int id) : TGView(p, w, h, id)
74 {
75  SetBit(kIsHtmlView);
76 
77  int i;
78 
79  fExiting = 0;
80  fPFirst = 0;
81  fPLast = 0;
82  fNToken = 0;
83  fLastSized = 0;
84  fNextPlaced = 0;
85  fFirstBlock = 0;
86  fLastBlock = 0;
87  fFirstInput = 0;
88  fLastInput = 0;
89  fNInput = 0;
90  fNForm = 0;
91  fVarId = 0; // do we need this??
92  fInputIdx = 0;
93  fRadioIdx = 0;
94  fSelBegin.fI = 0;
95  fSelBegin.fP = 0;
96  fSelEnd.fI = 0;
97  fSelEnd.fP = 0;
98  fPSelStartBlock = 0;
99  fPSelEndBlock = 0;
100  fInsOnTime = DEF_HTML_INSERT_ON_TIME;
101  fInsOffTime = DEF_HTML_INSERT_OFF_TIME;
102  fInsStatus = 0;
103  fInsTimer = 0;
104  fIns.fI = 0;
105  fIns.fP = 0;
106  fPInsBlock = 0;
107  fInsIndex = 0;
108  fZText = 0;
109  fNText = 0;
110  fNAlloc = 0;
111  fNComplete = 0;
112  fICol = 0;
113  fIPlaintext = 0;
114  fPScript = 0;
115  fIdle = 0;
116  fStyleStack = 0;
117  fParaAlignment = ALIGN_None;
118  fRowAlignment = ALIGN_None;
119  fAnchorFlags = 0;
120  fInDt = 0;
121  fInTr = 0;
122  fInTd = 0;
123  fAnchorStart = 0;
124  fFormStart = 0;
125  fFormElemStart = 0;
126  fFormElemLast = 0;
127  fLoEndPtr = 0;
128  fLoFormStart = 0;
129  fInnerList = 0;
130  ResetLayoutContext();
131  fHighlightWidth = 0;
132  fHighlightBgColorPtr = 0;
133  fHighlightColorPtr = 0;
134  for (i = 0; i < N_FONT; ++i) fAFont[i] = 0;
135  memset(fFontValid, 0, sizeof(fFontValid));
136  for (i = 0; i < N_COLOR; ++i) {
137  fApColor[i] = 0;
138  fIDark[i] = 0;
139  fILight[i] = 0;
140  }
141  fFgColor = AllocColor("black");
142  fBgColor = AllocColor("white"); //AllocColor("#c0c0c0");
143  fNewLinkColor = AllocColor(DEF_HTML_UNVISITED);
144  fOldLinkColor = AllocColor(DEF_HTML_VISITED);
145  fSelectionColor = AllocColor(DEF_HTML_SELECTION_COLOR);
146 
147  fApColor[COLOR_Normal] = fFgColor;
148  fApColor[COLOR_Visited] = fOldLinkColor;
149  fApColor[COLOR_Unvisited] = fNewLinkColor;
150  fApColor[COLOR_Selection] = fSelectionColor;
151  fApColor[COLOR_Background] = fBgColor;
152 
153  fBgImage = 0;
154 
155  SetBackgroundColor(fApColor[COLOR_Background]->fPixel);
156  SetBackgroundPixmap(0); // force usage of solid color
157 
158  fColorUsed = 0;
159 
160  for (i = 0; i < N_CACHE_GC; ++i) {
161  fAGcCache[i].fIndex = 0;
162  fAGcCache[i].fColor = 0;
163  fAGcCache[i].fFont = 0;
164  fAGcCache[i].fGc = 0;
165  }
166  fLastGC = 0;
167  fSelEndIndex =0;
168  fSelStartIndex = 0;
169  fGcNextToFree = 0;
170  fImageList = 0;
171  fZBaseHref = 0;
172  fInnerList = 0;
173  fFormPadding = 5;
174  fOverrideFonts = 0;
175  fOverrideColors = 0;
176  fHasScript = 0;
177  fHasFrames = 0;
178  fAddEndTags = 0;
179  fTableBorderMin = 0;
180  fVarind = 0;
181  fIdind = 0;
182  fInParse = 0;
183  fZGoto = 0;
184  fExts = 0;
185  fUnderlineLinks = kTRUE;
186  fExportSelection = DEF_HTML_EXPORT_SEL;
187  fTableRelief = HTML_RELIEF_RAISED;
188  fRuleRelief = HTML_RELIEF_SUNKEN;
189  fRulePadding = 5;
190  fZBase = 0;
191  fZBaseHref = 0;
192  fCursor = kPointer;
193  fMaxX = 0;
194  fMaxY = 0;
195 
196  fXMargin = fYMargin = 0; //HTML_INDENT/4;
197 
198  fFlags = RESIZE_ELEMENTS | RELAYOUT;
199 
200  fDirtyLeft = LARGE_NUMBER;
201  fDirtyRight = 0;
202  fDirtyTop = LARGE_NUMBER;
203  fDirtyBottom = 0;
204 
205 
206  fVsb->SetAccelerated();
207  fHsb->SetAccelerated();
208 
209  fLastUri = 0;
210 
211  AddInput(kExposureMask | kFocusChangeMask);
212  AddInput(kButtonPressMask | kButtonReleaseMask | kPointerMotionMask);
213 
214  fUidTable = new THashTable(100);
215 }
216 
217 ////////////////////////////////////////////////////////////////////////////////
218 /// HTML widget destructor.
219 
220 TGHtml::~TGHtml()
221 {
222  int i;
223 
224  fExiting = 1;
225  HClear();
226  for (i = 0; i < N_FONT; i++) {
227  if (fAFont[i] != 0) fClient->FreeFont(fAFont[i]);
228  }
229  if (fInsTimer) delete fInsTimer;
230  if (fIdle) delete fIdle;
231 
232  // TODO: should also free colors!
233 }
234 
235 ////////////////////////////////////////////////////////////////////////////////
236 /// Start background update.
237 
238 void TGHtml::UpdateBackgroundStart()
239 {
240  //GCValues_t gcv;
241  //unsigned int mask = GCTileStipXOrigin | GCTileStipYOrigin;
242 //
243  //gcv.ts_x_origin = -fVvisible.fX;
244  //gcv.ts_y_origin = -fVisibleStart.fY;
245  //XChangeGC(GetDisplay(), _backGC, mask, &gcv);
246 }
247 
248 ////////////////////////////////////////////////////////////////////////////////
249 /// Free system color.
250 
251 void TGHtml::FreeColor(ColorStruct_t *color)
252 {
253  gVirtualX->FreeColor(gClient->GetDefaultColormap(), color->fPixel);
254  delete color;
255 }
256 
257 ////////////////////////////////////////////////////////////////////////////////
258 /// Allocate system color by name.
259 
260 ColorStruct_t *TGHtml::AllocColor(const char *name)
261 {
262  ColorStruct_t *color = new ColorStruct_t;
263 
264  color->fPixel = 0;
265  if (gVirtualX->ParseColor(fClient->GetDefaultColormap(), name, *color)) {
266  if (!gVirtualX->AllocColor(fClient->GetDefaultColormap(), *color)) {
267  // force allocation of pixel 0
268  gVirtualX->QueryColor(fClient->GetDefaultColormap(), *color);
269  gVirtualX->AllocColor(fClient->GetDefaultColormap(), *color);
270  }
271  }
272 
273  return color;
274 }
275 
276 ////////////////////////////////////////////////////////////////////////////////
277 /// Allocate system color by value.
278 
279 ColorStruct_t *TGHtml::AllocColorByValue(ColorStruct_t *color)
280 {
281  ColorStruct_t *c = new ColorStruct_t;
282  *c = *color;
283 
284  if (!gVirtualX->AllocColor(gClient->GetDefaultColormap(), *c)) {
285  // force allocation of pixel 0
286  c->fPixel = 0;
287  gVirtualX->QueryColor(gClient->GetDefaultColormap(), *c);
288  gVirtualX->AllocColor(gClient->GetDefaultColormap(), *c);
289  }
290 
291  return c;
292 }
293 
294 ////////////////////////////////////////////////////////////////////////////////
295 /// Erase all HTML from this widget and clear the screen. This is
296 /// typically done before loading a new document.
297 
298 void TGHtml::Clear(Option_t *)
299 {
300  HClear();
301  TGView::Clear();
302  fFlags |= REDRAW_TEXT | VSCROLL | HSCROLL;
303  ScheduleRedraw();
304 }
305 
306 ////////////////////////////////////////////////////////////////////////////////
307 /// Appends (or insert at the specified position) the given HTML text to the
308 /// end of any HTML text that may have been inserted by prior calls to this
309 /// command. Then it runs the tokenizer, parser and layout engine as far as
310 /// possible with the text that is available. The display is updated
311 /// appropriately.
312 
313 int TGHtml::ParseText(char *text, const char *index)
314 {
315  SHtmlIndex_t iStart;
316  TGHtmlElement *savePtr=0;
317 
318  iStart.fP = 0;
319  iStart.fI = 0;
320 
321  fLoEndPtr = fPLast;
322 
323  if (index) {
324  int rc = GetIndex(index, &iStart.fP, &iStart.fI);
325  if (rc != 0) return kFALSE; // malformed index
326  if (iStart.fP) {
327  savePtr = iStart.fP->fPNext;
328  fPLast = iStart.fP;
329  iStart.fP->fPNext = 0;
330  }
331  }
332 
333  TokenizerAppend(text);
334 
335  if (fLoEndPtr) {
336  fFormStart = fLoFormStart;
337  if (iStart.fP && savePtr) {
338  AddStyle(fLoEndPtr);
339  fPLast->fPNext = savePtr;
340  savePtr->fPPrev = fPLast;
341  fPLast = fLoEndPtr;
342  fFlags |= REDRAW_TEXT | RELAYOUT;
343  ScheduleRedraw();
344  } else if (fLoEndPtr->fPNext) {
345  AddStyle(fLoEndPtr->fPNext);
346  }
347  } else if (fPFirst) {
348  fParaAlignment = ALIGN_None;
349  fRowAlignment = ALIGN_None;
350  fAnchorFlags = 0;
351  fInDt = 0;
352  fAnchorStart = 0;
353  fFormStart = 0;
354  fInnerList = 0;
355  fNInput = 0;
356  AddStyle(fPFirst);
357  }
358 #if 1
359  fLoEndPtr = fPLast;
360  fLoFormStart = fFormStart;
361 #endif
362 
363  fFlags |= EXTEND_LAYOUT;
364  ScheduleRedraw();
365 
366  return kTRUE;
367 }
368 
369 ////////////////////////////////////////////////////////////////////////////////
370 /// Sets relief mode of html table.
371 
372 void TGHtml::SetTableRelief(int relief)
373 {
374  if (fTableRelief != relief) {
375  fTableRelief = relief;
376  fFlags |= RELAYOUT;
377  RedrawEverything();
378  }
379 }
380 
381 ////////////////////////////////////////////////////////////////////////////////
382 /// Sets relief mode of html rule.
383 
384 void TGHtml::SetRuleRelief(int relief)
385 {
386  if (fRuleRelief != relief) {
387  fRuleRelief = relief;
388  fFlags |= RELAYOUT;
389  RedrawEverything();
390  }
391 }
392 
393 ////////////////////////////////////////////////////////////////////////////////
394 /// Set/reset html links underline.
395 
396 void TGHtml::UnderlineLinks(int onoff)
397 {
398  if (fUnderlineLinks != onoff) {
399  fUnderlineLinks = onoff;
400 // fFlags |= RESIZE_ELEMENTS | RELAYOUT;
401 // AddStyle(fPFirst);
402 
403  TGHtmlElement *p;
404  SHtmlStyle_t style = GetCurrentStyle();
405  for (p = fPFirst; p; p = p->fPNext) {
406  if (p->fType == Html_A) {
407  if (fAnchorStart) {
408  style = PopStyleStack(Html_EndA);
409  fAnchorStart = 0;
410  fAnchorFlags = 0;
411  }
412  const char *z = p->MarkupArg("href", 0);
413  if (z) {
414  style.fColor = GetLinkColor(z);
415  if (fUnderlineLinks) style.fFlags |= STY_Underline;
416  fAnchorFlags |= STY_Anchor;
417  PushStyleStack(Html_EndA, style);
418  fAnchorStart = (TGHtmlAnchor *) p;
419  }
420  } else if (p->fType == Html_EndA) {
421  if (fAnchorStart) {
422  ((TGHtmlRef *)p)->fPOther = fAnchorStart;
423  style = PopStyleStack(Html_EndA);
424  fAnchorStart = 0;
425  fAnchorFlags = 0;
426  }
427  }
428  p->fStyle.fFlags &= ~STY_Underline;
429  p->fStyle.fFlags |= (style.fFlags & STY_Underline);
430  }
431 
432  RedrawEverything();
433  }
434 }
435 
436 ////////////////////////////////////////////////////////////////////////////////
437 /// Sets base URI.
438 
439 void TGHtml::SetBaseUri(const char *uri)
440 {
441  if (fZBase) delete[] fZBase;
442  fZBase = 0;
443  if (uri) fZBase = StrDup(uri);
444 }
445 
446 ////////////////////////////////////////////////////////////////////////////////
447 /// Go to anchor position.
448 
449 int TGHtml::GotoAnchor(const char *name)
450 {
451  const char *z;
452  TGHtmlElement *p;
453 
454  for (p = fPFirst; p; p = p->fPNext) {
455  if (p->fType == Html_A) {
456  z = p->MarkupArg("name", 0);
457  if (z && strcmp(z, name) == 0) {
458  ScrollToPosition(TGLongPosition(fVisible.fX, ((TGHtmlAnchor *)p)->fY));
459  return kTRUE;
460  }
461  }
462  }
463 
464  return kFALSE;
465 }
466 
467 ////////////////////////////////////////////////////////////////////////////////
468 /// Given a string, this procedure returns a unique identifier for the
469 /// string.
470 ///
471 /// This procedure returns a pointer to a new char string corresponding to
472 /// the "string" argument. The new string has a value identical to string
473 /// (strcmp will return 0), but it's guaranteed that any other calls to this
474 /// procedure with a string equal to "string" will return exactly the same
475 /// result (i.e. can compare pointer *values* directly, without having to
476 /// call strcmp on what they point to).
477 
478 const char *TGHtml::GetUid(const char *string)
479 {
480  //int dummy;
481 
482  TObjString *obj = 0;
483  obj = (TObjString*)fUidTable->FindObject(string);
484 
485  if (!obj) {
486  obj = new TObjString(string);
487  fUidTable->Add(obj);
488  }
489 
490  return (const char *)obj->GetName();
491 }
492 
493 ////////////////////////////////////////////////////////////////////////////////
494 /// Computes virtual size of html area.
495 
496 void TGHtml::ComputeVirtualSize()
497 {
498  fVirtualSize = TGDimension(fMaxX, fMaxY);
499 }
500 
501 ////////////////////////////////////////////////////////////////////////////////
502 /// Clear the cache of GCs
503 
504 void TGHtml::ClearGcCache()
505 {
506  int i;
507 
508  for (i = 0; i < N_CACHE_GC; i++) {
509  if (fAGcCache[i].fIndex) {
510  gVirtualX->DeleteGC(fAGcCache[i].fGc);
511  fAGcCache[i].fIndex = 0;
512  }
513  }
514  fGcNextToFree = 0;
515 }
516 
517 ////////////////////////////////////////////////////////////////////////////////
518 /// Reset the main layout context in the main widget. This happens
519 /// before we redo the layout, or just before deleting the widget.
520 
521 void TGHtml::ResetLayoutContext()
522 {
523  fLayoutContext.Reset();
524 }
525 
526 ////////////////////////////////////////////////////////////////////////////////
527 /// This routine is invoked in order to redraw all or part of the HTML
528 /// widget. This might happen because the display has changed, or in
529 /// response to an expose event. In all cases, though, this routine
530 /// is called by an idle handler.
531 
532 void TGHtml::Redraw()
533 {
534  Pixmap_t pixmap; // The buffer on which to render HTML
535  int x, y, w, h; // Virtual canvas coordinates of area to draw
536  int hw; // highlight thickness
537  int clipwinH, clipwinW; // Width and height of the clipping window
538  TGHtmlBlock *pBlock; // For looping over blocks to be drawn
539  int redoSelection = 0; // kTRUE to recompute the selection
540 
541  // Don't do anything if we are in the middle of a parse.
542 
543  if (fInParse) {
544  fFlags &= ~REDRAW_PENDING;
545  return;
546  }
547 
548  // Recompute the layout, if necessary or requested.
549  //
550  // Calling LayoutDoc() is tricky because LayoutDoc() may invoke one
551  // or more user-overriden methods, and these methods could, in theory,
552  // do nasty things. So we have to take precautions:
553  //
554  // * Do not remove the REDRAW_PENDING flag until after LayoutDoc()
555  // has been called, to prevent a recursive call to Redraw().
556 
557  if ((fFlags & RESIZE_ELEMENTS) != 0 && (fFlags & STYLER_RUNNING) == 0) {
558  TGHtmlImage *pImage;
559  for (pImage = fImageList; pImage; pImage = pImage->fPNext) {
560  pImage->fPList = 0;
561  }
562  fLastSized = 0;
563  fFlags &= ~RESIZE_ELEMENTS;
564  fFlags |= RELAYOUT;
565  }
566 
567  // We used to make a distinction between RELAYOUT and EXTEND_LAYOUT.
568  // RELAYOUT would be used when the widget was resized, but the
569  // less compute-intensive EXTEND_LAYOUT would be used when new
570  // text was appended.
571  //
572  // Unfortunately, EXTEND_LAYOUT has some problem that arise when
573  // tables are used. The quick fix is to make an EXTEND_LAYOUT do
574  // a complete RELAYOUT. Someday, we need to fix EXTEND_LAYOUT so
575  // that it works right...
576 
577  if ((fFlags & (RELAYOUT | EXTEND_LAYOUT)) != 0
578  && (fFlags & STYLER_RUNNING) == 0) {
579  fNextPlaced = 0;
580  //fNInput = 0;
581  fVarId = 0;
582  fMaxX = 0;
583  fMaxY = 0;
584  ResetLayoutContext();
585  fFirstBlock = 0;
586  fLastBlock = 0;
587  redoSelection = 1;
588  fFlags &= ~RELAYOUT;
589  fFlags |= HSCROLL | VSCROLL | REDRAW_TEXT | EXTEND_LAYOUT;
590  }
591 
592  if ((fFlags & EXTEND_LAYOUT) && fPFirst != 0) {
593  LayoutDoc();
594  fFlags &= ~EXTEND_LAYOUT;
595  FormBlocks();
596  MapControls();
597  if (redoSelection && fSelBegin.fP && fSelEnd.fP) {
598  UpdateSelection(1);
599  UpdateInsert();
600  }
601  }
602  fFlags &= ~REDRAW_PENDING;
603 
604  // No need to do any actual drawing if we aren't mapped
605 
606 //// if (!IsMapped()) return;
607 
608  // Update the scrollbars.
609 
610  if ((fFlags & (HSCROLL | VSCROLL)) != 0) {
611  ComputeVirtualSize();
612  fFlags &= ~(HSCROLL | VSCROLL);
613 
614  if (fFlags & REDRAW_PENDING) return;
615  }
616 
617  // Redraw the focus highlight, if requested
618  hw = fHighlightWidth;
619  if (fFlags & REDRAW_FOCUS) {
620  if (hw > 0) {
621 #if 0
622  unsigned long color;
623 
624  if (fFlags & GOT_FOCUS) {
625  color = highlightColorPtr;
626  } else {
627  color = highlightBgColorPtr;
628  }
629  _DrawFocusHighlight(color);
630 #endif
631  }
632  fFlags &= ~REDRAW_FOCUS;
633  }
634 
635  // If the styler is in a callback, abort further processing.
636  // TODO: check this!
637 
638  if (fFlags & STYLER_RUNNING) {
639  goto earlyOut;
640  }
641 
642  MapControls();
643 
644  // Compute the virtual canvas coordinates corresponding to the
645  // dirty region of the clipping window.
646 
647  clipwinW = fCanvas->GetWidth();
648  clipwinH = fCanvas->GetHeight();
649  if (fFlags & REDRAW_TEXT) {
650  w = clipwinW;
651  h = clipwinH;
652  x = fVisible.fX;
653  y = fVisible.fY;
654  fDirtyLeft = 0;
655  fDirtyTop = 0;
656  fFlags &= ~REDRAW_TEXT;
657  } else {
658  if (fDirtyLeft < 0) fDirtyLeft = 0;
659  if (fDirtyRight > clipwinW) fDirtyRight = clipwinW;
660  if (fDirtyTop < 0) fDirtyTop = 0;
661  if (fDirtyBottom > clipwinH) fDirtyBottom = clipwinH;
662  w = fDirtyRight - fDirtyLeft;
663  h = fDirtyBottom - fDirtyTop;
664  x = fVisible.fX + fDirtyLeft;
665  y = fVisible.fY + fDirtyTop;
666  }
667 
668  // Skip the rest of the drawing process if the area to be refreshed is
669  // less than zero
670  if (w > 0 && h > 0) {
671  GContext_t gcBg;
672  TGRectangle xrec;
673  // printf("Redraw %dx%d at %d,%d\n", w, h, x, y);
674 
675  // Allocate and clear a pixmap upon which to draw
676  gcBg = GetGC(COLOR_Background, FONT_Any);
677  pixmap = gVirtualX->CreatePixmap(fCanvas->GetId(), w, h);
678  xrec.fX = 0;
679  xrec.fY = 0;
680  xrec.fW = w;
681  xrec.fH = h;
682 #if 0
683 
684 //old-- XFillRectangles(GetDisplay(), pixmap, gcBg, &xrec, 1);
685 //new-- if (fBgImage)
686 // BGDraw(fVisible.fX, fVisible.fY, w, h, fBgImage);
687 #else
688 
689  fWhiteGC.SetTileStipXOrigin(-fVisible.fX - fDirtyLeft);
690  fWhiteGC.SetTileStipYOrigin(-fVisible.fY - fDirtyTop);
691 
692  gVirtualX->FillRectangle(pixmap, fWhiteGC.GetGC(), 0, 0, w, h);
693  UpdateBackgroundStart(); // back to original
694 #endif
695 
696  // Render all visible HTML onto the pixmap
697  for (pBlock = fFirstBlock; pBlock; pBlock = pBlock->fBNext) {
698  if (pBlock->fTop <= y+h && pBlock->fBottom >= y-10 &&
699  pBlock->fLeft <= x+w && pBlock->fRight >= x-10) {
700  BlockDraw(pBlock, pixmap, x, y, w, h, pixmap);
701  }
702  }
703 
704  // Finally, copy the pixmap onto the window and delete the pixmap
705  gVirtualX->CopyArea(pixmap, fCanvas->GetId(),
706  gcBg, 0, 0, w, h, fDirtyLeft, fDirtyTop);
707  gVirtualX->Update(kFALSE);
708 
709  gVirtualX->DeletePixmap(pixmap);
710 // XFlush(GetDisplay());
711  }
712 
713  // Redraw images, if requested
714  if (fFlags & REDRAW_IMAGES) {
715  TGHtmlImage *pImage;
716  TGHtmlImageMarkup *pElem;
717  int top, bottom, left, right; // Coordinates of the clipping window
718  int imageTop; // Top edge of image
719 
720  top = fVisible.fY;
721  bottom = top + fCanvas->GetHeight();
722  left = fVisible.fX;
723  right = left + fCanvas->GetWidth();
724  for (pImage = fImageList; pImage; pImage = pImage->fPNext) {
725  for (pElem = pImage->fPList; pElem; pElem = pElem->fINext) {
726  if (pElem->fRedrawNeeded == 0) continue;
727  imageTop = pElem->fY - pElem->fAscent;
728  if (imageTop > bottom || imageTop + pElem->fH < top
729  || pElem->fX > right || pElem->fX + pElem->fW < left) continue;
730 
731  DrawImage(pElem, fCanvas->GetId(), left, top, right, bottom);
732  }
733  }
734  fFlags &= ~(REDRAW_IMAGES | ANIMATE_IMAGES);
735  }
736 
737  // Set the dirty region to the empty set.
738 earlyOut:
739  fDirtyTop = LARGE_NUMBER;
740  fDirtyLeft = LARGE_NUMBER;
741  fDirtyBottom = 0;
742  fDirtyRight = 0;
743 
744  return;
745 }
746 
747 ////////////////////////////////////////////////////////////////////////////////
748 /// Make sure that a call to the Redraw() routine has been queued.
749 
750 void TGHtml::ScheduleRedraw()
751 {
752  if ((fFlags & REDRAW_PENDING) == 0 /*&& IsMapped()*/) {
753  if (!fIdle) fIdle = new TGIdleHandler(this);
754  fFlags |= REDRAW_PENDING;
755  }
756 }
757 
758 ////////////////////////////////////////////////////////////////////////////////
759 /// Handles idle event.
760 
761 Bool_t TGHtml::HandleIdleEvent(TGIdleHandler *idle)
762 {
763  if (idle != fIdle) return kFALSE;
764  Redraw();
765  delete fIdle;
766  fIdle = NULL;
767  return kTRUE;
768 }
769 
770 ////////////////////////////////////////////////////////////////////////////////
771 /// If any part of the screen needs to be redrawn, then call this routine
772 /// with the values of a box (in window coordinates) that needs to be
773 /// redrawn. This routine will schedule an idle handler to do the redraw.
774 ///
775 /// The box coordinates are relative to the clipping window (fCanvas).
776 
777 void TGHtml::RedrawArea(int left, int top, int right, int bottom)
778 {
779  if (bottom < 0) return;
780  if (top > (int)fCanvas->GetHeight()) return;
781  if (right < 0) return;
782  if (left > (int)fCanvas->GetWidth()) return;
783  if (fDirtyTop > top) fDirtyTop = top;
784  if (fDirtyLeft > left) fDirtyLeft = left;
785  if (fDirtyBottom < bottom) fDirtyBottom = bottom;
786  if (fDirtyRight < right) fDirtyRight = right;
787  ScheduleRedraw();
788 }
789 
790 ////////////////////////////////////////////////////////////////////////////////
791 /// Draw region defined by [x,y] [w,h].
792 
793 void TGHtml::DrawRegion(Int_t x, Int_t y, UInt_t w, UInt_t h)
794 {
795  TGView::DrawRegion(x, y, w, h);
796 
797 #if 0
798  RedrawArea(x, y, x + w + 1, y + h + 1);
799 #else
800  int left = x;
801  int top = y;
802  int right = x + w + 1;
803  int bottom = y + h + 1;
804  if (bottom < 0) return;
805  if (top > (int) fCanvas->GetHeight()) return;
806  if (right < 0) return;
807  if (left > (int)fCanvas->GetWidth()) return;
808  if (fDirtyTop > top) fDirtyTop = top;
809  if (fDirtyLeft > left) fDirtyLeft = left;
810  if (fDirtyBottom < bottom) fDirtyBottom = bottom;
811  if (fDirtyRight < right) fDirtyRight = right;
812 
813  fFlags |= REDRAW_PENDING;
814  Redraw();
815 #endif
816  return;
817 }
818 
819 ////////////////////////////////////////////////////////////////////////////////
820 /// Layout html widget.
821 
822 Bool_t TGHtml::ItemLayout()
823 {
824 #if 0
825  fFlags |= RELAYOUT | VSCROLL | HSCROLL;
826  Redraw(); //RedrawEverything();
827 #else
828  fNextPlaced = 0;
829  //fNInput = 0;
830  fVarId = 0;
831  fMaxX = 0;
832  fMaxY = 0;
833  ResetLayoutContext();
834  fFirstBlock = 0;
835  fLastBlock = 0;
836  if (fPFirst != 0) {
837  LayoutDoc();
838  FormBlocks();
839  MapControls();
840  if (fSelBegin.fP && fSelEnd.fP) {
841  UpdateSelection(1);
842  UpdateInsert();
843  }
844  }
845  ComputeVirtualSize();
846  ScheduleRedraw();
847 #endif
848  return kTRUE;
849 }
850 
851 ////////////////////////////////////////////////////////////////////////////////
852 /// Redraw the TGHtmlBlock given.
853 
854 void TGHtml::RedrawBlock(TGHtmlBlock *p)
855 {
856  if (p) {
857  RedrawArea(p->fLeft - fVisible.fX, p->fTop - fVisible.fY,
858  p->fRight - fVisible.fX + 1, p->fBottom - fVisible.fY);
859  }
860 }
861 
862 ////////////////////////////////////////////////////////////////////////////////
863 /// Call this routine to force the entire widget to be redrawn.
864 
865 void TGHtml::RedrawEverything()
866 {
867  fFlags |= REDRAW_FOCUS | REDRAW_TEXT;
868  ScheduleRedraw();
869 }
870 
871 ////////////////////////////////////////////////////////////////////////////////
872 /// Call this routine to cause all of the rendered HTML at the
873 /// virtual canvas coordinate of Y and beyond to be redrawn.
874 
875 void TGHtml::RedrawText(int y)
876 {
877  int clipHeight; // Height of the clipping window
878 
879  clipHeight = fCanvas->GetHeight();
880  y -= fVisible.fY;
881  if (y < clipHeight) {
882  RedrawArea(0, y, LARGE_NUMBER, clipHeight);
883  }
884 }
885 
886 ////////////////////////////////////////////////////////////////////////////////
887 /// Erase all data from the HTML widget. Bring it back to an empty screen.
888 
889 void TGHtml::HClear()
890 {
891  int i;
892  TGHtmlElement *p, *fPNext;
893 
894  fXMargin = fYMargin = 0; //HTML_INDENT/4;
895 
896  DeleteControls();
897  for (p = fPFirst; p; p = fPNext) {
898  fPNext = p->fPNext;
899  delete p;
900  }
901  fPFirst = 0;
902  fPLast = 0;
903  fNToken = 0;
904  if (fZText) delete[] fZText;
905  fZText = 0;
906  fNText = 0;
907  fNAlloc = 0;
908  fNComplete = 0;
909  fIPlaintext = 0;
910 
911  for (i = 0; i < N_COLOR; ++i) {
912  if (fApColor[i] != 0) FreeColor(fApColor[i]);
913  fApColor[i] = 0;
914  fIDark[i] = 0;
915  fILight[i] = 0;
916  }
917 
918  if (!fExiting) {
919  fFgColor = AllocColor("black");
920  fBgColor = AllocColor("white"); //AllocColor("#c0c0c0");
921  fNewLinkColor = AllocColor(DEF_HTML_UNVISITED);
922  fOldLinkColor = AllocColor(DEF_HTML_VISITED);
923  fSelectionColor = AllocColor(DEF_HTML_SELECTION_COLOR);
924 
925  fApColor[COLOR_Normal] = fFgColor;
926  fApColor[COLOR_Visited] = fOldLinkColor;
927  fApColor[COLOR_Unvisited] = fNewLinkColor;
928  fApColor[COLOR_Selection] = fSelectionColor;
929  fApColor[COLOR_Background] = fBgColor;
930 
931  SetBackgroundColor(fApColor[COLOR_Background]->fPixel);
932  SetBackgroundPixmap(0); // use solid color
933  }
934 
935  fColorUsed = 0;
936  while (fImageList) {
937  TGHtmlImage *p2 = fImageList;
938  fImageList = p2->fPNext;
939  delete p2;
940  }
941 
942  if (fBgImage) delete fBgImage;
943  fBgImage = 0;
944 
945  while (fStyleStack) {
946  SHtmlStyleStack_t *p2 = fStyleStack;
947  fStyleStack = p2->fPNext;
948  delete p2;
949  }
950  ClearGcCache();
951  ResetLayoutContext();
952 // if (fZBase) delete[] fZBase;
953 // fZBase = 0;
954 
955  if (fZBaseHref) delete [] fZBaseHref;
956  fZBaseHref = 0;
957  fLastSized = 0;
958  fNextPlaced = 0;
959  fFirstBlock = 0;
960  fLastBlock = 0;
961  fNInput = 0;
962  fNForm = 0;
963  fVarId = 0;
964  fParaAlignment = ALIGN_None;
965  fRowAlignment = ALIGN_None;
966  fAnchorFlags = 0;
967  fInDt = 0;
968  fAnchorStart = 0;
969  fFormStart = 0;
970  fInnerList = 0;
971  fMaxX = 0;
972  fMaxY = 0;
973 #if 0 // in OXView::Clear()
974  fVisible = TGPosition(0, 0);
975  _virtualSize = TGDimension(0, 0);
976  ScrollTTGPosition(fVisible);
977 #endif
978  fPInsBlock = 0;
979  fIns.fP = 0;
980  fSelBegin.fP = 0;
981  fSelEnd.fP = 0;
982  fPSelStartBlock = 0;
983  fPSelEndBlock = 0;
984  fHasScript = 0;
985  fHasFrames = 0;
986  fLastUri = 0;
987 }
988 
989 ////////////////////////////////////////////////////////////////////////////////
990 /// Handle timer event.
991 
992 Bool_t TGHtml::HandleTimer(TTimer *t)
993 {
994  if (t == fInsTimer) {
995  if (fInsTimer) delete fInsTimer;
996  fInsTimer = NULL;
997  FlashCursor();
998  return kTRUE;
999  } else {
1000  TGHtmlImage *pImage;
1001  for (pImage = fImageList; pImage; pImage = pImage->fPNext) {
1002  if (pImage->fTimer == t) {
1003  AnimateImage(pImage);
1004  return kTRUE;
1005  }
1006  }
1007  }
1008  return kFALSE;
1009 }
1010 
1011 ////////////////////////////////////////////////////////////////////////////////
1012 /// Flash the insertion cursor.
1013 
1014 void TGHtml::FlashCursor()
1015 {
1016  if (fPInsBlock == 0 || fInsOnTime <= 0 || fInsOffTime <= 0) return;
1017  RedrawBlock(fPInsBlock);
1018  if ((fFlags & GOT_FOCUS) == 0) {
1019  fInsStatus = 0;
1020  } else if (fInsStatus) {
1021  fInsTimer = new TTimer(this, fInsOffTime);
1022  fInsStatus = 0;
1023  } else {
1024  fInsTimer = new TTimer(this, fInsOnTime);
1025  fInsStatus = 1;
1026  }
1027 }
1028 
1029 ////////////////////////////////////////////////////////////////////////////////
1030 /// Return a GC from the cache. As many as N_CACHE_GCs are kept valid
1031 /// at any one time. They are replaced using an LRU algorithm.
1032 ///
1033 /// A value of FONT_Any (-1) for the font means "don't care".
1034 
1035 GContext_t TGHtml::GetGC(int color, int font)
1036 {
1037  int i, j;
1038  GcCache_t *p = fAGcCache;
1039  GCValues_t gcValues;
1040  TGFont *xfont;
1041 
1042  // Check for an existing GC.
1043 
1044  if (color < 0 || color >= N_COLOR) color = 0;
1045  if (font < FONT_Any || font >= N_FONT) font = FONT_Default;
1046 
1047  for (i = 0; i < N_CACHE_GC; i++, p++) {
1048  if (p->fIndex == 0) continue;
1049  if ((font < 0 || p->fFont == font) && p->fColor == color) {
1050  if (p->fIndex > 1) {
1051  for (j = 0; j < N_CACHE_GC; j++) {
1052  if (fAGcCache[j].fIndex && fAGcCache[j].fIndex < p->fIndex ) {
1053  fAGcCache[j].fIndex++;
1054  }
1055  }
1056  p->fIndex = 1;
1057  }
1058  return fAGcCache[i].fGc;
1059  }
1060  }
1061 
1062  // No GC matches. Find a place to allocate a new GC.
1063 
1064  p = fAGcCache;
1065  for (i = 0; i < N_CACHE_GC; i++, p++) {
1066  if (p->fIndex == 0 || p->fIndex == N_CACHE_GC) break;
1067  }
1068  if (i >= N_CACHE_GC) { // No slot, so free one (round-robin)
1069  p = fAGcCache;
1070  for (i = 0; i < N_CACHE_GC && i < fGcNextToFree; ++i, ++p) {}
1071  fGcNextToFree = (fGcNextToFree + 1) % N_CACHE_GC;
1072  gVirtualX->DeleteGC(p->fGc);
1073  }
1074  gcValues.fForeground = fApColor[color]->fPixel;
1075  gcValues.fGraphicsExposures = kTRUE;
1076  gcValues.fMask = kGCForeground | kGCGraphicsExposures;
1077 
1078  if (font < 0) font = FONT_Default;
1079  xfont = GetFont(font);
1080 
1081  if (xfont) {
1082  gcValues.fFont = xfont->GetFontHandle();
1083  gcValues.fMask |= kGCFont;
1084  }
1085 
1086  p->fGc = gVirtualX->CreateGC(fId, &gcValues);
1087 
1088  if (p->fIndex == 0) p->fIndex = N_CACHE_GC + 1;
1089  for (j = 0; j < N_CACHE_GC; j++) {
1090  if (fAGcCache[j].fIndex && fAGcCache[j].fIndex < p->fIndex) {
1091  fAGcCache[j].fIndex++;
1092  }
1093  }
1094  p->fIndex = 1;
1095  p->fFont = font;
1096  p->fColor = color;
1097 
1098  return p->fGc;
1099 }
1100 
1101 ////////////////////////////////////////////////////////////////////////////////
1102 /// Retrieve any valid GC. The font and color don't matter since the
1103 /// GC will only be used for copying.
1104 
1105 GContext_t TGHtml::GetAnyGC()
1106 {
1107  int i;
1108  GcCache_t *p = fAGcCache;
1109 
1110  for (i = 0; i < N_CACHE_GC; i++, p++) {
1111  if (p->fIndex) return p->fGc;
1112  }
1113 
1114  return GetGC(COLOR_Normal, FONT_Default);
1115 }
1116 
1117 ////////////////////////////////////////////////////////////////////////////////
1118 /// Handle focus change event.
1119 
1120 Bool_t TGHtml::HandleFocusChange(Event_t *event)
1121 {
1122  if (event->fType == kFocusIn) {
1123  fFlags |= GOT_FOCUS | REDRAW_FOCUS;
1124  ScheduleRedraw();
1125  UpdateInsert();
1126  } else { // FocusOut
1127  fFlags &= ~GOT_FOCUS;
1128  fFlags |= REDRAW_FOCUS;
1129  ScheduleRedraw();
1130  }
1131  return kTRUE;
1132 }
1133 
1134 ////////////////////////////////////////////////////////////////////////////////
1135 /// This routine searchs for a hyperlink beneath the coordinates x,y
1136 /// and returns a pointer to the HREF for that hyperlink. The text
1137 /// is held in one of the markup argv[] fields of the <a> markup.
1138 
1139 TGHtmlInput *TGHtml::GetInputElement(int x, int y)
1140 {
1141  TGHtmlInput *p; // For looping over all controls
1142  int vx, vy, vw, vh; // Part of the virtual canvas that is visible
1143 
1144  vx = fVisible.fX;
1145  vy = fVisible.fY;
1146  vw = fCanvas->GetWidth();
1147  vh = fCanvas->GetHeight();
1148  for (p = fFirstInput; p; p = p->fINext) {
1149  if (p->fFrame == 0) continue;
1150  if (p->fY < vy + vh && p->fY + p->fH > vy &&
1151  p->fX < vx + vw && p->fX + p->fW > vx) {
1152  if ((x > p->fX) && (y > p->fY) && (x < (p->fX + p->fW)) &&
1153  (y < (p->fY + p->fH)) ) {
1154  return p;
1155  }
1156  }
1157  }
1158  return 0;
1159 }
1160 
1161 ////////////////////////////////////////////////////////////////////////////////
1162 /// Handle html input (button, checkbox, ...) event.
1163 
1164 Bool_t TGHtml::HandleHtmlInput(TGHtmlInput *pr, Event_t *event)
1165 {
1166  Window_t childdum;
1167  Event_t eventSt;
1168  eventSt.fType = event->fType;
1169  eventSt.fWindow = event->fWindow;
1170  eventSt.fTime = event->fTime;
1171  eventSt.fX = 2;
1172  eventSt.fY = 2;
1173  eventSt.fXRoot = event->fXRoot;
1174  eventSt.fYRoot = event->fYRoot;
1175  eventSt.fCode = event->fCode;
1176  eventSt.fState = event->fState;
1177  eventSt.fWidth = event->fWidth;
1178  eventSt.fHeight = event->fHeight;
1179  eventSt.fCount = event->fCount;
1180  eventSt.fSendEvent = event->fSendEvent;
1181  eventSt.fHandle = event->fHandle;
1182  eventSt.fFormat = event->fFormat;
1183  eventSt.fUser[0] = event->fUser[0];
1184  eventSt.fUser[1] = event->fUser[1];
1185  eventSt.fUser[2] = event->fUser[2];
1186  eventSt.fUser[3] = event->fUser[3];
1187  eventSt.fUser[4] = event->fUser[4];
1188  gVirtualX->TranslateCoordinates(GetId(), pr->fFrame->GetId(),
1189  event->fX, event->fY, eventSt.fX,
1190  eventSt.fY, childdum);
1191 
1192  const char *name = pr->MarkupArg("name", 0);
1193  const char *val = pr->MarkupArg("value", 0);
1194  switch (pr->fItype) {
1195  case INPUT_TYPE_Submit:
1196  case INPUT_TYPE_Button: {
1197  TGButton *b = (TGButton *) pr->fFrame;
1198  Bool_t was = !b->IsDown();
1199  b->HandleButton(&eventSt);
1200  Bool_t now = !b->IsDown();
1201  if (!was && now) {
1202  if (pr->fItype == INPUT_TYPE_Submit)
1203  SubmitClicked(val); // emit SubmitClicked
1204  else
1205  ButtonClicked(name, val); // emit ButtonClicked
1206  }
1207  break;
1208  }
1209  case INPUT_TYPE_Radio: {
1210  TGRadioButton *rb = (TGRadioButton *) pr->fFrame;
1211  Bool_t was = !rb->IsDown();
1212  rb->HandleButton(&eventSt);
1213  Bool_t now = !rb->IsDown();
1214  if ((!was && now) || (was && !now)) {
1215  HandleRadioButton(pr);
1216  RadioChanged(name, val); // emit RadioChanged
1217  }
1218  break;
1219  }
1220  case INPUT_TYPE_Checkbox: {
1221  TGCheckButton *cb = (TGCheckButton *) pr->fFrame;
1222  Bool_t was = !cb->IsDown();
1223  cb->HandleButton(&eventSt);
1224  Bool_t now = !cb->IsDown();
1225  if ((!was && now) || (was && !now))
1226  CheckToggled(name, !now, val); // emit CheckToggled
1227  break;
1228  }
1229  case INPUT_TYPE_Text:
1230  case INPUT_TYPE_Password: {
1231  TGTextEntry *te = (TGTextEntry *) pr->fFrame;
1232  te->SetFocus();
1233  break;
1234  }
1235  case INPUT_TYPE_Select: {
1236  RemoveInput(kButtonPressMask | kButtonReleaseMask | kPointerMotionMask);
1237  eventSt.fUser[0] = childdum;
1238  if (pr->fFrame->InheritsFrom("TGComboBox"))
1239  ((TGComboBox *)pr->fFrame)->HandleButton(&eventSt);
1240  else if (pr->fFrame->InheritsFrom("TGListBox"))
1241  ((TGListBox *)pr->fFrame)->HandleButton(&eventSt);
1242  InputSelected(name, val); // emit InputSelected
1243  AddInput(kButtonPressMask | kButtonReleaseMask | kPointerMotionMask);
1244  break;
1245  }
1246  default:
1247  break;
1248  }
1249  return kTRUE;
1250 }
1251 
1252 ////////////////////////////////////////////////////////////////////////////////
1253 /// Handle radio button event.
1254 
1255 Bool_t TGHtml::HandleRadioButton(TGHtmlInput *p)
1256 {
1257  TGHtmlInput *pr;
1258  for (pr = fFirstInput; pr; pr = pr->fINext) {
1259  if ((pr->fPForm == p->fPForm) && (pr->fItype == INPUT_TYPE_Radio)) {
1260  if (pr != p) {
1261  if (strcmp(pr->MarkupArg("name", ""), p->MarkupArg("name", "")) == 0) {
1262  ((TGRadioButton *)pr->fFrame)->SetState(kButtonUp);
1263  }
1264  }
1265  }
1266  }
1267 
1268  return kTRUE;
1269 }
1270 
1271 ////////////////////////////////////////////////////////////////////////////////
1272 /// Emit ButtonClicked() signal.
1273 
1274 void TGHtml::ButtonClicked(const char *name, const char *val)
1275 {
1276  Long_t args[2];
1277 
1278  args[0] = (Long_t)name;
1279  args[1] = (Long_t)val;
1280 
1281  Emit("ButtonClicked(char*,char*)", args);
1282 }
1283 
1284 ////////////////////////////////////////////////////////////////////////////////
1285 /// Emit CheckToggled() signal.
1286 
1287 void TGHtml::CheckToggled(const char *name, Bool_t on, const char *val)
1288 {
1289  Long_t args[3];
1290 
1291  args[0] = (Long_t)name;
1292  args[1] = on;
1293  args[2] = (Long_t)val;
1294 
1295  Emit("CheckToggled(char*,Bool_t,char*)", args);
1296 }
1297 
1298 ////////////////////////////////////////////////////////////////////////////////
1299 /// Emit RadioChanged() signal.
1300 
1301 void TGHtml::RadioChanged(const char *name, const char *val)
1302 {
1303  Long_t args[2];
1304 
1305  args[0] = (Long_t)name;
1306  args[1] = (Long_t)val;
1307 
1308  Emit("RadioChanged(char*,char*)", args);
1309 }
1310 
1311 ////////////////////////////////////////////////////////////////////////////////
1312 /// Emit Selected() signal.
1313 
1314 void TGHtml::InputSelected(const char *name, const char *val)
1315 {
1316  Long_t args[2];
1317 
1318  args[0] = (Long_t)name;
1319  args[1] = (Long_t)val;
1320 
1321  Emit("InputSelected(char*,char*)", args);
1322 }
1323 
1324 ////////////////////////////////////////////////////////////////////////////////
1325 /// Emit SubmitClicked() signal.
1326 
1327 void TGHtml::SubmitClicked(const char *val)
1328 {
1329  Emit("SubmitClicked(char*)", val);
1330 }
1331 
1332 ////////////////////////////////////////////////////////////////////////////////
1333 /// Handle mouse button event.
1334 
1335 Bool_t TGHtml::HandleButton(Event_t *event)
1336 {
1337  int amount, ch;
1338 
1339  ch = fCanvas->GetHeight();
1340  amount = fScrollVal.fY * TMath::Max(ch/6, 1);
1341 
1342  int ix = event->fX + fVisible.fX;
1343  int iy = event->fY + fVisible.fY;
1344  TGHtmlInput *pr = GetInputElement(ix, iy);
1345  if (pr) {
1346  HandleHtmlInput(pr, event);
1347  }
1348  if ((event->fType == kButtonPress) && (event->fCode == kButton1)) {
1349  int x = event->fX + fVisible.fX;
1350  int y = event->fY + fVisible.fY;
1351  const char *uri = GetHref(x, y);
1352 
1353 #if 0 // insertion cursor test
1354  char ix[20];
1355  sprintf(ix, "begin");
1356  SetInsert(ix);
1357 #endif
1358 
1359  if (uri) {
1360  uri = ResolveUri(uri);
1361  if (uri) {
1362  MouseDown(uri);
1363  //!!delete[] uri;
1364  }
1365  }
1366  } else if (event->fCode == kButton4) {
1367  ScrollToPosition(TGLongPosition(fVisible.fX, fVisible.fY / fScrollVal.fY - amount));
1368  } else if (event->fCode == kButton5) {
1369  ScrollToPosition(TGLongPosition(fVisible.fX, fVisible.fY / fScrollVal.fY + amount));
1370  } else {
1371  return TGView::HandleButton(event);
1372  }
1373  return kTRUE;
1374 }
1375 
1376 ////////////////////////////////////////////////////////////////////////////////
1377 /// handle mouse motion events
1378 
1379 Bool_t TGHtml::HandleMotion(Event_t *event)
1380 {
1381  int x = event->fX + fVisible.fX;
1382  int y = event->fY + fVisible.fY;
1383  const char *uri = GetHref(x, y);
1384 
1385  if (uri) {
1386  gVirtualX->SetCursor(fId, gVirtualX->CreateCursor(kHand));
1387  } else {
1388  gVirtualX->SetCursor(fId, gVirtualX->CreateCursor(kPointer));
1389  }
1390 
1391  if (uri != fLastUri) {
1392  fLastUri = uri;
1393  if (uri) uri = ResolveUri(uri);
1394  MouseOver(uri);
1395  //!!if (uri) delete [] uri;
1396  }
1397 
1398  return kTRUE;
1399 }
1400 
1401 ////////////////////////////////////////////////////////////////////////////////
1402 /// The rendering and layout routines should call this routine in order to
1403 /// get a font structure. The iFont parameter specifies which of the N_FONT
1404 /// fonts should be obtained. The font is allocated if necessary.
1405 
1406 TGFont *TGHtml::GetFont(int iFont)
1407 {
1408  TGFont *toFree = 0;
1409 
1410  if (iFont < 0) iFont = 0;
1411  if (iFont >= N_FONT) { iFont = N_FONT - 1; CANT_HAPPEN; }
1412 
1413  // If the font has previously been allocated, but the "fontValid" bitmap
1414  // shows it is no longer valid, then mark it for freeing later. We use
1415  // a policy of allocate-before-free because xclass' font cache operates
1416  // much more efficiently that way.
1417 
1418  if (!FontIsValid(iFont) && fAFont[iFont] != 0) {
1419  toFree = fAFont[iFont];
1420  fAFont[iFont] = 0;
1421  }
1422 
1423  // If we need to allocate a font, first construct the font name then
1424  // allocate it.
1425 
1426  if (fAFont[iFont] == 0) {
1427  char name[200]; // Name of the font
1428  const char *familyStr = "";
1429  int iFamily;
1430  int iSize;
1431  int size;
1432 
1433  iFamily = FontFamily(iFont) >> 3;
1434  iSize = FontSize(iFont) + 1;
1435 
1436  switch (iFamily) {
1437 //#define TIMES
1438 #ifdef TIMES
1439  case 0: familyStr = "times -%d"; break;
1440  case 1: familyStr = "times -%d bold"; break;
1441  case 2: familyStr = "times -%d italic"; break;
1442  case 3: familyStr = "times -%d bold italic"; break;
1443  case 4: familyStr = "courier -%d"; break;
1444  case 5: familyStr = "courier -%d bold"; break;
1445  case 6: familyStr = "courier -%d italic"; break;
1446  case 7: familyStr = "courier -%d bold italic"; break;
1447  default: familyStr = "times -16"; CANT_HAPPEN;
1448 #else
1449  case 0: familyStr = "helvetica -%d"; break;
1450  case 1: familyStr = "helvetica -%d bold"; break;
1451  case 2: familyStr = "helvetica -%d italic"; break;
1452  case 3: familyStr = "helvetica -%d bold italic"; break;
1453  case 4: familyStr = "courier -%d"; break;
1454  case 5: familyStr = "courier -%d bold"; break;
1455  case 6: familyStr = "courier -%d italic"; break;
1456  case 7: familyStr = "courier -%d bold italic"; break;
1457  default: familyStr = "helvetica -14"; CANT_HAPPEN;
1458 #endif
1459  }
1460 #if 0
1461  switch (iSize) {
1462  case 1: size = 6+finc/*8*/; break;
1463  case 2: size = 10+finc/*10*/; break;
1464  case 3: size = 12+finc/*12*/; break;
1465  case 4: size = 14+finc/*14*/; break;
1466  case 5: size = 20+finc/*16*/; break;
1467  case 6: size = 24+finc/*18*/; break;
1468  case 7: size = 30+finc/*24*/; break;
1469  default: size = 14+finc/*14*/; CANT_HAPPEN;
1470  }
1471 #else
1472  switch (iSize) {
1473  case 1: size = 8; break;
1474  case 2: size = 10; break;
1475  case 3: size = 12; break;
1476  case 4: size = 14; break;
1477  case 5: size = 16; break;
1478  case 6: size = 18; break;
1479  case 7: size = 24; break;
1480  default: size = 14; CANT_HAPPEN;
1481  }
1482 #endif
1483 #ifdef TIMES
1484  if (iFamily < 4) size += 2;
1485 #endif
1486 
1487  snprintf(name, 199, familyStr, size);
1488 
1489  // Get the named font
1490  fAFont[iFont] = fClient->GetFont(name);
1491 
1492  if (fAFont[iFont] == 0) {
1493  fprintf(stderr, "TGHtml: could not get font \"%s\", trying fixed\n",
1494  name);
1495  fAFont[iFont] = fClient->GetFont("fixed");
1496  }
1497  if (fAFont[iFont] == 0 ){
1498  fprintf(stderr, "TGHtml: could not get font \"fixed\", trying "
1499  "\"helvetica -12\"\n");
1500  fAFont[iFont] = fClient->GetFont("helvetica -12");
1501  }
1502  FontSetValid(iFont);
1503  }
1504 
1505  // Free the expired font, if any.
1506 
1507  if (toFree) fClient->FreeFont(toFree);
1508 
1509  return fAFont[iFont];
1510 }
1511 
1512 ////////////////////////////////////////////////////////////////////////////////
1513 /// Only support rect and circles for now
1514 
1515 int TGHtml::InArea(TGHtmlMapArea *p, int left, int top, int x, int y)
1516 {
1517  int *ip = p->fCoords;
1518  if (!ip) return 0;
1519 
1520  if (p->fMType == HTML_MAP_RECT) {
1521  return ((left + ip[0]) <= x && (left + ip[2]) >= x &&
1522  (top + ip[1]) <= y && (top + ip[3]) >= y);
1523  } else if (p->fMType == HTML_MAP_CIRCLE) {
1524  int dx = left + ip[0] - x;
1525  int dy = top + ip[1] - y;
1526  return (dx * dx + dy * dy <= ip[2] * ip[2]);
1527  }
1528  return 0;
1529 }
1530 
1531 ////////////////////////////////////////////////////////////////////////////////
1532 /// Returns html map element.
1533 
1534 TGHtmlElement *TGHtml::GetMap(const char *name)
1535 {
1536  TGHtmlElement *p = fPFirst;
1537  const char *z, *zb;
1538 
1539  while (p) {
1540  if (p->fType == Html_MAP) {
1541  z = p->MarkupArg("name", 0);
1542  zb = p->MarkupArg("shape", 0);
1543  if (zb && *zb != 'r') return 0;
1544  if (z && !strcmp(z, name)) return p;
1545  }
1546  p = p->fPNext;
1547  }
1548  return 0;
1549 }
1550 
1551 ////////////////////////////////////////////////////////////////////////////////
1552 /// Compute the squared distance between two colors
1553 
1554 float TGHtml::ColorDistance(ColorStruct_t *pA, ColorStruct_t *pB)
1555 {
1556  float x, y, z;
1557 
1558  x = 0.30 * (pA->fRed - pB->fRed);
1559  y = 0.61 * (pA->fGreen - pB->fGreen);
1560  z = 0.11 * (pA->fBlue - pB->fBlue);
1561 
1562  return x*x + y*y + z*z;
1563 }
1564 
1565 ////////////////////////////////////////////////////////////////////////////////
1566 /// This routine returns an index between 0 and N_COLOR-1 which indicates
1567 /// which ColorStruct_t structure in the fApColor[] array should be used to describe
1568 /// the color specified by the given name.
1569 
1570 int TGHtml::GetColorByName(const char *zColor)
1571 {
1572  ColorStruct_t *pNew;
1573  int iColor;
1574  const char *name; // unique!
1575  int i, n;
1576  char zAltColor[16];
1577 
1578  // Netscape accepts color names that are just HEX values, without
1579  // the # up front. This isn't valid HTML, but we support it for
1580  // compatibility.
1581 
1582  n = strlen(zColor);
1583  if (n == 6 || n == 3 || n == 9 || n == 12) {
1584  for (i = 0; i < n; i++) {
1585  if (!isxdigit(zColor[i])) break;
1586  }
1587  if (i == n) {
1588  snprintf(zAltColor, 15, "#%s", zColor);
1589  } else {
1590  strlcpy(zAltColor, zColor, sizeof(zAltColor));
1591  }
1592  name = GetUid(zAltColor);
1593  } else {
1594  name = GetUid(zColor);
1595  }
1596 
1597  pNew = AllocColor(name);
1598  if (pNew == 0) {
1599  return 0; // Color 0 is always the default
1600  }
1601 
1602  iColor = GetColorByValue(pNew);
1603  FreeColor(pNew);
1604 
1605  return iColor;
1606 }
1607 
1608 
1609 // Macros used in the computation of appropriate shadow colors.
1610 
1611 #define MAX_COLOR 65535
1612 #define MAX(A,B) ((A)<(B)?(B):(A))
1613 #define MIN(A,B) ((A)<(B)?(A):(B))
1614 
1615 ////////////////////////////////////////////////////////////////////////////////
1616 /// Check to see if the given color is too dark to be easily distinguished
1617 /// from black.
1618 
1619 int TGHtml::IsDarkColor(ColorStruct_t *p)
1620 {
1621  float x, y, z;
1622 
1623  x = 0.50 * p->fRed;
1624  y = 1.00 * p->fGreen;
1625  z = 0.28 * p->fBlue;
1626  return (x*x + y*y + z*z) < (0.05 * MAX_COLOR * MAX_COLOR);
1627 }
1628 
1629 ////////////////////////////////////////////////////////////////////////////////
1630 /// Given that the background color is iBgColor, figure out an
1631 /// appropriate color for the dark part of a 3D shadow.
1632 
1633 int TGHtml::GetDarkShadowColor(int iBgColor)
1634 {
1635  if (fIDark[iBgColor] == 0) {
1636  ColorStruct_t *pRef, val;
1637  val.fMask = kDoRed | kDoGreen | kDoBlue;
1638  val.fPixel = 0;
1639  pRef = fApColor[iBgColor];
1640  if (IsDarkColor(pRef)) {
1641  int t1, t2;
1642  t1 = (int) MIN(MAX_COLOR, pRef->fRed * 1.2);
1643  t2 = (pRef->fRed * 3 + MAX_COLOR) / 4;
1644  val.fRed = MAX(t1, t2);
1645  t1 = (int) MIN(MAX_COLOR, pRef->fGreen * 1.2);
1646  t2 = (pRef->fGreen * 3 + MAX_COLOR) / 4;
1647  val.fGreen = MAX(t1, t2);
1648  t1 = (int) MIN(MAX_COLOR, pRef->fBlue * 1.2);
1649  t2 = (pRef->fBlue * 3 + MAX_COLOR) / 4;
1650  val.fBlue = MAX(t1, t2);
1651  } else {
1652  val.fRed = (unsigned short) (pRef->fRed * 0.6);
1653  val.fGreen = (unsigned short) (pRef->fGreen * 0.6);
1654  val.fBlue = (unsigned short) (pRef->fBlue * 0.6);
1655  }
1656  fIDark[iBgColor] = GetColorByValue(&val) + 1;
1657  }
1658 
1659  return fIDark[iBgColor] - 1;
1660 }
1661 
1662 ////////////////////////////////////////////////////////////////////////////////
1663 /// Check to see if the given color is too light to be easily distinguished
1664 /// from white.
1665 
1666 int TGHtml::IsLightColor(ColorStruct_t *p)
1667 {
1668  return p->fGreen >= 0.85 * MAX_COLOR;
1669 }
1670 
1671 ////////////////////////////////////////////////////////////////////////////////
1672 /// Given that the background color is iBgColor, figure out an
1673 /// appropriate color for the bright part of the 3D shadow.
1674 
1675 int TGHtml::GetLightShadowColor(int iBgColor)
1676 {
1677  if (fILight[iBgColor] == 0) {
1678  ColorStruct_t *pRef, val;
1679  val.fMask = kDoRed | kDoGreen | kDoBlue;
1680  val.fPixel = 0;
1681  pRef = fApColor[iBgColor];
1682  if (IsLightColor(pRef)) {
1683  val.fRed = (unsigned short) (pRef->fRed * 0.9);
1684  val.fGreen = (unsigned short) (pRef->fGreen * 0.9);
1685  val.fBlue = (unsigned short) (pRef->fBlue * 0.9);
1686  } else {
1687  int t1, t2;
1688  t1 = (int) MIN(MAX_COLOR, pRef->fGreen * 1.4);
1689  t2 = (pRef->fGreen + MAX_COLOR) / 2;
1690  val.fGreen = MAX(t1, t2);
1691  t1 = (int) MIN(MAX_COLOR, pRef->fRed * 1.4);
1692  t2 = (pRef->fRed + MAX_COLOR) / 2;
1693  val.fRed = MAX(t1, t2);
1694  t1 = (int) MIN(MAX_COLOR, pRef->fBlue * 1.4);
1695  t2 = (pRef->fBlue + MAX_COLOR) / 2;
1696  val.fBlue = MAX(t1, t2);
1697  }
1698  fILight[iBgColor] = GetColorByValue(&val) + 1;
1699  }
1700 
1701  return fILight[iBgColor] - 1;
1702 }
1703 
1704 ////////////////////////////////////////////////////////////////////////////////
1705 /// Find a color integer for the color whose color components
1706 /// are given by pRef.
1707 
1708 int TGHtml::GetColorByValue(ColorStruct_t *pRef)
1709 {
1710  int i;
1711  float dist;
1712  float closestDist;
1713  int closest;
1714  int r, g, b;
1715 # define COLOR_MASK 0xf800
1716 
1717  // Search for an exact match
1718  r = pRef->fRed & COLOR_MASK;
1719  g = pRef->fGreen & COLOR_MASK;
1720  b = pRef->fBlue & COLOR_MASK;
1721  for (i = 0; i < N_COLOR; i++) {
1722  ColorStruct_t *p = fApColor[i];
1723  if (p &&
1724  ((p->fRed & COLOR_MASK) == r) &&
1725  ((p->fGreen & COLOR_MASK) == g) &&
1726  ((p->fBlue & COLOR_MASK) == b)) {
1727  fColorUsed |= (1<<i);
1728  return i;
1729  }
1730  }
1731 
1732  // No exact matches. Look for a completely unused slot
1733  for (i = N_PREDEFINED_COLOR; i < N_COLOR; i++) {
1734  if (fApColor[i] == 0) {
1735  fApColor[i] = AllocColorByValue(pRef);
1736  fColorUsed |= (1<<i);
1737  return i;
1738  }
1739  }
1740 
1741  // No empty slots. Look for a slot that contains a color that
1742  // isn't currently in use.
1743  for (i = N_PREDEFINED_COLOR; i < N_COLOR; i++) {
1744  if (((fColorUsed >> i) & 1) == 0) {
1745  FreeColor(fApColor[i]);
1746  fApColor[i] = AllocColorByValue(pRef);
1747  fColorUsed |= (1<<i);
1748  return i;
1749  }
1750  }
1751 
1752  // Ok, find the existing color that is closest to the color requested
1753  // and use it.
1754  closest = 0;
1755  closestDist = ColorDistance(pRef, fApColor[0]);
1756  for (i = 1; i < N_COLOR; i++) {
1757  dist = ColorDistance(pRef, fApColor[i]);
1758  if (dist < closestDist) {
1759  closestDist = dist;
1760  closest = i;
1761  }
1762  }
1763 
1764  return closest;
1765 }
1766 
1767 ////////////////////////////////////////////////////////////////////////////////
1768 /// This routine searchs for a hyperlink beneath the coordinates x,y
1769 /// and returns a pointer to the HREF for that hyperlink. The text
1770 /// is held in one of the markup argv[] fields of the <a> markup.
1771 
1772 const char *TGHtml::GetHref(int x, int y, const char **target)
1773 {
1774  TGHtmlBlock *pBlock;
1775  TGHtmlElement *pElem;
1776 
1777  for (pBlock = fFirstBlock; pBlock; pBlock = pBlock->fBNext) {
1778  if (pBlock->fTop > y || pBlock->fBottom < y ||
1779  pBlock->fLeft > x || pBlock->fRight < x) continue;
1780  pElem = pBlock->fPNext;
1781  if (pElem->fType == Html_IMG) {
1782  TGHtmlImageMarkup *image = (TGHtmlImageMarkup *) pElem;
1783  if (image->fPMap) {
1784  pElem = image->fPMap->fPNext;
1785  while (pElem && pElem->fType != Html_EndMAP) {
1786  if (pElem->fType == Html_AREA) {
1787  if (InArea((TGHtmlMapArea *) pElem, pBlock->fLeft, pBlock->fTop, x, y)) {
1788  if (target) *target = pElem->MarkupArg("target", 0);
1789  return pElem->MarkupArg("href", 0);
1790  }
1791  }
1792  pElem = pElem->fPNext;
1793  }
1794  continue;
1795  }
1796  }
1797  if ((pElem->fStyle.fFlags & STY_Anchor) == 0) continue;
1798  switch (pElem->fType) {
1799  case Html_Text:
1800  case Html_Space:
1801  case Html_IMG:
1802  while (pElem && pElem->fType != Html_A) pElem = pElem->fPPrev;
1803  if (pElem == 0 || pElem->fType != Html_A) break;
1804  if (target) *target = pElem->MarkupArg("target", 0);
1805  return pElem->MarkupArg("href", 0);
1806 
1807  default:
1808  break;
1809  }
1810  }
1811 
1812  return 0;
1813 }
1814 
1815 ////////////////////////////////////////////////////////////////////////////////
1816 /// Return coordinates of item
1817 
1818 int TGHtml::ElementCoords(TGHtmlElement *p, int /*i*/, int pct, int *coords)
1819 {
1820  TGHtmlBlock *pBlock;
1821 
1822  while (p && p->fType != Html_Block) p = p->fPPrev;
1823  if (!p) return 1;
1824 
1825  pBlock = (TGHtmlBlock *) p;
1826  if (pct) {
1827  TGHtmlElement *pEnd = fPLast;
1828  TGHtmlBlock *pb2;
1829  while (pEnd && pEnd->fType != Html_Block) pEnd = pEnd->fPPrev;
1830  pb2 = (TGHtmlBlock *) pEnd;
1831 #define HGCo(dir) (pb2 && pb2->dir) ? pBlock->dir * 100 / pb2->dir : 0
1832  coords[0] = HGCo(fLeft);
1833  coords[1] = HGCo(fTop);
1834  coords[3] = HGCo(fRight);
1835  coords[4] = HGCo(fBottom);
1836  } else {
1837  coords[0] = pBlock->fLeft;
1838  coords[1] = pBlock->fTop;
1839  coords[2] = pBlock->fRight;
1840  coords[3] = pBlock->fBottom;
1841  }
1842  return 0;
1843 }
1844 
1845 ////////////////////////////////////////////////////////////////////////////////
1846 /// Returns html element matching attribute name and value.
1847 
1848 TGHtmlElement *TGHtml::AttrElem(const char *name, char *value)
1849 {
1850  TGHtmlElement *p;
1851  const char *z;
1852 
1853  for (p = fPFirst; p; p = p->fPNext) {
1854  if (p->fType != Html_A) continue;
1855  z = p->MarkupArg(name, 0);
1856  if (z && (strcmp(z, value) == 0)) return p;
1857  }
1858  return 0;
1859 }
1860 
1861 ////////////////////////////////////////////////////////////////////////////////
1862 /// Given the selection end-points in fSelBegin and fSelEnd, recompute
1863 /// pSelBeginBlock and fPSelEndBlock, then call UpdateSelectionDisplay()
1864 /// to update the display.
1865 ///
1866 /// This routine should be called whenever the selection changes or
1867 /// whenever the set of TGHtmlBlock structures change.
1868 
1869 void TGHtml::UpdateSelection(int forceUpdate)
1870 {
1871  TGHtmlBlock *pBlock;
1872  int index;
1873  int needUpdate = forceUpdate;
1874  int temp;
1875 
1876  if (fSelEnd.fP == 0) fSelBegin.fP = 0;
1877 
1878  IndexToBlockIndex(fSelBegin, &pBlock, &index);
1879  if (needUpdate || pBlock != fPSelStartBlock) {
1880  needUpdate = 1;
1881  RedrawBlock(fPSelStartBlock);
1882  fPSelStartBlock = pBlock;
1883  fSelStartIndex = index;
1884  } else if (index != fSelStartIndex) {
1885  RedrawBlock(pBlock);
1886  fSelStartIndex = index;
1887  }
1888 
1889  if (fSelBegin.fP == 0) fSelEnd.fP = 0;
1890 
1891  IndexToBlockIndex(fSelEnd, &pBlock, &index);
1892  if (needUpdate || pBlock != fPSelEndBlock) {
1893  needUpdate = 1;
1894  RedrawBlock(fPSelEndBlock);
1895  fPSelEndBlock = pBlock;
1896  fSelEndIndex = index;
1897  } else if (index != fSelEndIndex) {
1898  RedrawBlock(pBlock);
1899  fSelEndIndex = index;
1900  }
1901 
1902  if (fPSelStartBlock && fPSelStartBlock == fPSelEndBlock &&
1903  fSelStartIndex > fSelEndIndex) {
1904  temp = fSelStartIndex;
1905  fSelStartIndex = fSelEndIndex;
1906  fSelEndIndex = temp;
1907  }
1908 
1909  if (needUpdate) {
1910  fFlags |= ANIMATE_IMAGES;
1911  UpdateSelectionDisplay();
1912  }
1913 }
1914 
1915 ////////////////////////////////////////////////////////////////////////////////
1916 /// The fPSelStartBlock and fPSelEndBlock values have been changed.
1917 /// This routine's job is to loop over all TGHtmlBlocks and either
1918 /// set or clear the HTML_Selected bits in the .fFlags field
1919 /// as appropriate. For every TGHtmlBlock where the bit changes,
1920 /// mark that block for redrawing.
1921 
1922 void TGHtml::UpdateSelectionDisplay()
1923 {
1924  int selected = 0;
1925  SHtmlIndex_t tempIndex;
1926  TGHtmlBlock *pTempBlock;
1927  int temp;
1928  TGHtmlBlock *p;
1929 
1930  for (p = fFirstBlock; p; p = p->fBNext) {
1931  if (p == fPSelStartBlock) {
1932  selected = 1;
1933  RedrawBlock(p);
1934  } else if (!selected && p == fPSelEndBlock) {
1935  selected = 1;
1936  tempIndex = fSelBegin;
1937  fSelBegin = fSelEnd;
1938  fSelEnd = tempIndex;
1939  pTempBlock = fPSelStartBlock;
1940  fPSelStartBlock = fPSelEndBlock;
1941  fPSelEndBlock = pTempBlock;
1942  temp = fSelStartIndex;
1943  fSelStartIndex = fSelEndIndex;
1944  fSelEndIndex = temp;
1945  RedrawBlock(p);
1946  }
1947  if (p->fFlags & HTML_Selected) {
1948  if (!selected) {
1949  p->fFlags &= ~HTML_Selected;
1950  RedrawBlock(p);
1951  }
1952  } else {
1953  if (selected) {
1954  p->fFlags |= HTML_Selected;
1955  RedrawBlock(p);
1956  }
1957  }
1958  if (p == fPSelEndBlock) {
1959  selected = 0;
1960  RedrawBlock(p);
1961  }
1962  }
1963 }
1964 
1965 ////////////////////////////////////////////////////////////////////////////////
1966 /// Clear selection.
1967 
1968 void TGHtml::LostSelection()
1969 {
1970  if (fExportSelection) {
1971  // clear selection
1972  fPSelStartBlock = 0;
1973  fPSelEndBlock = 0;
1974  fSelBegin.fP = 0;
1975  fSelEnd.fP = 0;
1976  UpdateSelectionDisplay();
1977  }
1978 }
1979 
1980 ////////////////////////////////////////////////////////////////////////////////
1981 /// Set selection.
1982 
1983 int TGHtml::SelectionSet(const char *startIx, const char *endIx)
1984 {
1985  SHtmlIndex_t sBegin, sEnd;
1986  int bi, ei;
1987 
1988  if (GetIndex(startIx, &sBegin.fP, &sBegin.fI)) {
1989  // malformed start index
1990  return kFALSE;
1991  }
1992 
1993  if (GetIndex(endIx, &sEnd.fP, &sEnd.fI)) {
1994  // malformed end index
1995  return kFALSE;
1996  }
1997 
1998  bi = TokenNumber(sBegin.fP);
1999  ei = TokenNumber(sEnd.fP);
2000 
2001  if (!(sBegin.fP && sEnd.fP)) return kTRUE;
2002 
2003  if (bi < ei || (bi == ei && sBegin.fI <= sEnd.fI)) {
2004  fSelBegin = sBegin;
2005  fSelEnd = sEnd;
2006  } else {
2007  fSelBegin = sEnd;
2008  fSelEnd = sBegin;
2009  }
2010 
2011  UpdateSelection(0);
2012  if (fExportSelection) {
2013  // TODO:
2014  // get selection ownership ... fId, XA_PRIMARY
2015  // selection lost handler must directly call LostSelection()
2016  }
2017 
2018  return kTRUE;
2019 }
2020 
2021 ////////////////////////////////////////////////////////////////////////////////
2022 /// Recompute the position of the insertion cursor based on the
2023 /// position in fIns.
2024 
2025 void TGHtml::UpdateInsert()
2026 {
2027  IndexToBlockIndex(fIns, &fPInsBlock, &fInsIndex);
2028  RedrawBlock(fPInsBlock);
2029  if (fInsTimer == 0) {
2030  fInsStatus = 0;
2031  FlashCursor();
2032  }
2033 }
2034 
2035 ////////////////////////////////////////////////////////////////////////////////
2036 /// Set the position of the insertion cursor.
2037 
2038 int TGHtml::SetInsert(const char *insIx)
2039 {
2040  SHtmlIndex_t i;
2041 
2042  if (!insIx) {
2043  RedrawBlock(fPInsBlock);
2044  fInsStatus = 0;
2045  fPInsBlock = 0;
2046  fIns.fP = 0;
2047  } else {
2048  if (GetIndex(insIx, &i.fP, &i.fI)) {
2049  // malformed index
2050  return kFALSE;
2051  }
2052  RedrawBlock(fPInsBlock);
2053  fIns = i;
2054  UpdateInsert();
2055  }
2056 
2057  return kTRUE;
2058 }
2059 
2060 ////////////////////////////////////////////////////////////////////////////////
2061 /// Save a html widget as a C++ statement(s) on output stream out.
2062 
2063 void TGHtml::SavePrimitive(std::ostream &out, Option_t *option /*= ""*/)
2064 {
2065  out << " TGHtml *";
2066  out << GetName() << " = new TGHtml(" << fParent->GetName()
2067  << "," << GetWidth() << "," << GetHeight()
2068  << ");"<< std::endl;
2069  if (option && strstr(option, "keep_names"))
2070  out << " " << GetName() << "->SetName(\"" << GetName() << "\");" << std::endl;
2071 
2072  if (fCanvas->GetBackground() != TGFrame::GetWhitePixel()) {
2073  out << " " << GetName() << "->ChangeBackground(" << fCanvas->GetBackground() << ");" << std::endl;
2074  }
2075 
2076  TString fn;
2077  TGText txt(GetText());
2078  fn.Form("Html%s.htm", GetName()+5);
2079  txt.Save(fn.Data());
2080  out << " " << "FILE *f = fopen(\"" << fn.Data() << "\", \"r\");" << std::endl;
2081  out << " " << "if (f) {" << std::endl;
2082  out << " " << GetName() << "->Clear();" << std::endl;
2083  out << " " << GetName() << "->Layout();" << std::endl;
2084  out << " " << GetName() << "->SetBaseUri(\"\");" << std::endl;
2085  out << " " << "char *buf = (char *)calloc(4096, sizeof(char));" << std::endl;
2086  out << " " << "while (fgets(buf, 4096, f)) {" << std::endl;
2087  out << " " << GetName() << "->ParseText(buf);" << std::endl;
2088  out << " " << "}" << std::endl;
2089  out << " " << "free(buf);" << std::endl;
2090  out << " " << "fclose(f);" << std::endl;
2091  out << " " << "}" << std::endl;
2092  out << " " << GetName() << "->Layout();" << std::endl;
2093 }