Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGLAnnotation.cxx
Go to the documentation of this file.
1 // @(#)root/gl:$Id$
2 // Author: Matevz and Alja Tadel 20/02/2009
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers. *
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 #include "TGLAnnotation.h"
13 
14 #include "TGLIncludes.h"
15 #include "TROOT.h"
16 #include "TColor.h"
17 #include "TGLUtil.h"
18 #include "TGLCamera.h"
19 #include "TGLRnrCtx.h"
20 #include "TGLSelectRecord.h"
21 #include "TGLViewerBase.h"
22 #include "TObjString.h"
23 #include "TGFrame.h"
24 #include "TGTextEdit.h"
25 #include "TGButton.h"
26 #include "TGLViewer.h"
27 
28 #include "TMath.h"
29 
30 #include <KeySymbols.h>
31 
32 /** \class TGLAnnotation
33 \ingroup opengl
34 GL-overlay annotation.
35 */
36 
37 ClassImp(TGLAnnotation);
38 
39 Color_t TGLAnnotation::fgBackColor = kAzure + 10;
40 Color_t TGLAnnotation::fgTextColor = kOrange;
41 
42 ////////////////////////////////////////////////////////////////////////////////
43 
44 TGLAnnotation::TGLAnnotation(TGLViewerBase *parent, const char *text, Float_t posx, Float_t posy) :
45  TGLOverlayElement(TGLOverlayElement::kAnnotation),
46 
47  fPosX(posx), fPosY(posy),
48  fMouseX(0), fMouseY(0),
49  fDrag(kNone),
50  fDrawW(0), fDrawH(0), fTextSizeDrag(0),
51  fActive(kFALSE),
52  fMainFrame(0), fTextEdit(0),
53 
54  fParent(0),
55 
56  fText(text),
57  fTextSize(0.03),
58  fTextAlign(TGLFont::kLeft),
59  fBackColor(fgBackColor),
60  fTextColor(fgTextColor),
61  fTransparency(100),
62  fDrawRefLine(kFALSE),
63  fUseColorSet(kTRUE),
64  fAllowClose(kTRUE)
65 {
66  // Constructor.
67  // Create annotation as plain text
68 
69  parent->AddOverlayElement(this);
70  fParent = (TGLViewer*)parent;
71 }
72 
73 ////////////////////////////////////////////////////////////////////////////////
74 
75 TGLAnnotation::TGLAnnotation(TGLViewerBase *parent, const char *text, Float_t posx, Float_t posy, TGLVector3 ref) :
76  TGLOverlayElement(TGLOverlayElement::kAnnotation),
77  fPosX(posx), fPosY(posy),
78  fMouseX(0), fMouseY(0),
79  fDrag(kNone),
80  fDrawW(0), fDrawH(0), fTextSizeDrag(0),
81  fActive(kFALSE),
82  fMainFrame(0), fTextEdit(0),
83 
84  fParent(0),
85 
86  fText(text),
87  fTextSize(0.03),
88  fTextAlign(TGLFont::kLeft),
89  fBackColor(fgBackColor),
90  fTextColor(fgTextColor),
91  fTransparency(40),
92  fDrawRefLine(kTRUE),
93  fUseColorSet(kTRUE),
94  fAllowClose(kTRUE)
95 {
96  // Constructor.
97  // Create annotation by picking an object.
98 
99  fPointer = ref;
100  parent->AddOverlayElement(this);
101  fParent = (TGLViewer*)parent;
102 }
103 
104 ////////////////////////////////////////////////////////////////////////////////
105 /// Destructor.
106 
107 TGLAnnotation::~TGLAnnotation()
108 {
109  fParent->RemoveOverlayElement(this);
110  delete fMainFrame;
111 }
112 
113 ////////////////////////////////////////////////////////////////////////////////
114 /// Handle overlay event.
115 /// Return TRUE if event was handled.
116 
117 Bool_t TGLAnnotation::Handle(TGLRnrCtx& rnrCtx,
118  TGLOvlSelectRecord& selRec,
119  Event_t* event)
120 {
121  if (selRec.GetN() < 2) return kFALSE;
122  Int_t recID = selRec.GetItem(1);
123  switch (event->fType)
124  {
125  case kButtonPress:
126  {
127  fMouseX = event->fX;
128  fMouseY = event->fY;
129  fDrag = (recID == kResizeID) ? kResize : kMove;
130  fTextSizeDrag = fTextSize;
131  return kTRUE;
132  }
133  case kButtonRelease:
134  {
135  fDrag = kNone;
136  if (recID == kDeleteID)
137  {
138  TGLViewer *v = fParent;
139  delete this;
140  v->RequestDraw(rnrCtx.ViewerLOD());
141  }
142  else if (recID == kEditID)
143  {
144  MakeEditor();
145  }
146  return kTRUE;
147  }
148  case kMotionNotify:
149  {
150  const TGLRect& vp = rnrCtx.RefCamera().RefViewport();
151  if (vp.Width() == 0 || vp.Height() == 0) return kFALSE;
152 
153  if (fDrag == kMove)
154  {
155  fPosX += (Float_t)(event->fX - fMouseX) / vp.Width();
156  fPosY -= (Float_t)(event->fY - fMouseY) / vp.Height();
157  fMouseX = event->fX;
158  fMouseY = event->fY;
159  // Make sure we don't go offscreen (use fDraw variables set in draw)
160  if (fPosX < 0)
161  fPosX = 0;
162  else if (fPosX + fDrawW > 1.0f)
163  fPosX = 1.0f - fDrawW;
164  if (fPosY < fDrawH)
165  fPosY = fDrawH;
166  else if (fPosY > 1.0f)
167  fPosY = 1.0f;
168  }
169  else if (fDrag == kResize)
170  {
171  using namespace TMath;
172  Float_t oovpw = 1.0f / vp.Width(), oovph = 1.0f / vp.Height();
173 
174  Float_t xw = oovpw * Min(Max(0, event->fX), vp.Width());
175  Float_t yw = oovph * Min(Max(0, vp.Height() - event->fY), vp.Height());
176 
177  Float_t rx = Max((xw - fPosX) / (oovpw * fMouseX - fPosX), 0.0f);
178  Float_t ry = Max((yw - fPosY) / (oovph*(vp.Height() - fMouseY) - fPosY), 0.0f);
179 
180  fTextSize = Max(fTextSizeDrag * Min(rx, ry), 0.01f);
181  }
182  return kTRUE;
183  }
184  default:
185  {
186  return kFALSE;
187  }
188  }
189 }
190 
191 ////////////////////////////////////////////////////////////////////////////////
192 /// Mouse has entered overlay area.
193 
194 Bool_t TGLAnnotation::MouseEnter(TGLOvlSelectRecord& /*rec*/)
195 {
196  fActive = kTRUE;
197  return kTRUE;
198 }
199 
200 ////////////////////////////////////////////////////////////////////////////////
201 /// Mouse has left overlay area.
202 
203 void TGLAnnotation::MouseLeave()
204 {
205  fActive = kFALSE;
206 }
207 
208 ////////////////////////////////////////////////////////////////////////////////
209 /// Render the annotation.
210 
211 void TGLAnnotation::Render(TGLRnrCtx& rnrCtx)
212 {
213  const TGLRect& vp = rnrCtx.RefCamera().RefViewport();
214  if (vp.Width() == 0 && vp.Height() == 0)
215  return;
216 
217  Float_t old_depth_range[2];
218  glGetFloatv(GL_DEPTH_RANGE, old_depth_range);
219  glDepthRange(0, 0.001);
220 
221 
222  glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POLYGON_BIT);
223  TGLCapabilitySwitch lights_off(GL_LIGHTING, kFALSE);
224  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
225  glDisable(GL_CULL_FACE);
226  glEnable(GL_BLEND);
227  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
228 
229  // prepare colors
230  Color_t bgCol = fBackColor;
231  Color_t fgCol = fTextColor;
232 
233  if (fUseColorSet)
234  {
235  fgCol = rnrCtx.ColorSet().Markup().GetColorIndex();
236 
237  TColor* c1 = gROOT->GetColor(rnrCtx.ColorSet().Markup().GetColorIndex());
238  TColor* c2 = gROOT->GetColor(rnrCtx.ColorSet().Background().GetColorIndex());
239 
240  if (c1 && c2) {
241  Float_t f1 = 0.5, f2 = 0.5;
242  bgCol = TColor::GetColor(c1->GetRed() *f1 + c2->GetRed() *f2,
243  c1->GetGreen()*f1 + c2->GetGreen()*f2,
244  c1->GetBlue() *f1 + c2->GetBlue() *f2);
245  }
246  }
247 
248  // reset matrix
249  rnrCtx.ProjectionMatrixPushIdentity();
250 
251  glPushMatrix();
252  // set ortho camera to [0,1] [0.1]
253  glLoadIdentity();
254  glTranslatef(-1.0f, -1.0f, 0.0f);
255  glScalef(2.0f, 2.0f, 1.0f);
256 
257  glEnable(GL_POLYGON_OFFSET_FILL);
258  glPolygonOffset(0.1f, 1.0f);
259 
260  glPushMatrix();
261 
262  TGLUtil::LineWidth(1.0f);
263 
264  // move to pos
265  glTranslatef(fPosX, fPosY, 0.0f);
266 
267  TObjArray *lines = fText.Tokenize("\n");
268  TIter line_iter(lines);
269  TObjString *osl;
270 
271  Float_t widthTxt, heightTxt, sx, sy, descent, line_height;
272  {
273  // get unscaled text size
274  Int_t fs = TGLFontManager::GetFontSize(TMath::Nint(vp.Height()*fTextSize), 12, 64);
275  rnrCtx.RegisterFontNoScale(fs, "arial", TGLFont::kTexture, fFont);
276  descent = fFont.GetDescent();
277  line_height = fFont.GetLineHeight();
278 
279  Float_t llx, lly, llz, urx, ury, urz;
280  widthTxt = heightTxt = 0;
281  while ((osl = (TObjString*) line_iter()) != 0)
282  {
283  fFont.BBox(osl->GetString().Data(), llx, lly, llz, urx, ury, urz);
284  widthTxt = TMath::Max(widthTxt, urx);
285  heightTxt += line_height;
286  }
287  widthTxt += 2.0f * descent;
288  heightTxt += 2.0f * descent;
289 
290  // keep proportions
291  sy = fTextSize / (line_height + descent);
292  sx = sy / vp.Aspect();
293  fDrawW = sx*widthTxt;
294  fDrawH = sy*heightTxt;
295  }
296  glScalef(sx, sy, 1.0f);
297 
298  glPushName(kMoveID);
299 
300  Float_t x1, x2, y1, y2;
301  Float_t z3 = 0.0f; // main background
302  Float_t z2 = -0.01f; // outlines and text
303  Float_t z1 = -0.02f; // button on top of text
304  Float_t z0 = -0.03f; // button on top of text
305 
306  // main background
307  glLoadName(kMoveID);
308  x1 = 0.0f;
309  x2 = widthTxt;
310  y1 = -heightTxt;
311  y2 = 0.0f;
312  TGLUtil::ColorTransparency(bgCol, fTransparency);
313  glBegin(GL_QUADS);
314  glVertex3f(x1, y1, z3);
315  glVertex3f(x2, y1, z3);
316  glVertex3f(x2, y2, z3);
317  glVertex3f(x1, y2, z3);
318  glEnd();
319  // main polygon outline
320  TGLUtil::ColorTransparency(fgCol, GetLineTransparency());
321  glBegin(GL_LINE_LOOP);
322  glVertex3f(x1, y1, z2);
323  glVertex3f(x2, y1, z2);
324  glVertex3f(x2, y2, z2);
325  glVertex3f(x1, y2, z2);
326  glEnd();
327 
328  // annotation text
329  TGLUtil::Color(fgCol);
330  fFont.PreRender();
331  glPushMatrix();
332  Float_t tx = 0;
333  line_iter.Reset();
334  while ((osl = (TObjString*) line_iter()) != 0)
335  {
336  if (fTextAlign == TGLFont::kLeft) {
337  tx = 0;
338  }
339  else if (fTextAlign == TGLFont::kCenterH) {
340  tx = 0.5f * widthTxt - descent ;
341  }
342  else {
343  tx = widthTxt - 2.0f * descent;
344  }
345  glTranslatef(0.0f, -line_height, 0.0f);
346  fFont.Render(osl->GetString(), tx+descent, 0, z2, fTextAlign, TGLFont::kTop) ;
347  }
348  glPopMatrix();
349  fFont.PostRender();
350 
351  delete lines;
352 
353  // buttons
354  if (fActive)
355  {
356  Float_t bbox[6];
357  fFont.PreRender();
358  fFont.BBox("X", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5]);
359  glLoadName(kEditID);
360  fFont.Render("E", descent, descent, z2, fTextAlign, TGLFont::kTop);
361  x2 = bbox[3] + 2.0f * descent;
362  if (fAllowClose)
363  {
364  glLoadName(kDeleteID);
365  fFont.Render("X", x2 + descent, descent, z2, fTextAlign, TGLFont::kTop);
366  }
367  fFont.PostRender();
368 
369  x1 = 0.0f;
370  y1 = 0.0f;
371  y2 = line_height + descent;
372  {
373  // edit button
374  glLoadName(kEditID);
375  // polygon
376  TGLUtil::ColorTransparency(bgCol, fTransparency);
377  glBegin(GL_QUADS);
378  glVertex3f(x1, y1, z3);
379  glVertex3f(x2, y1, z3);
380  glVertex3f(x2, y2, z3);
381  glVertex3f(x1, y2, z3);
382  glEnd();
383  // outline
384  TGLUtil::ColorTransparency(fgCol, GetLineTransparency());
385  glBegin(GL_LINE_LOOP);
386  glVertex3f(x1, y1, z0);
387  glVertex3f(x2, y1, z0);
388  glVertex3f(x2, y2, z0);
389  glVertex3f(x1, y2, z0);
390  glEnd();
391  }
392  x1 += x2;
393  x2 += x2;
394  if (fAllowClose)
395  {
396  // close button
397  glLoadName(kDeleteID);
398  // polygon
399  TGLUtil::ColorTransparency(bgCol, fTransparency);
400  glBegin(GL_QUADS);
401  glVertex3f(x1, y1, z3);
402  glVertex3f(x2, y1, z3);
403  glVertex3f(x2, y2, z3);
404  glVertex3f(x1, y2, z3);
405  glEnd();
406  // outline
407  TGLUtil::ColorTransparency(fgCol, GetLineTransparency());
408  glBegin(GL_LINE_LOOP);
409  glVertex3f(x1, y1, z0);
410  glVertex3f(x2, y1, z0);
411  glVertex3f(x2, y2, z0);
412  glVertex3f(x1, y2, z0);
413  glEnd();
414  }
415  {
416  // resize button
417  glLoadName(kResizeID);
418  // polygon
419  x1 = widthTxt - line_height;
420  x2 = widthTxt;
421  y1 = -heightTxt;
422  y2 = -heightTxt + line_height;
423  TGLUtil::ColorTransparency(bgCol, fTransparency);
424  glBegin(GL_QUADS);
425  glVertex3f(x1, y1, z1);
426  glVertex3f(x2, y1, z1);
427  glVertex3f(x2, y2, z1);
428  glVertex3f(x1, y2, z1);
429  glEnd();
430  // draw resize corner lines
431  TGLUtil::ColorTransparency(fgCol, GetLineTransparency());
432  glBegin(GL_LINES);
433  Float_t aOff = 0.25*line_height;
434  glVertex3f(x1+aOff, y1+aOff, z0);
435  glVertex3f(x2-aOff, y1+aOff, z0);
436  glVertex3f(x2-aOff, y1+aOff, z0);
437  glVertex3f(x2-aOff, y2-aOff, z0);
438  glEnd();
439  }
440  }
441 
442  glPopName();
443 
444  glPopMatrix();
445 
446  if (fDrawRefLine)
447  {
448  TGLVertex3 op = rnrCtx.RefCamera().WorldToViewport(fPointer);
449  op[0] /= vp.Width(); op[1] /= vp.Height();
450 
451  Float_t fx = op[0] < fPosX ? 0.0f : (op[0] > fPosX + fDrawW ? 1.0f : 0.5f);
452  Float_t fy = op[1] < fPosY-fDrawH ? 1.0f : (op[1] > fPosY ? 0.0f : 0.5f);
453 
454  if (fx != 0.5f || fy != 0.5f)
455  {
456  TGLUtil::ColorTransparency(bgCol, fTransparency);
457  TGLUtil::LineWidth(2);
458  glBegin(GL_LINES);
459  glVertex3f(fPosX + fx*fDrawW, fPosY - fy*fDrawH, z3);
460  glVertex3f(op[0], op[1], z3);
461  glEnd();
462  }
463  }
464 
465  glPopMatrix();
466  rnrCtx.ProjectionMatrixPop();
467 
468  glDepthRange(old_depth_range[0], old_depth_range[1]);
469  glPopAttrib();
470 }
471 
472 ////////////////////////////////////////////////////////////////////////////////
473 /// Returns transparency of annotation outline.
474 /// If annotation is selected enforce visibility of outline.
475 
476 Char_t TGLAnnotation::GetLineTransparency() const
477 {
478  if (fActive)
479  return TMath::Min(70, fTransparency);
480  else
481  return fTransparency;
482 }
483 
484 ////////////////////////////////////////////////////////////////////////////////
485 /// Show the annotation editor.
486 
487 void TGLAnnotation::MakeEditor()
488 {
489  if (fMainFrame == 0)
490  {
491  fMainFrame = new TGMainFrame(gClient->GetRoot(), 1000, 1000);
492  fMainFrame->SetWindowName("Annotation Editor");
493 
494  TGVerticalFrame* vf = new TGVerticalFrame(fMainFrame);
495 
496  fTextEdit = new TGTextEdit(vf, 1000, 1000, kSunkenFrame);
497  vf->AddFrame(fTextEdit, new TGLayoutHints(kLHintsExpandX|kLHintsExpandY));
498 
499  TGHorizontalFrame* hf = new TGHorizontalFrame(vf);
500 
501  TGTextButton* btt1 = new TGTextButton(hf, "OK");
502  hf->AddFrame(btt1, new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
503 
504  TGTextButton* btt2 = new TGTextButton(hf, "Cancel");
505  hf->AddFrame(btt2, new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
506 
507  btt1->Connect("Clicked()", "TGLAnnotation", this, "UpdateText()");
508  btt2->Connect("Clicked()", "TGLAnnotation", this, "CloseEditor()");
509 
510  vf->AddFrame(hf, new TGLayoutHints(kLHintsBottom | kLHintsRight | kLHintsExpandX, 2, 2, 5, 1));
511 
512  fMainFrame->AddFrame(vf, new TGLayoutHints(kLHintsExpandX|kLHintsExpandY));
513  fMainFrame->SetCleanup(kDeepCleanup);
514  fMainFrame->MapSubwindows();
515  }
516 
517  TGText *tgt = new TGText();
518  tgt->LoadBuffer(fText.Data());
519  fTextEdit->SetText(tgt);
520 
521  Int_t nrow = tgt->RowCount();
522  Int_t h = nrow*20;
523  Int_t w = fTextEdit->ReturnLongestLineWidth();
524  fMainFrame->Resize(TMath::Max(100, w+30), TMath::Max(100, h+40));
525 
526  fMainFrame->Layout();
527  fMainFrame->MapWindow();
528 }
529 
530 ////////////////////////////////////////////////////////////////////////////////
531 /// Close the annotation editor.
532 
533 void TGLAnnotation::CloseEditor()
534 {
535  fMainFrame->UnmapWindow();
536 }
537 
538 ////////////////////////////////////////////////////////////////////////////////
539 /// Modify the annotation text from the text-edit widget.
540 
541 void TGLAnnotation::UpdateText()
542 {
543  fText = fTextEdit->GetText()->AsString();
544  fMainFrame->UnmapWindow();
545  fParent->RequestDraw();
546 }