Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGMenu.cxx
Go to the documentation of this file.
1 // @(#)root/gui:$Id$
2 // Author: Fons Rademakers 09/01/98
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, 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 
13  This source is based on Xclass95, a Win95-looking GUI toolkit.
14  Copyright (C) 1996, 1997 David Barth, Ricky Ralston, Hector Peraza.
15 
16  Xclass95 is free software; you can redistribute it and/or
17  modify it under the terms of the GNU Library General Public
18  License as published by the Free Software Foundation; either
19  version 2 of the License, or (at your option) any later version.
20 
21 **************************************************************************/
22 
23 //////////////////////////////////////////////////////////////////////////
24 // //
25 // TGMenuBar, TGPopupMenu, TGMenuTitle and TGMenuEntry //
26 // //
27 // The TGMenu.h header contains all different menu classes. //
28 // //
29 // The TGMenuBar class implements a menu bar widget. It is used to //
30 // specify and provide access to common and frequently used application //
31 // actions grouped under menu titles (TGMenuTitle class). The menu bar //
32 // takes the highest-level of the menu system and it is a starting //
33 // point for many interactions. It is always visible and allows using //
34 // the keyboard equivalents. The geometry of the menu bar is //
35 // automatically set to the parent widget, i.e. the menu bar //
36 // automatically resizes itself so that it has the same width as its //
37 // parent (typically TGMainFrame). A menu bar contains one or more //
38 // popup menus and usually is placed along the top of the application //
39 // window. Any popup menu is invisible until the user invokes it by //
40 // using the mouse pointer or the keyboard. //
41 // //
42 // Popup menus implemented by TGPopupMenu class are unique in that, //
43 // by convention, they are not placed with the other GUI components in //
44 // the user interfaces. Instead, a popup menu usually appears either in //
45 // a menu bar or as a context menu on the TOP of the GUI. For that //
46 // reason it needs gClient->GetDefaultRoot() as a parent to get the //
47 // pointer to the root (i.e. desktop) window. This way a popup menu //
48 // will never be embedded. //
49 // NOTE: Using gClient->GetRoot() as a parent of TGPopupMenu will not //
50 // avoid the possibility of embedding the corresponding popup menu //
51 // because the current window hierarchy can be changed by using //
52 // gClient->SetRoot() method. //
53 // //
54 // As a context menus TGPopupMenu shows up after pressing the right //
55 // mouse button, over a popup-enabled component. The popup menu then //
56 // appears under the mouse pointer. //
57 // //
58 // Selecting a menu item will generate the event: //
59 // kC_COMMAND, kCM_MENU, menu id, user data. //
60 // //
61 //////////////////////////////////////////////////////////////////////////
62 
63 #include "TGMenu.h"
64 #include "TGResourcePool.h"
65 #include "TTimer.h"
66 #include "TMath.h"
67 #include "TSystem.h"
68 #include "TList.h"
69 #include "Riostream.h"
70 #include "KeySymbols.h"
71 #include "TGButton.h"
72 #include "TQConnection.h"
73 #include "TParameter.h"
74 #include "RConfigure.h"
75 #include "TEnv.h"
76 
77 const TGGC *TGPopupMenu::fgDefaultGC = 0;
78 const TGGC *TGPopupMenu::fgDefaultSelectedGC = 0;
79 const TGGC *TGPopupMenu::fgDefaultSelectedBackgroundGC = 0;
80 const TGFont *TGPopupMenu::fgDefaultFont = 0;
81 const TGFont *TGPopupMenu::fgHilightFont = 0;
82 
83 const TGGC *TGMenuTitle::fgDefaultGC = 0;
84 const TGGC *TGMenuTitle::fgDefaultSelectedGC = 0;
85 const TGFont *TGMenuTitle::fgDefaultFont = 0;
86 
87 
88 ClassImp(TGMenuBar);
89 ClassImp(TGMenuTitle);
90 ClassImpQ(TGPopupMenu)
91 
92 
93 ////////////////////////////////////////////////////////////////////////////////
94 
95 class TPopupDelayTimer : public TTimer {
96 private:
97  TGPopupMenu *fPopup; // popup menu
98 public:
99  TPopupDelayTimer(TGPopupMenu *p, Long_t ms) : TTimer(ms, kTRUE) { fPopup = p; }
100  Bool_t Notify();
101 };
102 
103 ////////////////////////////////////////////////////////////////////////////////
104 /// Notify when timer times out and reset the timer.
105 
106 Bool_t TPopupDelayTimer::Notify()
107 {
108  fPopup->HandleTimer(0);
109  Reset();
110  return kFALSE;
111 }
112 
113 
114 //////////////////////////////////////////////////////////////////////////
115 // //
116 // TGMenuBar member functions. //
117 // //
118 //////////////////////////////////////////////////////////////////////////
119 
120 ////////////////////////////////////////////////////////////////////////////////
121 /// Create a menu bar object.
122 
123 TGMenuBar::TGMenuBar(const TGWindow *p, UInt_t w, UInt_t h, UInt_t options)
124  : TGHorizontalFrame(p, w, h, options | kHorizontalFrame)
125 {
126  fCurrent = 0;
127  fTitles = new TList;
128  fStick = kTRUE;
129  fDefaultCursor = fClient->GetResourcePool()->GetGrabCursor();
130  fTrash = new TList();
131 
132  gVirtualX->GrabButton(fId, kButton1, kAnyModifier,
133  kButtonPressMask | kButtonReleaseMask | kEnterWindowMask,
134  kNone, kNone);
135 
136  fKeyNavigate = kFALSE;
137 
138  fMenuMore = new TGPopupMenu(gClient->GetDefaultRoot());
139  fMenuMore->AddLabel("Hidden Menus");
140  fMenuMore->AddSeparator();
141  fMenuBarMoreLayout = new TGLayoutHints(kLHintsTop | kLHintsRight);
142 
143  fWithExt = kFALSE;
144  fOutLayouts = new TList();
145  fNeededSpace = new TList();
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
149 /// Delete menu bar object. Removes also the hot keys from the main frame,
150 /// so hitting them will not cause the menus to popup.
151 
152 TGMenuBar::~TGMenuBar()
153 {
154  TGFrameElement *el;
155  TGMenuTitle *t;
156  Int_t keycode;
157 
158  if (!MustCleanup()) {
159  fTrash->Delete();
160  }
161  delete fTrash;
162 
163  const TGMainFrame *main = (TGMainFrame *)GetMainFrame();
164 
165  if (!MustCleanup()) {
166  TIter next(fList);
167  while ((el = (TGFrameElement *) next())) {
168  t = (TGMenuTitle *) el->fFrame;
169  if ((keycode = t->GetHotKeyCode()) != 0 && main) {
170  main->RemoveBind(this, keycode, kKeyMod1Mask);
171  }
172  }
173  }
174 
175  // delete TGMenuTitles
176  if (fTitles && !MustCleanup()) fTitles->Delete();
177  delete fTitles;
178 
179  delete fOutLayouts;
180  fNeededSpace->Delete();
181  delete fNeededSpace;
182  delete fMenuMore;
183  delete fMenuBarMoreLayout;
184 }
185 
186 ////////////////////////////////////////////////////////////////////////////////
187 /// Calculates whether the >> menu must be shown or not and
188 /// which menu titles are hidden.
189 
190 void TGMenuBar::Layout()
191 {
192  if (GetDefaultWidth() > GetWidth()) {
193  while (!(GetDefaultWidth() < GetWidth() ||
194  GetList()->GetSize() <= 1)) {
195  TGFrameElement* entry = GetLastOnLeft();
196  if (!entry) break;
197  TGMenuTitle* menuTitle = (TGMenuTitle*) entry->fFrame;
198  fNeededSpace->AddLast(new TParameter<Int_t>("", menuTitle->GetWidth() +
199  entry->fLayout->GetPadLeft() +
200  entry->fLayout->GetPadRight() ) );
201  fOutLayouts->AddLast( entry->fLayout );
202  fMenuMore->AddPopup( menuTitle->GetName(), menuTitle->GetMenu() );
203  menuTitle->GetMenu()->Connect("PoppedUp()", "TGMenuBar", this, "PopupConnection()");
204  RemovePopup( menuTitle->GetName() );
205  }
206  }
207 
208  if (fNeededSpace->GetSize() > 0) {
209  Int_t neededWidth = ((TParameter<Int_t>*) fNeededSpace->Last())->GetVal();
210  Bool_t fit = kFALSE;
211  if (fNeededSpace->GetSize() > 1)
212  fit = GetDefaultWidth() + neededWidth + 5 < GetWidth();
213  else
214  fit = GetDefaultWidth() + neededWidth - 7 < GetWidth();
215  while (fit) {
216  TGMenuEntry* menu = (TGMenuEntry*) fMenuMore->GetListOfEntries()->Last();
217  TGLayoutHints* layout = (TGLayoutHints*) fOutLayouts->Last();
218  ULong_t hints = (layout) ? layout->GetLayoutHints() : 0;
219  TGPopupMenu* beforeMenu = 0;
220  if (hints & kLHintsRight) {
221  TGFrameElement* entry = GetLastOnLeft();
222  if (entry) {
223  TGMenuTitle* beforeMenuTitle = (TGMenuTitle*) entry->fFrame;
224  beforeMenu = beforeMenuTitle->GetMenu();
225  }
226  }
227  if (menu && menu->GetPopup()) {
228  menu->GetPopup()->Disconnect("PoppedUp()", this, "PopupConnection()");
229  AddPopup( menu->GetName(), menu->GetPopup(), layout, beforeMenu );
230  }
231  fOutLayouts->Remove( fOutLayouts->Last() );
232  fNeededSpace->Remove( fNeededSpace->Last() );
233  fMenuMore->DeleteEntry(menu);
234 
235  if (fNeededSpace->GetSize() > 0) {
236  neededWidth = ((TParameter<Int_t>*)fNeededSpace->Last())->GetVal();
237  if (fNeededSpace->GetSize() > 1)
238  fit = GetDefaultWidth() + neededWidth + 5 < GetWidth();
239  else
240  fit = GetDefaultWidth() + neededWidth - 7 < GetWidth();
241  } else
242  fit = kFALSE;
243  }
244  }
245 
246  if (fNeededSpace->GetSize() > 0) {
247  if (!fWithExt) {
248  AddPopup(">>", fMenuMore, fMenuBarMoreLayout,
249  ((TGMenuTitle*)((TGFrameElement*)GetList()->First())->fFrame)->GetMenu());
250  fWithExt = kTRUE;
251  }
252  } else {
253  RemovePopup(">>");
254  fWithExt = kFALSE;
255  }
256 
257  MapSubwindows();
258  TGHorizontalFrame::Layout();
259 }
260 
261 ////////////////////////////////////////////////////////////////////////////////
262 /// Returns the last visible menu title on the left of the '>>'
263 /// in the menu bar.
264 
265 TGFrameElement* TGMenuBar::GetLastOnLeft()
266 {
267  TIter next(GetList());
268  while (TGFrameElement *entry = (TGFrameElement*) next()) {
269 
270  TGMenuTitle* menuTitle = (TGMenuTitle*) entry->fFrame;
271  TGLayoutHints* tmpLayout = (TGLayoutHints*) entry->fLayout;
272  ULong_t hints = tmpLayout->GetLayoutHints();
273 
274  if (hints & kLHintsRight && menuTitle->GetMenu() != fMenuMore) {
275  return entry;
276  }
277  }
278 
279  return ((TGFrameElement*)GetList()->Last());
280 }
281 
282 ////////////////////////////////////////////////////////////////////////////////
283 /// Connects the corresponding cascaded menu to the proper slots,
284 /// according to the highlighted menu entry in '>>' menu.
285 
286 void TGMenuBar::PopupConnection()
287 {
288  // Disconnect all previous signals
289  TList* slots = fMenuMore->GetListOfSignals();
290  TIter next (slots);
291  while (TList* connlist = (TList*) next()) {
292 
293  const char* signal_name = connlist->GetName();
294  TIter next2(connlist);
295  while (TQConnection* conn = (TQConnection*) next2()) {
296  const char* slot_name = conn->GetName();
297  void* receiver = conn->GetReceiver();
298  fMenuMore->Disconnect(signal_name, receiver, slot_name);
299  }
300  }
301  fMenuMore->fMsgWindow = 0;
302 
303  // Check wheter the current entry is a menu or not (just in case)
304  TGMenuEntry* currentEntry = fMenuMore->GetCurrent();
305  if (currentEntry->GetType() != kMenuPopup) return;
306 
307  // Connect the corresponding active signals to the >> menu
308  TGPopupMenu* currentMenu = currentEntry->GetPopup();
309 
310  slots = currentMenu->GetListOfSignals();
311  TIter next3 (slots);
312  while (TList* connlist = (TList*) next3()) {
313 
314  const char* signal_name = connlist->GetName();
315  if (strcmp(signal_name, "Activated(int)") == 0) {
316  TIter next2(connlist);
317  while (TQConnection* conn = (TQConnection*) next2()) {
318 
319  const char* slot_name = conn->GetName();
320  const char* class_name = conn->GetClassName();
321  void* receiver = conn->GetReceiver();
322  fMenuMore->Connect(signal_name, class_name, receiver, slot_name);
323  }
324  }
325  }
326 
327  fMenuMore->fMsgWindow = currentMenu->fMsgWindow;
328 }
329 
330 ////////////////////////////////////////////////////////////////////////////////
331 /// If on kTRUE bind arrow, popup menu hot keys, otherwise
332 /// remove key bindings.
333 
334 void TGMenuBar::BindKeys(Bool_t on)
335 {
336  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(kKey_Left), kAnyModifier, on);
337  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(kKey_Right), kAnyModifier, on);
338  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(kKey_Up), kAnyModifier, on);
339  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(kKey_Down), kAnyModifier, on);
340  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(kKey_Enter), kAnyModifier, on);
341  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(kKey_Return), kAnyModifier, on);
342  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(kKey_Escape), kAnyModifier, on);
343 
344  if (fCurrent && fCurrent->GetMenu()) {
345  BindMenu(fCurrent->GetMenu(), on);
346  }
347 }
348 
349 ////////////////////////////////////////////////////////////////////////////////
350 /// If on kTRUE bind subMenu hot keys, otherwise remove key bindings.
351 
352 void TGMenuBar::BindMenu(TGPopupMenu* subMenu, Bool_t on)
353 {
354  TGMenuEntry *e;
355  TIter next(subMenu->GetListOfEntries());
356 
357  while ((e = (TGMenuEntry*)next())) {
358  Int_t hot = 0;
359  if ( e->GetType() == kMenuPopup )
360  BindMenu(e->GetPopup(), on);
361  if (e->GetLabel()) {
362  hot = e->GetLabel()->GetHotChar();
363  }
364  if (!hot) continue;
365  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(hot), 0, on);
366  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(hot), kKeyShiftMask, on);
367  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(hot), kKeyLockMask, on);
368  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(hot), kKeyMod2Mask, on);
369  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(hot), kKeyShiftMask | kKeyLockMask, on);
370  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(hot), kKeyShiftMask | kKeyMod2Mask, on);
371  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(hot), kKeyLockMask | kKeyMod2Mask, on);
372  gVirtualX->GrabKey(fId, gVirtualX->KeysymToKeycode(hot), kKeyShiftMask | kKeyLockMask | kKeyMod2Mask, on);
373  }
374 }
375 
376 
377 ////////////////////////////////////////////////////////////////////////////////
378 /// If on kTRUE bind hot keys, otherwise remove key binding.
379 
380 void TGMenuBar::BindHotKey(Int_t keycode, Bool_t on)
381 {
382  const TGMainFrame *main = (TGMainFrame *) GetMainFrame();
383 
384  if (!main || !main->InheritsFrom("TGMainFrame")) return;
385 
386  if (on) {
387  // case unsensitive bindings
388  main->BindKey(this, keycode, kKeyMod1Mask);
389  main->BindKey(this, keycode, kKeyMod1Mask | kKeyShiftMask);
390  main->BindKey(this, keycode, kKeyMod1Mask | kKeyLockMask);
391  main->BindKey(this, keycode, kKeyMod1Mask | kKeyShiftMask | kKeyLockMask);
392 
393  main->BindKey(this, keycode, kKeyMod1Mask | kKeyMod2Mask);
394  main->BindKey(this, keycode, kKeyMod1Mask | kKeyShiftMask | kKeyMod2Mask);
395  main->BindKey(this, keycode, kKeyMod1Mask | kKeyMod2Mask | kKeyLockMask);
396  main->BindKey(this, keycode, kKeyMod1Mask | kKeyShiftMask | kKeyMod2Mask | kKeyLockMask);
397  } else {
398  main->RemoveBind(this, keycode, kKeyMod1Mask);
399  main->RemoveBind(this, keycode, kKeyMod1Mask | kKeyShiftMask);
400  main->RemoveBind(this, keycode, kKeyMod1Mask | kKeyLockMask);
401  main->RemoveBind(this, keycode, kKeyMod1Mask | kKeyShiftMask | kKeyLockMask);
402 
403  main->RemoveBind(this, keycode, kKeyMod1Mask | kKeyMod2Mask);
404  main->RemoveBind(this, keycode, kKeyMod1Mask | kKeyShiftMask | kKeyMod2Mask);
405  main->RemoveBind(this, keycode, kKeyMod1Mask | kKeyMod2Mask | kKeyLockMask);
406  main->RemoveBind(this, keycode, kKeyMod1Mask | kKeyShiftMask | kKeyMod2Mask | kKeyLockMask);
407  }
408 }
409 
410 ////////////////////////////////////////////////////////////////////////////////
411 /// Add popup menu to menu bar. The hot string will be adopted by the
412 /// menubar (actually the menu title) and deleted when possible.
413 /// If before is not 0 the menu will be added before it.
414 
415 void TGMenuBar::AddPopup(TGHotString *s, TGPopupMenu *menu, TGLayoutHints *l,
416  TGPopupMenu *before)
417 {
418  TGMenuTitle *t;
419  Int_t keycode;
420 
421  AddFrameBefore(t = new TGMenuTitle(this, s, menu), l, before);
422  fTitles->Add(t); // keep track of menu titles for later cleanup in dtor
423 
424  if ((keycode = t->GetHotKeyCode()) != 0) {
425  BindHotKey(keycode, kTRUE);
426  }
427 }
428 
429 ////////////////////////////////////////////////////////////////////////////////
430 /// Add popup via created before menu title.
431 
432 void TGMenuBar::AddTitle(TGMenuTitle *title, TGLayoutHints *l, TGPopupMenu *before)
433 {
434  Int_t keycode;
435 
436  AddFrameBefore(title, l, before);
437  fTitles->Add(title); // keep track of menu titles for later cleanup in dtor
438 
439  if ((keycode = title->GetHotKeyCode()) != 0) {
440  BindHotKey(keycode, kTRUE);
441  }
442 }
443 
444 ////////////////////////////////////////////////////////////////////////////////
445 /// Add popup menu to menu bar. If before is not 0 the menu will be
446 /// added before it.
447 
448 void TGMenuBar::AddPopup(const char *s, TGPopupMenu *menu, TGLayoutHints *l,
449  TGPopupMenu *before)
450 {
451  AddPopup(new TGHotString(s), menu, l, before);
452 }
453 
454 ////////////////////////////////////////////////////////////////////////////////
455 /// Add popup menu to menu bar.
456 ///
457 /// Comment:
458 /// This method is valid only for horizontal menu bars.
459 /// The most common case is menu bar containing equidistant titles padding left side.
460 /// TGMenuBar *bar;
461 /// bar->AddPopup("title1", 10);
462 /// bar->AddPopup("title2", 10);
463 /// ...
464 ///
465 /// To add equidistant titles padding right side padleft must be 0.
466 /// TGMenuBar *bar;
467 /// bar->AddPopup("title1", 0, 10);
468 /// bar->AddPopup("title2", 0, 10);
469 /// ...
470 ///
471 /// This method guarantee automatic cleanup when menu bar is destroyed.
472 /// Do not delete returned popup-menu
473 
474 TGPopupMenu *TGMenuBar::AddPopup(const TString &s, Int_t padleft, Int_t padright,
475  Int_t padtop, Int_t padbottom)
476 {
477  ULong_t hints = kLHintsTop;
478 
479  if (padleft) {
480  hints |= kLHintsLeft;
481  } else {
482  hints |= kLHintsRight;
483  }
484 
485  TGLayoutHints *l = new TGLayoutHints(hints, padleft, padright,
486  padtop, padbottom);
487  fTrash->Add(l);
488 
489  TGPopupMenu *menu = new TGPopupMenu(fClient->GetDefaultRoot());
490  AddPopup(new TGHotString(s), menu, l, 0);
491  fTrash->Add(menu);
492  return menu;
493 }
494 
495 ////////////////////////////////////////////////////////////////////////////////
496 /// Private version of AddFrame for menubar, to make sure that we
497 /// indeed only add TGMenuTitle objects to it. If before is not 0
498 /// the menu will be added before it.
499 
500 void TGMenuBar::AddFrameBefore(TGFrame *f, TGLayoutHints *l,
501  TGPopupMenu *before)
502 {
503  if (!f->InheritsFrom("TGMenuTitle")) {
504  Error("AddFrameBefore", "may only add TGMenuTitle objects to a menu bar");
505  return;
506  }
507 
508  if (!before) {
509  AddFrame(f, l);
510  return;
511  }
512 
513  TGFrameElement *nw;
514 
515  nw = new TGFrameElement;
516  nw->fFrame = f;
517  nw->fLayout = l ? l : fgDefaultHints;
518  nw->fState = 1;
519 
520  TGFrameElement *el;
521  TIter next(fList);
522  while ((el = (TGFrameElement *) next())) {
523  TGMenuTitle *t = (TGMenuTitle *) el->fFrame;
524  if (t->GetMenu() == before) {
525  fList->AddBefore(el, nw);
526  return;
527  }
528  }
529  fList->Add(nw);
530 }
531 
532 ////////////////////////////////////////////////////////////////////////////////
533 /// Return popup menu with the specified name. Returns 0 if menu is
534 /// not found. Returnes menu can be used as "before" in AddPopup().
535 /// Don't use hot key (&) in name.
536 
537 TGPopupMenu *TGMenuBar::GetPopup(const char *s)
538 {
539  if (!GetList()) return 0;
540 
541  TGFrameElement *el;
542  TIter next(GetList());
543  TString str = s;
544 
545  while ((el = (TGFrameElement *) next())) {
546  TGMenuTitle *t = (TGMenuTitle *) el->fFrame;
547  if (str == t->GetName())
548  return t->GetMenu();
549  }
550  return 0;
551 }
552 
553 ////////////////////////////////////////////////////////////////////////////////
554 /// Remove popup menu from menu bar. Returned menu has to be deleted by
555 /// the user, or can be re-used in another AddPopup(). Returns 0 if
556 /// menu is not found. Don't use hot key (&) in name.
557 
558 TGPopupMenu *TGMenuBar::RemovePopup(const char *s)
559 {
560  if (!GetList()) return 0;
561 
562  TGFrameElement *el;
563  TIter next(GetList());
564  TString str = s;
565 
566  while ((el = (TGFrameElement *) next())) {
567  TGMenuTitle *t = (TGMenuTitle *) el->fFrame;
568  if (str == t->GetName()) {
569  Int_t keycode;
570  if ((keycode = t->GetHotKeyCode())) {
571  BindHotKey(keycode, kFALSE); // remove bind
572  }
573  TGPopupMenu *m = t->GetMenu();
574  fTitles->Remove(t);
575  t->DestroyWindow();
576  RemoveFrame(t);
577  delete t;
578  return m;
579  }
580  }
581  return 0;
582 }
583 
584 ////////////////////////////////////////////////////////////////////////////////
585 /// Handle a mouse motion event in a menu bar.
586 
587 Bool_t TGMenuBar::HandleMotion(Event_t *event)
588 {
589  if (fKeyNavigate) return kTRUE;
590 
591  Int_t dummy;
592  Window_t wtarget;
593  TGMenuTitle *target = 0;
594 
595  if (!(event->fState & kButton1Mask))
596  fStick = kFALSE; // use some threshold!
597 
598  gVirtualX->TranslateCoordinates(fId, fId, event->fX, event->fY,
599  dummy, dummy, wtarget);
600  if (wtarget) target = (TGMenuTitle*) fClient->GetWindowById(wtarget);
601 
602  if (fCurrent && target && (target != fCurrent)) {
603  // deactivate all others
604  TGFrameElement *el;
605  TIter next(fList);
606  while ((el = (TGFrameElement *) next()))
607  ((TGMenuTitle*)el->fFrame)->SetState(kFALSE);
608 
609  fStick = kTRUE;
610  fCurrent = target;
611  target->SetState(kTRUE);
612  }
613 
614  return kTRUE;
615 }
616 
617 ////////////////////////////////////////////////////////////////////////////////
618 /// Handle a mouse button event in a menubar.
619 
620 Bool_t TGMenuBar::HandleButton(Event_t *event)
621 {
622  Int_t dummy;
623  Window_t wtarget;
624  TGMenuTitle *target;
625 
626  // We don't need to check the button number as GrabButton will
627  // only allow button1 events
628 
629  if (event->fType == kButtonPress) {
630 
631  gVirtualX->TranslateCoordinates(fId, fId, event->fX, event->fY,
632  dummy, dummy, wtarget);
633  target = (TGMenuTitle*) fClient->GetWindowById(wtarget);
634 
635  if (target != 0) {
636  fStick = kTRUE;
637 
638  if (target != fCurrent) {
639  // deactivate all others
640  TGFrameElement *el;
641  TIter next(fList);
642  while ((el = (TGFrameElement *) next()))
643  ((TGMenuTitle*)el->fFrame)->SetState(kFALSE);
644 
645  fStick = kTRUE;
646  fCurrent = target;
647  target->SetState(kTRUE);
648 
649  gVirtualX->GrabPointer(fId, kButtonPressMask | kButtonReleaseMask |
650  kPointerMotionMask, kNone, fDefaultCursor);
651  }
652  }
653  }
654 
655  if (event->fType == kButtonRelease) {
656  if (fStick) {
657  fStick = kFALSE;
658  return kTRUE;
659  }
660 
661  TGFrameElement *el;
662  TIter next(fList);
663  while ((el = (TGFrameElement *) next()))
664  ((TGMenuTitle*)el->fFrame)->SetState(kFALSE);
665 
666  gVirtualX->GrabPointer(0, 0, 0, 0, kFALSE); // ungrab pointer
667 
668  if (fCurrent != 0) {
669  target = fCurrent; // tricky, because WaitFor
670  fCurrent = 0;
671  if (!fKeyNavigate)
672  target->DoSendMessage();
673  }
674  fKeyNavigate = kFALSE;
675  }
676 
677  return kTRUE;
678 }
679 
680 ////////////////////////////////////////////////////////////////////////////////
681 /// Handle keyboard events in a menu bar.
682 
683 Bool_t TGMenuBar::HandleKey(Event_t *event)
684 {
685  TGMenuTitle *target = 0;
686  TGFrameElement *el;
687  void *dummy;
688  Int_t ax, ay;
689  Window_t wdummy;
690  TIter next(fList);
691 
692  if (event->fType == kGKeyPress) {
693  UInt_t keysym;
694  char tmp[2];
695 
696  gVirtualX->LookupString(event, tmp, sizeof(tmp), keysym);
697 
698  if (event->fState & kKeyMod1Mask) {
699  while ((el = (TGFrameElement *) next())) {
700  target = (TGMenuTitle *) el->fFrame;
701  if ((Int_t)event->fCode == target->GetHotKeyCode()) {
702  RequestFocus();
703  fKeyNavigate = kTRUE;
704  break;
705  }
706  }
707  if (el == 0) target = 0;
708  } else {
709  fKeyNavigate = kTRUE;
710 
711  if (fCurrent) {
712  TGFrameElement *cur = 0;
713  TGPopupMenu *menu = 0;
714  next.Reset();
715  el = 0;
716  while ((el = (TGFrameElement *) next())) {
717  if (el->fFrame == fCurrent) {
718  cur = el;
719  menu = ((TGMenuTitle*)el->fFrame)->GetMenu();
720  break;
721  }
722  }
723 
724  if (!menu || !menu->fPoppedUp) return kFALSE;
725 
726  TGMenuEntry *ce = 0;
727 
728  TGPopupMenu* currentMenu = fCurrent->GetMenu();
729  TGMenuEntry* currentEntry = currentMenu->GetCurrent();
730  while ( currentEntry ) {
731  if ( currentEntry->GetType() == kMenuPopup )
732  currentMenu = currentEntry->GetPopup();
733  if ( currentEntry != currentMenu->GetCurrent() )
734  currentEntry = currentMenu->GetCurrent();
735  else
736  currentEntry = 0;
737  }
738 
739  TIter next2(currentMenu->GetListOfEntries());
740 
741  while ((ce = (TGMenuEntry*)next2())) {
742  UInt_t hot = 0;
743  if (ce->GetLabel()) hot = ce->GetLabel()->GetHotChar();
744  if (!hot || (hot != keysym)) continue;
745 
746  currentMenu->Activate(ce);
747  if (ce->GetType() != kMenuPopup) {
748  gVirtualX->GrabPointer(0, 0, 0, 0, kFALSE);
749  fCurrent->SetState(kFALSE);
750  currentMenu->fStick = kFALSE;
751  Event_t ev;
752  ev.fType = kButtonRelease;
753  ev.fWindow = currentMenu->GetId();
754  fCurrent = 0;
755  return currentMenu->HandleButton(&ev);
756  }
757  else {
758  gVirtualX->TranslateCoordinates(currentMenu->fId,
759  (ce->fPopup->GetParent())->GetId(),
760  ce->fEx+currentMenu->fMenuWidth, ce->fEy,
761  ax, ay, wdummy);
762 #ifdef R__HAS_COCOA
763  gVirtualX->SetWMTransientHint(ce->fPopup->GetId(), GetId());
764 #endif
765  ce->fPopup->PlaceMenu(ax-5, ay-1, kFALSE, kFALSE);
766  }
767  }
768 
769  ce = menu->GetCurrent();
770  TGPopupMenu *submenu = 0;
771 
772  while (ce && (ce->GetType() == kMenuPopup)) {
773  submenu = ce->GetPopup();
774  if (!submenu->fPoppedUp) break;
775  ce = submenu->GetCurrent();
776  menu = submenu;
777  }
778  switch ((EKeySym)keysym) {
779  case kKey_Left:
780  if ((submenu) && (submenu->fPoppedUp)) {
781  submenu->EndMenu(dummy);
782  break;
783  }
784  el = (TGFrameElement*)fList->Before(cur);
785  if (!el) el = (TGFrameElement*)fList->Last();
786  break;
787  case kKey_Right:
788  if (submenu) {
789  if (submenu->fPoppedUp) {
790  if (!submenu->GetCurrent()) {
791  ce = (TGMenuEntry*)submenu->GetListOfEntries()->First();
792  } else {
793  submenu->EndMenu(dummy);
794  }
795  }
796  else {
797  gVirtualX->TranslateCoordinates(menu->fId,
798  (submenu->GetParent())->GetId(),
799  ce->fEx+menu->fMenuWidth, ce->fEy,
800  ax, ay, wdummy);
801 #ifdef R__HAS_COCOA
802  gVirtualX->SetWMTransientHint(submenu->GetId(), GetId());
803 #endif
804  submenu->PlaceMenu(ax-5, ay-1, kFALSE, kFALSE);
805  }
806  break;
807  }
808  el = (TGFrameElement*)fList->After(cur);
809  if (!el) el = (TGFrameElement*)fList->First();
810  break;
811  case kKey_Up:
812  if (ce) ce = (TGMenuEntry*)menu->GetListOfEntries()->Before(ce);
813  while (ce && ((ce->GetType() == kMenuSeparator) ||
814  (ce->GetType() == kMenuLabel) ||
815  !(ce->GetStatus() & kMenuEnableMask))) {
816  ce = (TGMenuEntry*)menu->GetListOfEntries()->Before(ce);
817  }
818  if (!ce) ce = (TGMenuEntry*)menu->GetListOfEntries()->Last();
819  break;
820  case kKey_Down:
821  if (ce) ce = (TGMenuEntry*)menu->GetListOfEntries()->After(ce);
822  while (ce && ((ce->GetType() == kMenuSeparator) ||
823  (ce->GetType() == kMenuLabel) ||
824  !(ce->GetStatus() & kMenuEnableMask))) {
825  ce = (TGMenuEntry*)menu->GetListOfEntries()->After(ce);
826  }
827  if (!ce) ce = (TGMenuEntry*)menu->GetListOfEntries()->First();
828  break;
829  case kKey_Enter:
830  case kKey_Return: {
831  gVirtualX->GrabPointer(0, 0, 0, 0, kFALSE);
832  fCurrent->SetState(kFALSE);
833  menu->fStick = kFALSE;
834  Event_t ev;
835  ev.fType = kButtonRelease;
836  ev.fWindow = menu->GetId();
837  fCurrent = 0;
838  return menu->HandleButton(&ev);
839  }
840  case kKey_Escape:
841  gVirtualX->GrabPointer(0, 0, 0, 0, kFALSE);
842  fCurrent->SetState(kFALSE);
843  fStick = kFALSE;
844  fCurrent = 0;
845  return menu->EndMenu(dummy);
846  default:
847  break;
848  }
849  if (ce) menu->Activate(ce);
850 
851  el = el ? el : cur;
852  if (el) target = (TGMenuTitle*)el->fFrame;
853  } else {
854  return kFALSE;
855  }
856  }
857 
858  if (target != 0) {
859  fStick = kTRUE;
860 
861  if (target != fCurrent) {
862  // deactivate all others
863  next.Reset();
864  while ((el = (TGFrameElement *) next()))
865  ((TGMenuTitle*)el->fFrame)->SetState(kFALSE);
866 
867  fCurrent = target;
868  target->SetState(kTRUE);
869  fStick = kTRUE;
870 
871  gVirtualX->GrabPointer(fId, kButtonPressMask | kButtonReleaseMask |
872  kPointerMotionMask, kNone, fDefaultCursor);
873 
874  TGMenuEntry *ptr;
875  TIter nexte(target->GetMenu()->GetListOfEntries());
876 
877  while ((ptr = (TGMenuEntry *) nexte())) {
878  if ((ptr->GetStatus() & kMenuEnableMask) &&
879  !(ptr->GetStatus() & kMenuHideMask) &&
880  (ptr->GetType() != kMenuSeparator) &&
881  (ptr->GetType() != kMenuLabel)) break;
882  }
883  if (ptr)
884  target->GetMenu()->Activate(ptr);
885 
886  return kTRUE;
887  }
888  } else {
889  return kFALSE;
890  }
891  }
892 
893  if (event->fType == kKeyRelease) {
894  if (fStick) {
895  fStick = kFALSE;
896  return kTRUE;
897  }
898  gVirtualX->GrabPointer(0, 0, 0, 0, kFALSE); // ungrab pointer
899 
900  next.Reset();
901  while ((el = (TGFrameElement *) next()))
902  ((TGMenuTitle*)el->fFrame)->SetState(kFALSE);
903 
904  if (fCurrent != 0) {
905  target = fCurrent; // tricky, because WaitFor
906  fCurrent = 0;
907  target->DoSendMessage();
908  }
909  }
910 
911  return kTRUE;
912 }
913 
914 
915 //////////////////////////////////////////////////////////////////////////
916 // //
917 // TGPopupMenu member functions. //
918 // //
919 //////////////////////////////////////////////////////////////////////////
920 
921 ////////////////////////////////////////////////////////////////////////////////
922 /// Create a popup menu.
923 
924 TGPopupMenu::TGPopupMenu(const TGWindow *p, UInt_t w, UInt_t h, UInt_t options)
925  : TGFrame(p, w, h, options | kOwnBackground)
926 {
927  fNormGC = GetDefaultGC()();
928  fSelGC = GetDefaultSelectedGC()();
929  fSelbackGC = GetDefaultSelectedBackgroundGC()();
930  fFontStruct = GetDefaultFontStruct();
931  fHifontStruct = GetHilightFontStruct();
932  fDefaultCursor = fClient->GetResourcePool()->GetGrabCursor();
933 
934  // We need to change the default context to actually use the
935  // Menu Fonts. [Are we actually changing the global settings?]
936  GCValues_t gcval;
937  gcval.fMask = kGCFont;
938  gcval.fFont = gVirtualX->GetFontHandle(fFontStruct);
939  gVirtualX->ChangeGC(fNormGC, &gcval);
940  gVirtualX->ChangeGC(fSelGC, &gcval);
941 
942  fDelay = 0;
943  fEntryList = new TList;
944 
945  // in case any of these magic values is changes, check also Reposition()
946  fBorderWidth = 3;
947  fMenuHeight = 6;
948  fMenuWidth = 8;
949  fXl = 16;
950  fMsgWindow = p;
951  fStick = kTRUE;
952  fCurrent = 0;
953  fHasGrab = kFALSE;
954  fPoppedUp = kFALSE;
955  fMenuBar = 0;
956  fSplitButton = 0;
957  fEntrySep = 3;
958 
959  SetWindowAttributes_t wattr;
960  wattr.fMask = kWAOverrideRedirect | kWASaveUnder;
961  wattr.fOverrideRedirect = kTRUE;
962  wattr.fSaveUnder = kTRUE;
963 
964  gVirtualX->ChangeWindowAttributes(fId, &wattr);
965 
966  AddInput(kPointerMotionMask | kEnterWindowMask | kLeaveWindowMask);
967 }
968 
969 ////////////////////////////////////////////////////////////////////////////////
970 /// Delete a popup menu.
971 
972 TGPopupMenu::~TGPopupMenu()
973 {
974  gClient->UnregisterPopup(this);
975 
976  if (fEntryList) fEntryList->Delete();
977  delete fEntryList;
978  delete fDelay;
979 }
980 
981 ////////////////////////////////////////////////////////////////////////////////
982 /// Add a menu entry. The hotstring is adopted by the menu (actually by
983 /// the TGMenuEntry) and deleted when possible. A possible picture is
984 /// borrowed from the picture pool and therefore not adopted.
985 /// If before is not 0, the entry will be added before it.
986 
987 void TGPopupMenu::AddEntry(TGHotString *s, Int_t id, void *ud,
988  const TGPicture *p, TGMenuEntry *before)
989 {
990  if (!s) return;
991  TGMenuEntry *nw = new TGMenuEntry;
992  Ssiz_t tab = s->Index('\t');
993  if (tab > 0) {
994  TString ts(s->Data());
995  TString shortcut = ts(tab+1, s->Length());
996  nw->fShortcut = new TGString(shortcut.Data());
997  nw->fLabel = new TGHotString(*s);
998  nw->fLabel->Remove(tab);
999  }
1000  else {
1001  nw->fLabel = s;
1002  }
1003  nw->fPic = p;
1004  nw->fType = kMenuEntry;
1005  nw->fEntryId = id;
1006  nw->fUserData = ud;
1007  nw->fPopup = 0;
1008  nw->fStatus = kMenuEnableMask;
1009  nw->fEx = 2;
1010  nw->fEy = fMenuHeight-2;
1011 
1012  if (before)
1013  fEntryList->AddBefore(before, nw);
1014  else
1015  fEntryList->Add(nw);
1016 
1017  UInt_t tw, ph = 0, pw = 0;
1018  tw = gVirtualX->TextWidth(fHifontStruct, s->GetString(), s->GetLength());
1019  if (p) {
1020  ph = p->GetHeight();
1021  pw = p->GetWidth();
1022  if (pw+12 > fXl) { fMenuWidth += pw+12-fXl; fXl = pw+12; }
1023  }
1024  if (nw->fShortcut) {
1025  tw += 10;
1026  delete s;
1027  }
1028 
1029  Int_t max_ascent, max_descent;
1030  nw->fEw = tw + pw /*+8*/+18+12;
1031  fMenuWidth = TMath::Max(fMenuWidth, nw->fEw);
1032  gVirtualX->GetFontProperties(fHifontStruct, max_ascent, max_descent);
1033  nw->fEh = max_ascent + max_descent + fEntrySep;
1034  if (nw->fEh < ph+fEntrySep) nw->fEh = ph+fEntrySep;
1035  fMenuHeight += nw->fEh;
1036 
1037  if (before)
1038  Reposition();
1039  else
1040  Resize(fMenuWidth, fMenuHeight);
1041 }
1042 
1043 ////////////////////////////////////////////////////////////////////////////////
1044 /// Add a menu entry. The string s in not adopted.
1045 /// If before is not 0, the entry will be added before it.
1046 
1047 void TGPopupMenu::AddEntry(const char *s, Int_t id, void *ud,
1048  const TGPicture *p, TGMenuEntry *before)
1049 {
1050  AddEntry(new TGHotString(s), id, ud, p, before);
1051 }
1052 
1053 ////////////////////////////////////////////////////////////////////////////////
1054 /// Add a menu separator to the menu.
1055 /// If before is not 0, the entry will be added before it.
1056 
1057 void TGPopupMenu::AddSeparator(TGMenuEntry *before)
1058 {
1059  TGMenuEntry *nw = new TGMenuEntry;
1060 
1061  nw->fLabel = 0;
1062  nw->fPic = 0;
1063  nw->fType = kMenuSeparator;
1064  nw->fEntryId = -1;
1065  nw->fUserData = 0;
1066  nw->fPopup = 0;
1067  nw->fStatus = kMenuEnableMask;
1068  nw->fEx = 2;
1069  nw->fEy = fMenuHeight-2;
1070 
1071  if (before)
1072  fEntryList->AddBefore(before, nw);
1073  else
1074  fEntryList->Add(nw);
1075 
1076  nw->fEw = 0;
1077  nw->fEh = 4;
1078  fMenuHeight += nw->fEh;
1079 
1080  if (before)
1081  Reposition();
1082  else
1083  Resize(fMenuWidth, fMenuHeight);
1084 }
1085 
1086 ////////////////////////////////////////////////////////////////////////////////
1087 /// Add a menu label to the menu. The hotstring is adopted by the menu
1088 /// (actually by the TGMenuEntry) and deleted when possible. A possible
1089 /// picture is borrowed from the picture pool and therefore not adopted.
1090 /// If before is not 0, the entry will be added before it.
1091 
1092 void TGPopupMenu::AddLabel(TGHotString *s, const TGPicture *p,
1093  TGMenuEntry *before)
1094 {
1095  TGMenuEntry *nw = new TGMenuEntry;
1096 
1097  nw->fLabel = s;
1098  nw->fPic = p;
1099  nw->fType = kMenuLabel;
1100  nw->fEntryId = -1;
1101  nw->fUserData = 0;
1102  nw->fPopup = 0;
1103  nw->fStatus = kMenuEnableMask | kMenuDefaultMask;
1104  nw->fEx = 2;
1105  nw->fEy = fMenuHeight-2;
1106 
1107  if (before)
1108  fEntryList->AddBefore(before, nw);
1109  else
1110  fEntryList->Add(nw);
1111 
1112  UInt_t tw, ph = 0, pw = 0;
1113  tw = gVirtualX->TextWidth(fHifontStruct, s->GetString(), s->GetLength());
1114  if (p) {
1115  ph = p->GetHeight();
1116  pw = p->GetWidth();
1117  if (pw+12 > fXl) { fMenuWidth += pw+12-fXl; fXl = pw+12; }
1118  }
1119 
1120  Int_t max_ascent, max_descent;
1121  nw->fEw = tw + pw /*+8*/+18+12;
1122  fMenuWidth = TMath::Max(fMenuWidth, nw->fEw);
1123  gVirtualX->GetFontProperties(fHifontStruct, max_ascent, max_descent);
1124  nw->fEh = max_ascent + max_descent + fEntrySep;
1125  if (nw->fEh < ph+fEntrySep) nw->fEh = ph+fEntrySep;
1126  fMenuHeight += nw->fEh;
1127 
1128  if (before)
1129  Reposition();
1130  else
1131  Resize(fMenuWidth, fMenuHeight);
1132 }
1133 
1134 ////////////////////////////////////////////////////////////////////////////////
1135 /// Add a menu label to the menu. The string s in not adopted.
1136 /// If before is not 0, the entry will be added before it.
1137 
1138 void TGPopupMenu::AddLabel(const char *s, const TGPicture *p,
1139  TGMenuEntry *before)
1140 {
1141  AddLabel(new TGHotString(s), p, before);
1142 }
1143 
1144 ////////////////////////////////////////////////////////////////////////////////
1145 /// Add a (cascading) popup menu to a popup menu. The hotstring is adopted
1146 /// by the menu (actually by the TGMenuEntry) and deleted when possible.
1147 /// If before is not 0, the entry will be added before it.
1148 
1149 void TGPopupMenu::AddPopup(TGHotString *s, TGPopupMenu *popup,
1150  TGMenuEntry *before, const TGPicture *p)
1151 {
1152  TGMenuEntry *nw = new TGMenuEntry;
1153 
1154  nw->fLabel = s;
1155  nw->fPic = p;
1156  nw->fType = kMenuPopup;
1157  nw->fEntryId = -2;
1158  nw->fUserData = 0;
1159  nw->fPopup = popup;
1160  nw->fStatus = kMenuEnableMask;
1161  nw->fEx = 2;
1162  nw->fEy = fMenuHeight-2;
1163 
1164  if (before)
1165  fEntryList->AddBefore(before, nw);
1166  else
1167  fEntryList->Add(nw);
1168 
1169  UInt_t tw = gVirtualX->TextWidth(fHifontStruct, s->GetString(),
1170  s->GetLength());
1171 
1172  UInt_t ph = 0, pw = 8;
1173  if (p) {
1174  ph = p->GetHeight();
1175  pw = p->GetWidth();
1176  if (pw+12 > fXl) { fMenuWidth += pw+12-fXl; fXl = pw+12; }
1177  }
1178  Int_t max_ascent, max_descent;
1179  nw->fEw = tw + pw+18+12;
1180  fMenuWidth = TMath::Max(fMenuWidth, nw->fEw);
1181  gVirtualX->GetFontProperties(fHifontStruct, max_ascent, max_descent);
1182  nw->fEh = max_ascent + max_descent + fEntrySep;
1183  if (nw->fEh < ph+fEntrySep) nw->fEh = ph+fEntrySep;
1184  fMenuHeight += nw->fEh;
1185 
1186  if (before)
1187  Reposition();
1188  else
1189  Resize(fMenuWidth, fMenuHeight);
1190 }
1191 
1192 ////////////////////////////////////////////////////////////////////////////////
1193 /// Add a (cascading) popup menu to a popup menu. The string s is not
1194 /// adopted. If before is not 0, the entry will be added before it.
1195 
1196 void TGPopupMenu::AddPopup(const char *s, TGPopupMenu *popup,
1197  TGMenuEntry *before, const TGPicture *p)
1198 {
1199  AddPopup(new TGHotString(s), popup, before, p);
1200 }
1201 
1202 ////////////////////////////////////////////////////////////////////////////////
1203 /// Reposition entries in popup menu. Called after menu item has been
1204 /// hidden or removed or inserted at a specified location.
1205 
1206 void TGPopupMenu::Reposition()
1207 {
1208  // in case any of these magic values is changes, check also the ctor.
1209  fMenuHeight = 6;
1210  fMenuWidth = 8;
1211  fXl = 16;
1212 
1213  TGMenuEntry *ptr;
1214  TIter next(fEntryList);
1215 
1216  while ((ptr = (TGMenuEntry *) next())) {
1217 
1218  if (ptr->fStatus & kMenuHideMask) continue;
1219 
1220  if (ptr->fPic) {
1221  UInt_t pw = ptr->fPic->GetWidth();
1222  if (pw+12 > fXl) { fMenuWidth += pw+12-fXl; fXl = pw+12; }
1223  }
1224  ptr->fEx = 2;
1225  ptr->fEy = fMenuHeight-2;
1226  fMenuWidth = TMath::Max(fMenuWidth, ptr->fEw);
1227  fMenuHeight += ptr->fEh;
1228  }
1229  Resize(fMenuWidth, fMenuHeight);
1230 }
1231 
1232 ////////////////////////////////////////////////////////////////////////////////
1233 /// Popup a popup menu. If stick mode is true keep the menu up. If
1234 /// grab_pointer is true the pointer will be grabbed, which means that
1235 /// all pointer events will go to the popup menu, independent of in
1236 /// which window the pointer is.
1237 
1238 void TGPopupMenu::PlaceMenu(Int_t x, Int_t y, Bool_t stick_mode, Bool_t grab_pointer)
1239 {
1240  void *ud;
1241  EndMenu(ud);
1242 
1243  Int_t rx, ry;
1244  UInt_t rw, rh;
1245 
1246  fStick = stick_mode;
1247  fCurrent = 0;
1248 
1249  // Parent is root window for a popup menu
1250  gVirtualX->GetWindowSize(fParent->GetId(), rx, ry, rw, rh);
1251 
1252  if (gVirtualX->InheritsFrom("TGWin32")) {
1253  if ((x > 0) && ((x + abs(rx) + (Int_t)fMenuWidth) > (Int_t)rw))
1254  x = rw - abs(rx) - fMenuWidth;
1255  if ((y > 0) && (y + abs(ry) + (Int_t)fMenuHeight > (Int_t)rh))
1256  y = rh - fMenuHeight;
1257  } else {
1258  if (x < 0) x = 0;
1259  if (x + fMenuWidth > rw) x = rw - fMenuWidth;
1260  if (y < 0) y = 0;
1261  if (y + fMenuHeight > rh) y = rh - fMenuHeight;
1262  }
1263 
1264  Move(x, y);
1265  MapRaised();
1266 
1267  if (grab_pointer) {
1268  gVirtualX->GrabPointer(fId, kButtonPressMask | kButtonReleaseMask |
1269  kPointerMotionMask, kNone, fDefaultCursor);
1270  fHasGrab = kTRUE;
1271  } else {
1272  fHasGrab = kFALSE;
1273  }
1274 
1275  fPoppedUp = kTRUE;
1276  PoppedUp();
1277  if (fMenuBar) fMenuBar->BindKeys(kTRUE);
1278 
1279  fClient->RegisterPopup(this);
1280 }
1281 
1282 ////////////////////////////////////////////////////////////////////////////////
1283 /// Close menu and return ID of selected menu item.
1284 /// In case of cascading menus, recursively close all menus.
1285 
1286 Int_t TGPopupMenu::EndMenu(void *&userData)
1287 {
1288  Int_t id;
1289 
1290  if (fDelay) fDelay->Remove();
1291 
1292  // destroy any cascaded children and get any ID
1293 
1294  if (fCurrent != 0) {
1295 
1296  // deactivate the entry
1297  fCurrent->fStatus &= ~kMenuActiveMask;
1298 
1299  if ((fCurrent->fType == kMenuPopup) && fCurrent->fPopup) {
1300  id = fCurrent->fPopup->EndMenu(userData);
1301  } else {
1302  // return the ID if the entry is enabled, otherwise -1
1303  if (fCurrent->fStatus & kMenuEnableMask) {
1304  id = fCurrent->fEntryId;
1305  userData = fCurrent->fUserData;
1306  } else {
1307  id = -1;
1308  userData = 0;
1309  }
1310  }
1311 
1312  } else {
1313  // if no entry selected...
1314  id = -1;
1315  userData = 0;
1316  }
1317 
1318  // then unmap itself
1319  UnmapWindow();
1320 
1321  gClient->UnregisterPopup(this);
1322  if (fMenuBar) fMenuBar->BindKeys(kFALSE);
1323 
1324  if (fPoppedUp) {
1325  fPoppedUp = kFALSE;
1326  PoppedDown();
1327  }
1328 
1329  return id;
1330 }
1331 
1332 ////////////////////////////////////////////////////////////////////////////////
1333 /// Handle button event in the popup menu.
1334 
1335 Bool_t TGPopupMenu::HandleButton(Event_t *event)
1336 {
1337  int id;
1338  void *ud = 0;
1339 
1340  if (event->fType == kButtonRelease) {
1341  if (fStick) {
1342  fStick = kFALSE;
1343  return kTRUE;
1344  }
1345  //if (fCurrent != 0)
1346  // if (fCurrent->fType == kMenuPopup) return kTRUE;
1347  id = EndMenu(ud);
1348  if (fHasGrab) gVirtualX->GrabPointer(0, 0, 0, 0, kFALSE); // ungrab
1349  if (fCurrent != 0) {
1350  fCurrent->fStatus &= ~kMenuActiveMask;
1351  if (fCurrent->fStatus & kMenuEnableMask) {
1352  SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_MENU), id,
1353  (Long_t)ud);
1354  Activated(id);
1355  }
1356  }
1357  }
1358  return kTRUE;
1359 }
1360 
1361 ////////////////////////////////////////////////////////////////////////////////
1362 /// Handle pointer crossing event in popup menu.
1363 
1364 Bool_t TGPopupMenu::HandleCrossing(Event_t *event)
1365 {
1366  if (event->fType == kEnterNotify) {
1367 
1368  TGMenuEntry *ptr;
1369  TIter next(fEntryList);
1370 
1371  while ((ptr = (TGMenuEntry *) next())) {
1372  if (ptr->fStatus & kMenuHideMask) continue;
1373 
1374  if ((event->fX >= ptr->fEx) && (event->fX <= ptr->fEx+(Int_t)fMenuWidth-10) &&
1375  (event->fY >= ptr->fEy) && (event->fY <= ptr->fEy+(Int_t)ptr->fEh))
1376  break;
1377  }
1378  Activate(ptr);
1379  } else {
1380  Activate((TGMenuEntry*)0);
1381  }
1382  if (fMenuBar) fMenuBar->fKeyNavigate = kFALSE;
1383  if (fSplitButton) fSplitButton->fKeyNavigate = kFALSE;
1384 
1385  return kTRUE;
1386 }
1387 
1388 ////////////////////////////////////////////////////////////////////////////////
1389 /// Handle pointer motion event in popup menu.
1390 
1391 Bool_t TGPopupMenu::HandleMotion(Event_t *event)
1392 {
1393  TGFrame::HandleMotion(event);
1394  static Int_t twice = 0;
1395  TGMenuEntry *ptr;
1396  TIter next(fEntryList);
1397 
1398  if (twice < 2) {
1399  // hack to eat mouse move events generated by Windows when
1400  // pressing/releasing a mouse button
1401  ++twice;
1402  }
1403  else {
1404  twice = 0;
1405  fStick = kFALSE; // be careful with this, use some threshold
1406  }
1407  while ((ptr = (TGMenuEntry *) next())) {
1408  if (ptr->fStatus & kMenuHideMask) continue;
1409 
1410  if ((event->fX >= ptr->fEx) && (event->fX <= ptr->fEx+(Int_t)fMenuWidth-4) && //fMenuWidth-10??
1411  (event->fY >= ptr->fEy) && (event->fY <= ptr->fEy+(Int_t)ptr->fEh))
1412  break;
1413  }
1414  Activate(ptr);
1415 
1416  return kTRUE;
1417 }
1418 
1419 ////////////////////////////////////////////////////////////////////////////////
1420 /// Activate a menu entry in a popup menu.
1421 
1422 void TGPopupMenu::Activate(TGMenuEntry *entry)
1423 {
1424  if (entry == fCurrent) return;
1425 
1426  //-- Deactivate the current entry
1427 
1428  if (fCurrent != 0) {
1429  void *ud;
1430  if (entry == 0 && fCurrent->fType == kMenuPopup) return;
1431  if ((fCurrent->fType == kMenuPopup) && fCurrent->fPopup)
1432  fCurrent->fPopup->EndMenu(ud);
1433  fCurrent->fStatus &= ~kMenuActiveMask;
1434  DrawEntry(fCurrent);
1435  }
1436 
1437  if (fDelay) fDelay->Remove();
1438 
1439  //-- Activate the new one
1440 
1441  if (entry) {
1442  entry->fStatus |= kMenuActiveMask;
1443  DrawEntry(entry);
1444  if (entry->fType == kMenuPopup) {
1445  if (!fDelay) fDelay = new TPopupDelayTimer(this, 350);
1446  fDelay->Reset();
1447  gSystem->AddTimer(fDelay);
1448  // after delay expires it will popup the cascading popup menu
1449  // iff it is still the current entry
1450  } else if (entry->fType == kMenuEntry) {
1451  // test...
1452  SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_MENUSELECT),
1453  entry->fEntryId, (Long_t)entry->fUserData);
1454  Highlighted(entry->fEntryId);
1455  }
1456  }
1457  fCurrent = entry;
1458 }
1459 
1460 ////////////////////////////////////////////////////////////////////////////////
1461 /// If TPopupDelayTimer times out popup cascading popup menu (if it is
1462 /// still the current entry).
1463 
1464 Bool_t TGPopupMenu::HandleTimer(TTimer *)
1465 {
1466  if (fCurrent != 0) {
1467  if ((fCurrent->fType == kMenuPopup) && fCurrent->fPopup) {
1468  Int_t ax, ay;
1469  Window_t wdummy;
1470 
1471  gVirtualX->TranslateCoordinates(fId,
1472  (fCurrent->fPopup->GetParent())->GetId(),
1473  fCurrent->fEx+fMenuWidth, fCurrent->fEy,
1474  ax, ay, wdummy);
1475 #ifdef R__HAS_COCOA
1476  gVirtualX->SetWMTransientHint(fCurrent->fPopup->GetId(), GetId());
1477 #endif
1478  fCurrent->fPopup->PlaceMenu(ax-5, ay-1, kFALSE, kFALSE);
1479  }
1480  }
1481  fDelay->Remove();
1482 
1483  return kTRUE;
1484 }
1485 
1486 ////////////////////////////////////////////////////////////////////////////////
1487 /// Draw popup menu.
1488 
1489 void TGPopupMenu::DoRedraw()
1490 {
1491  TGFrame::DoRedraw();
1492 
1493  TGMenuEntry *ptr;
1494  TIter next(fEntryList);
1495 
1496  while ((ptr = (TGMenuEntry *) next()))
1497  DrawEntry(ptr);
1498 }
1499 
1500 ////////////////////////////////////////////////////////////////////////////////
1501 /// Draw popup menu entry.
1502 
1503 void TGPopupMenu::DrawEntry(TGMenuEntry *entry)
1504 {
1505  FontStruct_t font;
1506  GCValues_t gcval;
1507 
1508  if (entry->fStatus & kMenuHideMask)
1509  return;
1510 
1511  if (entry->fStatus & kMenuDefaultMask) {
1512  font = fHifontStruct;
1513  gcval.fMask = kGCFont;
1514  gcval.fFont = gVirtualX->GetFontHandle(font);
1515  gVirtualX->ChangeGC(fNormGC, &gcval);
1516  gVirtualX->ChangeGC(fSelGC, &gcval);
1517  } else {
1518  font = fFontStruct;
1519  }
1520 
1521  UInt_t tw = 0;
1522  int max_ascent, max_descent;
1523  gVirtualX->GetFontProperties(font, max_ascent, max_descent);
1524  int tx = entry->fEx + fXl;
1525  // center text
1526  int offset = (entry->fEh - (max_ascent + max_descent)) / 2;
1527  int ty = entry->fEy + max_ascent + offset - 1;
1528  if (entry->fShortcut)
1529  tw = 7 + gVirtualX->TextWidth(fFontStruct, entry->fShortcut->Data(), entry->fShortcut->Length());
1530 
1531  switch (entry->fType) {
1532  case kMenuPopup:
1533  case kMenuLabel:
1534  case kMenuEntry:
1535  if ((entry->fStatus & kMenuActiveMask) && entry->fType != kMenuLabel) {
1536  gVirtualX->FillRectangle(fId, fSelbackGC, entry->fEx+1, entry->fEy-1,
1537  fMenuWidth-6, entry->fEh);
1538  if (gClient->GetStyle() > 1)
1539  gVirtualX->DrawRectangle(fId, GetShadowGC()(), entry->fEx+1, entry->fEy-2,
1540  fMenuWidth-7, entry->fEh);
1541  if (entry->fType == kMenuPopup)
1542  DrawTrianglePattern(fSelGC, fMenuWidth-10, entry->fEy+fEntrySep, fMenuWidth-6, entry->fEy+11);
1543  if (entry->fStatus & kMenuCheckedMask)
1544  DrawCheckMark(fSelGC, 6, entry->fEy+fEntrySep, 14, entry->fEy+11);
1545  if (entry->fStatus & kMenuRadioMask)
1546  DrawRCheckMark(fSelGC, 6, entry->fEy+fEntrySep, 14, entry->fEy+11);
1547  if (entry->fPic != 0)
1548  entry->fPic->Draw(fId, fSelGC, 8, entry->fEy+1);
1549  entry->fLabel->Draw(fId,
1550  (entry->fStatus & kMenuEnableMask) ? fSelGC : GetShadowGC()(),
1551  tx, ty);
1552  if (entry->fShortcut)
1553  entry->fShortcut->Draw(fId, (entry->fStatus & kMenuEnableMask) ? fSelGC : GetShadowGC()(),
1554  fMenuWidth - tw, ty);
1555  } else {
1556  if (gClient->GetStyle() > 1)
1557  gVirtualX->DrawRectangle(fId, GetBckgndGC()(), entry->fEx+1, entry->fEy-2,
1558  fMenuWidth-7, entry->fEh);
1559  gVirtualX->FillRectangle(fId, GetBckgndGC()(), entry->fEx+1, entry->fEy-1,
1560  fMenuWidth-6, entry->fEh);
1561  if (entry->fType == kMenuPopup)
1562  DrawTrianglePattern(fNormGC, fMenuWidth-10, entry->fEy+fEntrySep, fMenuWidth-6, entry->fEy+11);
1563  if (entry->fStatus & kMenuCheckedMask)
1564  DrawCheckMark(fNormGC, 6, entry->fEy+fEntrySep, 14, entry->fEy+11);
1565  if (entry->fStatus & kMenuRadioMask)
1566  DrawRCheckMark(fNormGC, 6, entry->fEy+fEntrySep, 14, entry->fEy+11);
1567  if (entry->fPic != 0)
1568  entry->fPic->Draw(fId, fNormGC, 8, entry->fEy+1);
1569  if (entry->fStatus & kMenuEnableMask) {
1570  entry->fLabel->Draw(fId, fNormGC, tx, ty);
1571  if (entry->fShortcut)
1572  entry->fShortcut->Draw(fId, fNormGC, fMenuWidth - tw, ty);
1573  } else {
1574  entry->fLabel->Draw(fId, GetHilightGC()(), tx+1, ty+1);
1575  entry->fLabel->Draw(fId, GetShadowGC()(), tx, ty);
1576  if (entry->fShortcut) {
1577  entry->fShortcut->Draw(fId, GetHilightGC()(), fMenuWidth - tw+1, ty+1);
1578  entry->fShortcut->Draw(fId, GetShadowGC()(), fMenuWidth - tw, ty);
1579  }
1580  }
1581  }
1582  break;
1583 
1584  case kMenuSeparator:
1585  gVirtualX->DrawLine(fId, GetShadowGC()(), 2, entry->fEy, fMenuWidth-fEntrySep, entry->fEy);
1586  gVirtualX->DrawLine(fId, GetHilightGC()(), 2, entry->fEy+1, fMenuWidth-fEntrySep, entry->fEy+1);
1587  break;
1588  }
1589 
1590  // restore font
1591  if (entry->fStatus & kMenuDefaultMask) {
1592  gcval.fFont = gVirtualX->GetFontHandle(fFontStruct);
1593  gVirtualX->ChangeGC(fNormGC, &gcval);
1594  gVirtualX->ChangeGC(fSelGC, &gcval);
1595  }
1596 }
1597 
1598 ////////////////////////////////////////////////////////////////////////////////
1599 /// Draw border round popup menu.
1600 
1601 void TGPopupMenu::DrawBorder()
1602 {
1603  if (gClient->GetStyle() > 0) {
1604  // new modern (flat) version
1605  gVirtualX->DrawLine(fId, GetShadowGC()(), 0, 0, 0, fMenuHeight-1);
1606  gVirtualX->DrawLine(fId, GetShadowGC()(), 0, fMenuHeight-1, fMenuWidth-1, fMenuHeight-1);
1607  gVirtualX->DrawLine(fId, GetShadowGC()(), fMenuWidth-1, fMenuHeight-1, fMenuWidth-1, 0);
1608  gVirtualX->DrawLine(fId, GetShadowGC()(), fMenuWidth-1, 0, 0, 0);
1609  }
1610  else {
1611  // old (raised frame) version
1612  gVirtualX->DrawLine(fId, GetBckgndGC()(), 0, 0, fMenuWidth-2, 0);
1613  gVirtualX->DrawLine(fId, GetBckgndGC()(), 0, 0, 0, fMenuHeight-2);
1614  gVirtualX->DrawLine(fId, GetHilightGC()(), 1, 1, fMenuWidth-fEntrySep, 1);
1615  gVirtualX->DrawLine(fId, GetHilightGC()(), 1, 1, 1, fMenuHeight-fEntrySep);
1616 
1617  gVirtualX->DrawLine(fId, GetShadowGC()(), 1, fMenuHeight-2, fMenuWidth-2, fMenuHeight-2);
1618  gVirtualX->DrawLine(fId, GetShadowGC()(), fMenuWidth-2, fMenuHeight-2, fMenuWidth-2, 1);
1619  gVirtualX->DrawLine(fId, GetBlackGC()(), 0, fMenuHeight-1, fMenuWidth-1, fMenuHeight-1);
1620  gVirtualX->DrawLine(fId, GetBlackGC()(), fMenuWidth-1, fMenuHeight-1, fMenuWidth-1, 0);
1621  }
1622 }
1623 
1624 ////////////////////////////////////////////////////////////////////////////////
1625 /// Draw triangle pattern. Used for menu entries that are of type
1626 /// kMenuPopup (i.e. cascading menus).
1627 
1628 void TGPopupMenu::DrawTrianglePattern(GContext_t gc, Int_t l, Int_t t,
1629  Int_t r, Int_t b)
1630 {
1631  Point_t points[3];
1632 
1633  int m = (t + b) >> 1;
1634 
1635  points[0].fX = l;
1636  points[0].fY = t;
1637  points[1].fX = l;
1638  points[1].fY = b;
1639  points[2].fX = r;
1640  points[2].fY = m;
1641 
1642  gVirtualX->FillPolygon(fId, gc, points, 3);
1643 }
1644 
1645 ////////////////////////////////////////////////////////////////////////////////
1646 /// Draw check mark. Used for checked button type menu entries.
1647 
1648 void TGPopupMenu::DrawCheckMark(GContext_t gc, Int_t l, Int_t t, Int_t, Int_t b)
1649 {
1650  Segment_t seg[6];
1651 
1652  t = (t + b - 8) >> 1; ++t;
1653 
1654  seg[0].fX1 = 1+l; seg[0].fY1 = 3+t; seg[0].fX2 = 3+l; seg[0].fY2 = 5+t;
1655  seg[1].fX1 = 1+l; seg[1].fY1 = 4+t; seg[1].fX2 = 3+l; seg[1].fY2 = 6+t;
1656  seg[2].fX1 = 1+l; seg[2].fY1 = 5+t; seg[2].fX2 = 3+l; seg[2].fY2 = 7+t;
1657  seg[3].fX1 = 3+l; seg[3].fY1 = 5+t; seg[3].fX2 = 7+l; seg[3].fY2 = 1+t;
1658  seg[4].fX1 = 3+l; seg[4].fY1 = 6+t; seg[4].fX2 = 7+l; seg[4].fY2 = 2+t;
1659  seg[5].fX1 = 3+l; seg[5].fY1 = 7+t; seg[5].fX2 = 7+l; seg[5].fY2 = 3+t;
1660 
1661  gVirtualX->DrawSegments(fId, gc, seg, 6);
1662 }
1663 
1664 ////////////////////////////////////////////////////////////////////////////////
1665 /// Draw radio check mark. Used for radio button type menu entries.
1666 
1667 void TGPopupMenu::DrawRCheckMark(GContext_t gc, Int_t l, Int_t t, Int_t r, Int_t b)
1668 {
1669  Segment_t seg[5];
1670 
1671  t = (t + b - 5) >> 1; ++t;
1672  l = (l + r - 5) >> 1; ++l;
1673 
1674  seg[0].fX1 = 1+l; seg[0].fY1 = 0+t; seg[0].fX2 = 3+l; seg[0].fY2 = 0+t;
1675  seg[1].fX1 = 0+l; seg[1].fY1 = 1+t; seg[1].fX2 = 4+l; seg[1].fY2 = 1+t;
1676  seg[2].fX1 = 0+l; seg[2].fY1 = 2+t; seg[2].fX2 = 4+l; seg[2].fY2 = 2+t;
1677  seg[3].fX1 = 0+l; seg[3].fY1 = 3+t; seg[3].fX2 = 4+l; seg[3].fY2 = 3+t;
1678  seg[4].fX1 = 1+l; seg[4].fY1 = 4+t; seg[4].fX2 = 3+l; seg[4].fY2 = 4+t;
1679 
1680  gVirtualX->DrawSegments(fId, gc, seg, 5);
1681 }
1682 
1683 ////////////////////////////////////////////////////////////////////////////////
1684 /// Set default entry (default entries are drawn with bold text).
1685 
1686 void TGPopupMenu::DefaultEntry(Int_t id)
1687 {
1688  TGMenuEntry *ptr;
1689  TIter next(fEntryList);
1690 
1691  while ((ptr = (TGMenuEntry *) next())) {
1692  if (ptr->fEntryId == id)
1693  ptr->fStatus |= kMenuDefaultMask;
1694  else
1695  ptr->fStatus &= ~kMenuDefaultMask;
1696  }
1697 }
1698 
1699 ////////////////////////////////////////////////////////////////////////////////
1700 /// Enable entry. By default entries are enabled.
1701 
1702 void TGPopupMenu::EnableEntry(Int_t id)
1703 {
1704  TGMenuEntry *ptr;
1705  TIter next(fEntryList);
1706 
1707  while ((ptr = (TGMenuEntry *) next()))
1708  if (ptr->fEntryId == id) {
1709  ptr->fStatus |= kMenuEnableMask;
1710  if (ptr->fStatus & kMenuHideMask) {
1711  ptr->fStatus &= ~kMenuHideMask;
1712  Reposition();
1713  }
1714  break;
1715  }
1716 }
1717 
1718 ////////////////////////////////////////////////////////////////////////////////
1719 /// Disable entry (disabled entries appear in a sunken relieve).
1720 
1721 void TGPopupMenu::DisableEntry(Int_t id)
1722 {
1723  TGMenuEntry *ptr;
1724  TIter next(fEntryList);
1725 
1726  while ((ptr = (TGMenuEntry *) next()))
1727  if (ptr->fEntryId == id) { ptr->fStatus &= ~kMenuEnableMask; break; }
1728 }
1729 
1730 ////////////////////////////////////////////////////////////////////////////////
1731 /// Return true if menu entry is enabled.
1732 
1733 Bool_t TGPopupMenu::IsEntryEnabled(Int_t id)
1734 {
1735  TGMenuEntry *ptr;
1736  TIter next(fEntryList);
1737 
1738  while ((ptr = (TGMenuEntry *) next()))
1739  if (ptr->fEntryId == id)
1740  return (ptr->fStatus & kMenuEnableMask) ? kTRUE : kFALSE;
1741  return kFALSE;
1742 }
1743 
1744 ////////////////////////////////////////////////////////////////////////////////
1745 /// Hide entry (hidden entries are not shown in the menu).
1746 /// To enable a hidden entry call EnableEntry().
1747 
1748 void TGPopupMenu::HideEntry(Int_t id)
1749 {
1750  TGMenuEntry *ptr;
1751  TIter next(fEntryList);
1752 
1753  while ((ptr = (TGMenuEntry *) next()))
1754  if (ptr->fEntryId == id) {
1755  ptr->fStatus |= kMenuHideMask;
1756  ptr->fStatus &= ~kMenuEnableMask;
1757  Reposition();
1758  break;
1759  }
1760 }
1761 
1762 ////////////////////////////////////////////////////////////////////////////////
1763 /// Return true if menu entry is hidden.
1764 
1765 Bool_t TGPopupMenu::IsEntryHidden(Int_t id)
1766 {
1767  TGMenuEntry *ptr;
1768  TIter next(fEntryList);
1769 
1770  while ((ptr = (TGMenuEntry *) next()))
1771  if (ptr->fEntryId == id)
1772  return (ptr->fStatus & kMenuHideMask) ? kTRUE : kFALSE;
1773  return kFALSE;
1774 }
1775 
1776 ////////////////////////////////////////////////////////////////////////////////
1777 /// Check a menu entry (i.e. add a check mark in front of it).
1778 
1779 void TGPopupMenu::CheckEntry(Int_t id)
1780 {
1781  TGMenuEntry *ptr;
1782  TIter next(fEntryList);
1783 
1784  while ((ptr = (TGMenuEntry *) next()))
1785  if (ptr->fEntryId == id) { ptr->fStatus |= kMenuCheckedMask; break; }
1786 }
1787 
1788 ////////////////////////////////////////////////////////////////////////////////
1789 /// Check a menu entry (i.e. add a check mark in front of it).
1790 /// The input argument is user data associated with entry
1791 
1792 void TGPopupMenu::CheckEntryByData(void *user_data)
1793 {
1794  TGMenuEntry *ptr;
1795  TIter next(fEntryList);
1796 
1797  while ((ptr = (TGMenuEntry *) next()))
1798  if (ptr->fUserData == user_data) { ptr->fStatus |= kMenuCheckedMask; break; }
1799 }
1800 
1801 ////////////////////////////////////////////////////////////////////////////////
1802 /// Uncheck menu entry (i.e. remove check mark).
1803 
1804 void TGPopupMenu::UnCheckEntry(Int_t id)
1805 {
1806  TGMenuEntry *ptr;
1807  TIter next(fEntryList);
1808 
1809  while ((ptr = (TGMenuEntry *) next()))
1810  if (ptr->fEntryId == id) { ptr->fStatus &= ~kMenuCheckedMask; break; }
1811 }
1812 
1813 ////////////////////////////////////////////////////////////////////////////////
1814 /// Uncheck all entries.
1815 
1816 void TGPopupMenu::UnCheckEntries()
1817 {
1818  TGMenuEntry *ptr;
1819  TIter next(fEntryList);
1820 
1821  while ((ptr = (TGMenuEntry *) next())) {
1822  ptr->fStatus &= ~kMenuCheckedMask;
1823  }
1824 }
1825 
1826 ////////////////////////////////////////////////////////////////////////////////
1827 /// Uncheck a menu entry (i.e. remove check mark in front of it).
1828 /// The input argument is user data associated with entry
1829 
1830 void TGPopupMenu::UnCheckEntryByData(void *user_data)
1831 {
1832  TGMenuEntry *ptr;
1833  TIter next(fEntryList);
1834 
1835  while ((ptr = (TGMenuEntry *) next()))
1836  if (ptr->fUserData == user_data) { ptr->fStatus &= ~kMenuCheckedMask; break; }
1837 }
1838 
1839 ////////////////////////////////////////////////////////////////////////////////
1840 /// Return true if menu item is checked.
1841 
1842 Bool_t TGPopupMenu::IsEntryChecked(Int_t id)
1843 {
1844  TGMenuEntry *ptr;
1845  TIter next(fEntryList);
1846 
1847  while ((ptr = (TGMenuEntry *) next()))
1848  if (ptr->fEntryId == id)
1849  return (ptr->fStatus & kMenuCheckedMask) ? kTRUE : kFALSE;
1850  return kFALSE;
1851 }
1852 
1853 ////////////////////////////////////////////////////////////////////////////////
1854 /// Radio-select entry (note that they cannot be unselected,
1855 /// the selection must be moved to another entry instead).
1856 
1857 void TGPopupMenu::RCheckEntry(Int_t id, Int_t IDfirst, Int_t IDlast)
1858 {
1859  TGMenuEntry *ptr;
1860  TIter next(fEntryList);
1861 
1862  while ((ptr = (TGMenuEntry *) next()))
1863  if (ptr->fEntryId == id)
1864  ptr->fStatus |= kMenuRadioMask | kMenuRadioEntryMask;
1865  else
1866  if (ptr->fEntryId >= IDfirst && ptr->fEntryId <= IDlast) {
1867  ptr->fStatus &= ~kMenuRadioMask;
1868  ptr->fStatus |= kMenuRadioEntryMask;
1869  }
1870 }
1871 
1872 ////////////////////////////////////////////////////////////////////////////////
1873 /// Return true if menu item has radio check mark.
1874 
1875 Bool_t TGPopupMenu::IsEntryRChecked(Int_t id)
1876 {
1877  TGMenuEntry *ptr;
1878  TIter next(fEntryList);
1879 
1880  while ((ptr = (TGMenuEntry *) next()))
1881  if (ptr->fEntryId == id)
1882  return (ptr->fStatus & kMenuRadioMask) ? kTRUE : kFALSE;
1883  return kFALSE;
1884 }
1885 
1886 ////////////////////////////////////////////////////////////////////////////////
1887 /// Find entry with specified id. Use the returned entry in DeleteEntry()
1888 /// or as the "before" item in the AddXXXX() methods. Returns 0 if entry
1889 /// is not found. To find entries that don't have an id like the separators,
1890 /// use the GetListOfEntries() method to get the complete entry
1891 /// list and iterate over it and check the type of each entry
1892 /// to find the separators.
1893 
1894 TGMenuEntry *TGPopupMenu::GetEntry(Int_t id)
1895 {
1896  TGMenuEntry *ptr;
1897  TIter next(fEntryList);
1898 
1899  while ((ptr = (TGMenuEntry *) next()))
1900  if (ptr->fEntryId == id)
1901  return ptr;
1902  return 0;
1903 }
1904 
1905 ////////////////////////////////////////////////////////////////////////////////
1906 /// Find entry with specified name. Name must match the original
1907 /// name without hot key symbol, like "Print" and not "&Print".
1908 /// Use the returned entry in DeleteEntry() or as the "before" item
1909 /// in the AddXXXX() methods. Returns 0 if entry is not found.
1910 /// To find entries that don't have a name like the separators,
1911 /// use the GetListOfEntries() method to get the complete entry
1912 /// list and iterate over it and check the type of each entry
1913 /// to find the separators.
1914 
1915 TGMenuEntry *TGPopupMenu::GetEntry(const char *s)
1916 {
1917  return (TGMenuEntry*) fEntryList->FindObject(s);
1918 }
1919 
1920 ////////////////////////////////////////////////////////////////////////////////
1921 /// Delete entry with specified id from menu.
1922 
1923 void TGPopupMenu::DeleteEntry(Int_t id)
1924 {
1925  TGMenuEntry *ptr;
1926  TIter next(fEntryList);
1927 
1928  while ((ptr = (TGMenuEntry *) next()))
1929  if (ptr->fEntryId == id) {
1930  fEntryList->Remove(ptr);
1931  delete ptr;
1932  Reposition();
1933  if (fCurrent == ptr)
1934  fCurrent = 0;
1935  return;
1936  }
1937 }
1938 
1939 ////////////////////////////////////////////////////////////////////////////////
1940 /// Delete specified entry from menu.
1941 
1942 void TGPopupMenu::DeleteEntry(TGMenuEntry *entry)
1943 {
1944  TGMenuEntry *ptr;
1945  TIter next(fEntryList);
1946 
1947  while ((ptr = (TGMenuEntry *) next()))
1948  if (ptr == entry) {
1949  fEntryList->Remove(ptr);
1950  delete ptr;
1951  Reposition();
1952  if (fCurrent == ptr)
1953  fCurrent = 0;
1954  return;
1955  }
1956 }
1957 
1958 ////////////////////////////////////////////////////////////////////////////////
1959 /// Return default graphics context.
1960 
1961 const TGGC &TGPopupMenu::GetDefaultGC()
1962 {
1963  if (!fgDefaultGC)
1964  fgDefaultGC = gClient->GetResourcePool()->GetFrameGC();
1965  return *fgDefaultGC;
1966 }
1967 
1968 ////////////////////////////////////////////////////////////////////////////////
1969 /// Return the selection graphics context in use.
1970 
1971 const TGGC &TGPopupMenu::GetDefaultSelectedGC()
1972 {
1973  if (!fgDefaultSelectedGC)
1974  fgDefaultSelectedGC = gClient->GetResourcePool()->GetSelectedGC();
1975  return *fgDefaultSelectedGC;
1976 }
1977 
1978 ////////////////////////////////////////////////////////////////////////////////
1979 /// Return the selection background graphics context in use.
1980 
1981 const TGGC &TGPopupMenu::GetDefaultSelectedBackgroundGC()
1982 {
1983  if (!fgDefaultSelectedBackgroundGC)
1984  fgDefaultSelectedBackgroundGC = gClient->GetResourcePool()->GetSelectedBckgndGC();
1985  return *fgDefaultSelectedBackgroundGC;
1986 }
1987 
1988 ////////////////////////////////////////////////////////////////////////////////
1989 /// Return the default font structure in use.
1990 
1991 FontStruct_t TGPopupMenu::GetDefaultFontStruct()
1992 {
1993  if (!fgDefaultFont)
1994  fgDefaultFont = gClient->GetResourcePool()->GetMenuFont();
1995  return fgDefaultFont->GetFontStruct();
1996 }
1997 
1998 ////////////////////////////////////////////////////////////////////////////////
1999 /// Return the font structure in use for highlighted menu entries.
2000 
2001 FontStruct_t TGPopupMenu::GetHilightFontStruct()
2002 {
2003  if (!fgHilightFont)
2004  fgHilightFont = gClient->GetResourcePool()->GetMenuHiliteFont();
2005  return fgHilightFont->GetFontStruct();
2006 }
2007 
2008 
2009 //////////////////////////////////////////////////////////////////////////
2010 // //
2011 // TGMenuTitle member functions. //
2012 // //
2013 //////////////////////////////////////////////////////////////////////////
2014 
2015 ////////////////////////////////////////////////////////////////////////////////
2016 /// Create a menu title. This object is created by a menu bar when adding
2017 /// a popup menu. The menu title adopts the hotstring.
2018 
2019 TGMenuTitle::TGMenuTitle(const TGWindow *p, TGHotString *s, TGPopupMenu *menu,
2020  GContext_t norm, FontStruct_t font, UInt_t options)
2021  : TGFrame(p, 1, 1, options)
2022 {
2023  fLabel = s;
2024  fMenu = menu;
2025  fFontStruct = font;
2026  fSelGC = GetDefaultSelectedGC()();
2027  fNormGC = norm;
2028  fState = kFALSE;
2029  fTitleId = -1;
2030  fTextColor = GetForeground();
2031  fTitleData = 0;
2032 
2033  Int_t hotchar;
2034  if (s && (hotchar = s->GetHotChar()) != 0)
2035  fHkeycode = gVirtualX->KeysymToKeycode(hotchar);
2036  else
2037  fHkeycode = 0;
2038 
2039  UInt_t tw = 0;
2040  Int_t max_ascent, max_descent;
2041  if (fLabel)
2042  tw = gVirtualX->TextWidth(fFontStruct, fLabel->GetString(), fLabel->GetLength());
2043  gVirtualX->GetFontProperties(fFontStruct, max_ascent, max_descent);
2044 
2045  Resize(tw + 8, max_ascent + max_descent + 7);
2046 
2047  if (p && p->InheritsFrom(TGMenuBar::Class())) {
2048  TGMenuBar *bar = (TGMenuBar*)p;
2049  fMenu->SetMenuBar(bar);
2050  }
2051 }
2052 
2053 ////////////////////////////////////////////////////////////////////////////////
2054 /// Set state of menu title.
2055 
2056 void TGMenuTitle::SetState(Bool_t state)
2057 {
2058  fState = state;
2059  if (state) {
2060  if (fMenu != 0) {
2061  Int_t ax, ay;
2062  Window_t wdummy;
2063 
2064  gVirtualX->TranslateCoordinates(fId, (fMenu->GetParent())->GetId(),
2065  0, 0, ax, ay, wdummy);
2066 
2067  // place the menu just under the window:
2068 #ifdef R__HAS_COCOA
2069  gVirtualX->SetWMTransientHint(fMenu->GetId(), GetId());
2070 #endif
2071  fMenu->PlaceMenu(ax-1, ay+fHeight, kTRUE, kFALSE); //kTRUE);
2072  }
2073  } else {
2074  if (fMenu != 0) {
2075  fTitleId = fMenu->EndMenu(fTitleData);
2076  }
2077  }
2078  fOptions &= ~(kSunkenFrame | kRaisedFrame);
2079  fClient->NeedRedraw(this);
2080 }
2081 
2082 ////////////////////////////////////////////////////////////////////////////////
2083 /// Draw a menu title.
2084 
2085 void TGMenuTitle::DoRedraw()
2086 {
2087  TGFrame::DoRedraw();
2088 
2089  int x, y, max_ascent, max_descent;
2090  x = y = 4;
2091 
2092  gVirtualX->GetFontProperties(fFontStruct, max_ascent, max_descent);
2093 
2094  if (fState) {
2095  gVirtualX->SetForeground(fNormGC, GetDefaultSelectedBackground());
2096  if (gClient->GetStyle() > 1) {
2097  gVirtualX->FillRectangle(fId, fNormGC, 1, 2, fWidth-3, fHeight-4);
2098  gVirtualX->DrawRectangle(fId, GetShadowGC()(), 1, 1, fWidth-3, fHeight-3);
2099  }
2100  else {
2101  gVirtualX->FillRectangle(fId, fNormGC, 0, 0, fWidth, fHeight);
2102  }
2103  gVirtualX->SetForeground(fNormGC, GetForeground());
2104  fLabel->Draw(fId, fSelGC, x, y + max_ascent);
2105  } else {
2106  // Use same background color than the menu bar
2107  Pixel_t back = GetDefaultFrameBackground();
2108  if (fMenu && fMenu->fMenuBar && fMenu->fMenuBar->GetBackground() != back)
2109  back = fMenu->fMenuBar->GetBackground();
2110  gVirtualX->SetForeground(fNormGC, back);
2111  if (gClient->GetStyle() > 1) {
2112  gVirtualX->DrawRectangle(fId, fNormGC, 1, 1, fWidth-3, fHeight-3);
2113  gVirtualX->FillRectangle(fId, fNormGC, 1, 2, fWidth-3, fHeight-4);
2114  }
2115  else {
2116  gVirtualX->FillRectangle(fId, fNormGC, 0, 0, fWidth, fHeight);
2117  }
2118  gVirtualX->SetForeground(fNormGC, fTextColor);
2119  fLabel->Draw(fId, fNormGC, x, y + max_ascent);
2120  if (fTextColor != GetForeground())
2121  gVirtualX->SetForeground(fNormGC, GetForeground());
2122  }
2123 }
2124 
2125 ////////////////////////////////////////////////////////////////////////////////
2126 /// Send final selected menu item to be processed.
2127 
2128 void TGMenuTitle::DoSendMessage()
2129 {
2130  if (fMenu)
2131  if (fTitleId != -1) {
2132  SendMessage(fMenu->fMsgWindow, MK_MSG(kC_COMMAND, kCM_MENU), fTitleId,
2133  (Long_t)fTitleData);
2134  fMenu->Activated(fTitleId);
2135  }
2136 }
2137 
2138 ////////////////////////////////////////////////////////////////////////////////
2139 /// Return default font structure in use.
2140 
2141 FontStruct_t TGMenuTitle::GetDefaultFontStruct()
2142 {
2143  if (!fgDefaultFont)
2144  fgDefaultFont = gClient->GetResourcePool()->GetMenuFont();
2145  return fgDefaultFont->GetFontStruct();
2146 }
2147 
2148 ////////////////////////////////////////////////////////////////////////////////
2149 /// Return default graphics context in use.
2150 
2151 const TGGC &TGMenuTitle::GetDefaultGC()
2152 {
2153  if (!fgDefaultGC)
2154  fgDefaultGC = gClient->GetResourcePool()->GetFrameGC();
2155  return *fgDefaultGC;
2156 }
2157 
2158 ////////////////////////////////////////////////////////////////////////////////
2159 /// Return default selection graphics context in use.
2160 
2161 const TGGC &TGMenuTitle::GetDefaultSelectedGC()
2162 {
2163  if (!fgDefaultSelectedGC)
2164  fgDefaultSelectedGC = gClient->GetResourcePool()->GetSelectedGC();
2165  return *fgDefaultSelectedGC;
2166 }
2167 
2168 ////////////////////////////////////////////////////////////////////////////////
2169 /// Save a popup menu widget as a C++ statement(s) on output stream out.
2170 
2171 void TGPopupMenu::SavePrimitive(std::ostream &out, Option_t *option /*= ""*/)
2172 {
2173  char quote = '"';
2174 
2175  out << " TGPopupMenu *";
2176  out << GetName() << " = new TGPopupMenu(gClient->GetDefaultRoot()"
2177  << "," << GetWidth() << "," << GetHeight() << "," << GetOptionString() << ");" << std::endl;
2178 
2179  Bool_t hasradio = kFALSE;
2180  Int_t r_first, r_last, r_active;
2181  r_active = r_first = r_last = -1;
2182 
2183  TGMenuEntry *mentry;
2184  TIter next(GetListOfEntries());
2185 
2186  while ((mentry = (TGMenuEntry *) next())) {
2187  const char *text;
2188  Int_t i, lentext, hotpos;
2189  char shortcut[80];
2190  char *outext;
2191 
2192  switch (mentry->GetType()) {
2193  case kMenuEntry:
2194  text = mentry->GetName();
2195  lentext = mentry->fLabel->GetLength();
2196  hotpos = mentry->fLabel->GetHotPos();
2197  outext = new char[lentext+2];
2198  i=0;
2199  while (text && lentext) {
2200  if (i == hotpos-1) {
2201  outext[i] = '&';
2202  i++;
2203  }
2204  outext[i] = *text;
2205  i++; text++; lentext--;
2206  }
2207  outext[i]=0;
2208  if (mentry->fShortcut) {
2209  snprintf(shortcut, 80, "\\t%s", mentry->GetShortcutText());
2210  }
2211  else {
2212  memset(shortcut, 0, 80);
2213  }
2214 
2215  out << " " << GetName() << "->AddEntry(" << quote
2216  << gSystem->ExpandPathName(gSystem->UnixPathName(outext)) // can be a file name
2217  << shortcut
2218  << quote << "," << mentry->GetEntryId();
2219  if (mentry->fUserData) {
2220  out << "," << mentry->fUserData;
2221  }
2222  if (mentry->fPic) {
2223  out << ",gClient->GetPicture(" << quote
2224  << gSystem->ExpandPathName(gSystem->UnixPathName(mentry->fPic->GetName()))
2225  << quote << ")";
2226  }
2227  out << ");" << std::endl;
2228  delete [] outext;
2229  break;
2230  case kMenuPopup:
2231  out << std::endl;
2232  out << " // cascaded menu " << quote << mentry->GetName() << quote <<std::endl;
2233  mentry->fPopup->SavePrimitive(out, option);
2234  text = mentry->GetName();
2235  lentext = mentry->fLabel->GetLength();
2236  hotpos = mentry->fLabel->GetHotPos();
2237  outext = new char[lentext+2];
2238  i=0;
2239  while (text && lentext) {
2240  if (i == hotpos-1) {
2241  outext[i] = '&';
2242  i++;
2243  }
2244  outext[i] = *text;
2245  i++; text++; lentext--;
2246  }
2247  outext[i]=0;
2248 
2249  out << " " << GetName() << "->AddPopup(" << quote
2250  << outext << quote << "," << mentry->fPopup->GetName()
2251  << ");" << std::endl;
2252  delete [] outext;
2253  break;
2254  case kMenuLabel:
2255  out << " " << GetName() << "->AddLabel(" << quote
2256  << mentry->GetName() << quote;
2257  if (mentry->fPic) {
2258  out << ",gClient->GetPicture(" << quote
2259  << mentry->fPic->GetName()
2260  << quote << ")";
2261  }
2262  out << ");" << std::endl;
2263  break;
2264  case kMenuSeparator:
2265  out << " " << GetName() << "->AddSeparator();" << std::endl;
2266  break;
2267  }
2268 
2269  if (!(mentry->GetStatus() & kMenuEnableMask)) {
2270  out<< " " << GetName() << "->DisableEntry(" << mentry->GetEntryId()
2271  << ");" << std::endl;
2272  }
2273  if (mentry->GetStatus() & kMenuHideMask) {
2274  out<< " " << GetName() << "->HideEntry(" << mentry->GetEntryId()
2275  << ");" << std::endl;
2276  }
2277  if (mentry->GetStatus() & kMenuCheckedMask) {
2278  out<< " " << GetName() << "->CheckEntry(" << mentry->GetEntryId()
2279  << ");" << std::endl;
2280  }
2281  if (mentry->GetStatus() & kMenuDefaultMask) {
2282  out<< " "<< GetName() << "->DefaultEntry(" << mentry->GetEntryId()
2283  << ");" << std::endl;
2284  }
2285  if (mentry->GetStatus() & kMenuRadioEntryMask) {
2286  if (hasradio) {
2287  r_last = mentry->GetEntryId();
2288  if (IsEntryRChecked(mentry->GetEntryId())) r_active = mentry->GetEntryId();
2289  }
2290  else {
2291  r_first = mentry->GetEntryId();
2292  hasradio = kTRUE;
2293  if (IsEntryRChecked(mentry->GetEntryId())) r_active = mentry->GetEntryId();
2294  }
2295  } else if (hasradio) {
2296  out << " " << GetName() << "->RCheckEntry(" << r_active << "," << r_first
2297  << "," << r_last << ");" << std::endl;
2298  hasradio = kFALSE;
2299  r_active = r_first = r_last = -1;
2300  }
2301  }
2302 }
2303 
2304 ////////////////////////////////////////////////////////////////////////////////
2305 /// Save a title menu widget as a C++ statement(s) on output stream out.
2306 
2307 void TGMenuTitle::SavePrimitive(std::ostream &out, Option_t *option /*= ""*/)
2308 {
2309  char quote = '"';
2310 
2311  out << std::endl;
2312  out << " // " << quote << fLabel->GetString() << quote <<" menu" << std::endl;
2313 
2314  fMenu->SavePrimitive(out, option);
2315 
2316  const char *text = fLabel->GetString();
2317  Int_t lentext = fLabel->GetLength();
2318  Int_t hotpos = fLabel->GetHotPos();
2319  char *outext = new char[lentext+2];
2320  Int_t i=0;
2321  while (lentext) {
2322  if (i == hotpos-1) {
2323  outext[i] = '&';
2324  i++;
2325  }
2326  outext[i] = *text;
2327  i++; text++; lentext--;
2328  }
2329  outext[i]=0;
2330  out << " " << fParent->GetName() << "->AddPopup(" << quote << outext
2331  << quote << "," << fMenu->GetName();
2332 
2333  delete [] outext;
2334 }
2335 
2336 ////////////////////////////////////////////////////////////////////////////////
2337 /// Save a menu bar widget as a C++ statement(s) on output stream out.
2338 
2339 void TGMenuBar::SavePrimitive(std::ostream &out, Option_t *option /*= ""*/)
2340 {
2341  out << std::endl;
2342  out << " // menu bar" << std::endl;
2343 
2344  out << " TGMenuBar *";
2345  out << GetName() << " = new TGMenuBar(" << fParent->GetName()
2346  << "," << GetWidth() << "," << GetHeight() << "," << GetOptionString() << ");" << std::endl;
2347  if (option && strstr(option, "keep_names"))
2348  out << " " << GetName() << "->SetName(\"" << GetName() << "\");" << std::endl;
2349 
2350  if (!fList) return;
2351 
2352  TGFrameElement *el;
2353  TIter next(fList);
2354 
2355  while ((el = (TGFrameElement *)next())) {
2356  el->fFrame->SavePrimitive(out, option);
2357  el->fLayout->SavePrimitive(out, option);
2358  out << ");" << std::endl;
2359  }
2360 }