X11 Work Bench Toolkit  1.0
menu_bar.c
Go to the documentation of this file.
1 // _ //
3 // _ __ ___ ___ _ __ _ _ | |__ __ _ _ __ ___ //
4 // | '_ ` _ \ / _ \| '_ \ | | | | | '_ \ / _` || '__|/ __| //
5 // | | | | | || __/| | | || |_| | | |_) || (_| || | _| (__ //
6 // |_| |_| |_| \___||_| |_| \__,_|_____|_.__/ \__,_||_|(_)\___| //
7 // |_____| //
8 // //
9 // generic menu bar implementation //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-2016 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17  all rights reserved
18 
19  DISCLAIMER: The X11workbench application and toolkit software are supplied
20  'as-is', with no warranties, either implied or explicit.
21  Any claims to alleged functionality or features should be
22  considered 'preliminary', and might not function as advertised.
23 
24  BSD-like license:
25 
26  There is no restriction as to what you can do with this software, so long
27  as you include the above copyright notice and DISCLAIMER for any distributed
28  work that is equal to or derived from this one, along with this paragraph
29  that explains the terms of the license if the source is also being made
30  available. A "derived work" describes a work that uses a significant portion
31  of the source files or algorithms that are included with this one.
32  Specifically excluded from this are files that were generated by the software,
33  or anything that is included with the software that is part of another package
34  (such as files that were created or added during the 'configure' process).
35  Specifically included is the use of part or all of any of the X11 workbench
36  toolkit source or header files in your distributed application. If you do not
37  ship the source, the above copyright statement is still required to be placed
38  in a reasonably prominent place, such as documentation, splash screens, and/or
39  'about the application' dialog boxes.
40 
41  Use and distribution are in accordance with GPL, LGPL, and/or the above
42  BSD-like license. See COPYING and README files for more information.
43 
44 
45  Additional information at http://sourceforge.net/projects/X11workbench
46 
47 ******************************************************************************/
48 
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <unistd.h>
61 #include <memory.h>
62 #include <string.h>
63 #include <strings.h>
64 #include <signal.h>
65 #include <time.h>
66 #include <X11/cursorfont.h>
67 
68 #include "window_helper.h"
69 #include "pixmap_helper.h" // pixmap helpers, including pre-defined icons
70 #include "frame_window.h"
71 #include "menu_bar.h"
72 #include "menu_popup.h"
73 #include "conf_help.h"
74 
75 
76 // sample menu resource format
77 //
78 // 1\n
79 // _File\tpopup\t2\n
80 // \tseparator\n
81 // _Help\t100\n
82 //
83 // 2\tpopup\n
84 // _Open\t102\Open File\n
85 // _Save\t103\Save File\n
86 // \tseparator\n
87 // E_xit\t104\Close Application\n
88 //
89 // this describes 2 separate menu resource strings. The first has ID 1, the 2nd has ID 2.
90 // The ID values are relative only to the menu itself.
91 
92 // generic description of format:
93 // <menu ID>[<tab>popup]<newline>
94 // <menu text><tab>[separator|popup<tab><submenu ID>|<notify ID>]<newline>
95 // <menu text><tab>[separator|popup<tab><submenu ID>|<notify ID>]<newline>
96 // (etc.)
97 //
98 
99 int MBMenuParentEvent(Window wIDMenu, XEvent *pEvent); // callback for parent windows that attach this menu
100 int MBMenuWinEvent(Window wID, XEvent *pEvent); // callback for menu events
101 
102 // message handlers
103 static int MenuBarDoExposeEvent(XExposeEvent *pEvent, WBMenu *pMenu, Display *pDisplay,
104  Window wID, WBMenuBarWindow *pSelf);
105 
106 
107 #define BORDER 32 /* was 1 */
108 #define MENU_FONT "fixed"
109 #define MENU_ALT_FONT "*-fixed-*"
110 #define MENU_FONT_SIZE 13
111 
112 // global color definitions
115 
116 static int iInitColorFlag = 0;
117 static XFontStruct *pDefaultMenuFont = NULL; // default menu font
118 
130 Atom aMENU_RESIZE = 0;
131 
150 Atom aMENU_ACTIVATE = 0;
151 
166 
167 
168 // for release code, define DEBUG_VALIDATE(X) as X
169 #ifdef NO_DEBUG
170 #define DEBUG_VALIDATE(X) X
171 #else // NO_DEBUG
172 #define DEBUG_VALIDATE(X) if(!(X)) { WB_WARN_PRINT("%s:%d - %s\n", __FUNCTION__, __LINE__, "WARNING - " #X " failed!\n"); }
173 #endif // NO_DEBUG
174 
175 #define LOAD_COLOR(X,Y,Z) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) <= 0){ WB_WARN_PRINT("%s - WARNING: can't find color %s, using default value %s\n", __FUNCTION__, X, Z); strcpy(Y,Z); }
176 
177 int MBInitGlobal(void)
178 {
179  Colormap colormap;
180 
181  if(!pDefaultMenuFont)
182  {
183 // pDefaultMenuFont = XLoadQueryFont(WBGetDefaultDisplay(), MENU_FONT);
184  pDefaultMenuFont = WBLoadFont(WBGetDefaultDisplay(), MENU_FONT, MENU_FONT_SIZE, WBFontFlag_WT_BOLD);
185  if(!pDefaultMenuFont)
186  {
187  // sometimes there is no font alias, so try again
188  pDefaultMenuFont = WBLoadFont(WBGetDefaultDisplay(), MENU_ALT_FONT, MENU_FONT_SIZE, WBFontFlag_WT_BOLD);
189 
190  if(!pDefaultMenuFont) // still nothing?
191  {
192  pDefaultMenuFont = WBLoadFont(WBGetDefaultDisplay(), "*", MENU_FONT_SIZE,
194  if(!pDefaultMenuFont) // still nothing?
195  {
196  pDefaultMenuFont = WBLoadFont(WBGetDefaultDisplay(), "*", MENU_FONT_SIZE,
198  if(!pDefaultMenuFont) // still nothing?
199  {
200  pDefaultMenuFont = WBLoadFont(WBGetDefaultDisplay(), MENU_FONT, MENU_FONT_SIZE, 0); // "ANY" style
201 
202  if(!pDefaultMenuFont) // still nothing?
203  {
204  pDefaultMenuFont = WBLoadFont(WBGetDefaultDisplay(), "*", MENU_FONT_SIZE,
206  if(!pDefaultMenuFont) // still nothing? REALLY????
207  {
208  pDefaultMenuFont = WBLoadFont(WBGetDefaultDisplay(), "*", MENU_FONT_SIZE, 0); // just give me something THAT size
209  }
210  }
211  }
212  }
213  }
214  }
215  }
216 
217  if(!pDefaultMenuFont)
218  {
219  pDefaultMenuFont = WBCopyFont(WBGetDefaultFont());
220 
221  if(!pDefaultMenuFont)
222  {
223  // MY give up. ~facepalm~
224 
225  WB_ERROR_PRINT("%s - no font, MENU FONT is %s,%s %d (menus will not be displayed)\n", __FUNCTION__, MENU_FONT, MENU_ALT_FONT, 13);
226  WBDumpFontInfo("*");
227 
228  return 0;
229  }
230  }
231 
232  if(!iInitColorFlag)
233  {
234  char szMenuFG[16], szMenuBG[16], szMenuActiveFG[16], szMenuActiveBG[16],
235  szMenuBorder1[16], szMenuActiveDisabledFG[16], szMenuDisabledFG[16];
236  static const char*szMenuBorder2="#FFFFFF", *szMenuBorder3="#9C9A94";
237 
238  colormap = DefaultColormap(WBGetDefaultDisplay(), DefaultScreen(WBGetDefaultDisplay()));
239  LOAD_COLOR("*Menu.foreground",szMenuFG,"#000000");
240  LOAD_COLOR("*Menu.background",szMenuBG,"#DCDAD5");
241  LOAD_COLOR("*Menu.activeForeground",szMenuActiveFG,"#FFFFFF");
242  LOAD_COLOR("*Menu.activeBackground",szMenuActiveBG,"#4B6983");
243  LOAD_COLOR("*Menu.disabledForeground",szMenuDisabledFG,"#808080"); // TODO: verify these Menu.xxx strings for DISABLED
244  LOAD_COLOR("*Menu.disabledForeground",szMenuActiveDisabledFG,"#808080");
245  LOAD_COLOR("*borderColor",szMenuBorder1,"#000000");
246 
247  // NOTE: 'DEBUG_VALIDATE' (defined above) simply validates the return and prints a message if it failed.
248  // in a release build, the code is still executed, but no error checks are performed on the return value
249 
250  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuFG, &clrMenuFG));
251  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuFG)); // NOTE: do I need 'XFreeColors' for these ?
252 
253  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuBG, &clrMenuBG));
254  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuBG));
255 
256  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuActiveFG, &clrMenuActiveFG));
257  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuActiveFG)); // NOTE: do I need 'XFreeColors' for these ?
258 
259  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuActiveBG, &clrMenuActiveBG));
260  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuActiveBG));
261 
262  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuDisabledFG, &clrMenuDisabledFG));
263  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuDisabledFG));
264 
265  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuActiveDisabledFG, &clrMenuActiveDisabledFG));
266  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuActiveDisabledFG));
267 
268  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuBorder1, &clrMenuBorder1));
269  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuBorder1));
270 
271  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuBorder2, &clrMenuBorder2));
272  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuBorder2));
273 
274  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuBorder3, &clrMenuBorder3));
275  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuBorder3));
276 
277  // TODO: make sure I was able to actually allocate these colors
278 
279  iInitColorFlag = 1;
280  }
281 
282  if(aMENU_RESIZE == None)
283  {
284  aMENU_RESIZE = WBGetAtom(WBGetDefaultDisplay(), "WB_MENU_RESIZE");
285  }
286 
287  if(aMENU_ACTIVATE == None)
288  {
289  aMENU_ACTIVATE = WBGetAtom(WBGetDefaultDisplay(), "WB_MENU_ACTIVATE");
290  }
291 
292  if(aMENU_DISPLAY_POPUP == None)
293  {
294  aMENU_DISPLAY_POPUP = WBGetAtom(WBGetDefaultDisplay(), "WB_MENU_DISPLAY_POPUP");
295  }
296 
297  return 1;
298 }
299 
300 XFontStruct *MBGetDefaultMenuFont(void)
301 {
302  return pDefaultMenuFont;
303 }
304 
305 WBMenuBarWindow *MBCreateMenuBarWindow(Window wIDParent, const char *pszResource,
306 // int iX, int iY, int *piWidth, int *piHeight,
307  int iFlags)
308 {
309  Display *pDisplay;
310  WBMenuBarWindow *pRval = NULL;
311  unsigned long fg, bg, bd; /* Pixel values */
312  XSetWindowAttributes xswa; /* Temporary Set Window Attribute struct */
313  XSizeHints xsh; /* Size hints for window manager - width of owner + 2 * height of font */
314  XWMHints xwmh;
315  WB_RECT rct;
316  XFontStruct *pFS;
317 
318 
319  // initialize global menu objects
320  if(!MBInitGlobal())
321  return NULL;
322 
323  if(wIDParent > 0)
324  pDisplay = WBGetWindowDisplay(wIDParent);
325  else
326  pDisplay = WBGetDefaultDisplay();
327 
328  // step 1: create the window
329 
330  bd = clrMenuBorder1.pixel; // BlackPixel(pDisplay, DefaultScreen(pDisplay));
331  fg = clrMenuFG.pixel;// BlackPixel(pDisplay, DefaultScreen(pDisplay)); // black foreground for menus (always)
332  bg = clrMenuBG.pixel;// WhitePixel(pDisplay, DefaultScreen(pDisplay)); // white background for menus (for now)
333 
334  WBGetClientRect(wIDParent, &rct);
335  pFS = WBGetWindowFontStruct(wIDParent);
336 
337  if(!pDefaultMenuFont && !pFS)
338  {
339  WB_ERROR_PRINT("%s - * BUG * no font!\n", __FUNCTION__);
340  return 0;
341  }
342  else if(pDefaultMenuFont)
343  pFS = pDefaultMenuFont;
344 
345 
346  // set size hints to match client area, upper left corner (always)
347  xsh.flags = (PPosition | PSize);
348  xsh.x = rct.left;
349  xsh.y = rct.top;
350  xsh.width = rct.right - rct.left;
351  if(!pFS)
352  xsh.height = 32;
353  else
354  xsh.height = 2 * (pFS->max_bounds.ascent + pFS->max_bounds.descent);
355 
356  if(xsh.height > (rct.bottom - rct.top))
357  xsh.height = rct.bottom - rct.top;
358 
359  memset(&xswa, 0, sizeof(xswa));
360 
361  xswa.border_pixel = bd;
362  xswa.background_pixel = bg;
363  xswa.colormap = DefaultColormap(pDisplay, DefaultScreen(pDisplay));
364  xswa.bit_gravity = CenterGravity;
365 
366  pRval = (WBMenuBarWindow *)WBAlloc(sizeof(*pRval));
367 
368  if(!pRval)
369  return NULL;
370 
371  pRval->ulTag = MENU_WINDOW_TAG;
372 
373  pRval->pMenu = MBCreateMenu(-1, 0, pszResource, WBMENU_RESERVE_DEFAULT);
374 
375  if(!pRval->pMenu)
376  {
377  WB_WARN_PRINT("%s - WARNING: pMenu is NULL in WBMenuBarWindow object\n", __FUNCTION__);
378  }
379 
380  pRval->wSelf = WBCreateWindow(pDisplay, wIDParent, MBMenuWinEvent, "MenuBar",
381  xsh.x, xsh.y, xsh.width, xsh.height, 0,
382  InputOutput,
383  CWBorderPixel | CWBackPixel | CWColormap | CWBitGravity,
384  &xswa);
385 
386  if(pRval->wSelf == -1)
387  {
388  WBFree(pRval);
389  return NULL;
390  }
391 
392  pRval->wOwner = wIDParent;
393  pRval->iPrevSel = pRval->iSelected = -1; // 'none'
394  pRval->iFlags = iFlags; // make a copy of them (for now)
395 
396  // calculate the initial position and height of the menu bar within the window
397  pRval->iX = xsh.x + 4;
398  pRval->iY = xsh.y + 4;
399  pRval->iWidth = xsh.width - 8;
400  pRval->iHeight = xsh.height - 8;
401 
402  // establish this window as NEVER getting the input focus
403  bzero(&xwmh, sizeof(xwmh));
404  xwmh.flags = InputHint;
405  xwmh.input = 0; // this represents 'None' (or 'Globally Active' if WM_TAKE_FOCUS is present)
406 
407  WBSetWMProperties(pRval->wSelf, NULL, NULL, &xwmh, NULL);
408 
409  WBSetWindowDefaultCursor(pRval->wSelf, XC_hand2); //XC_top_left_arrow);
410  WBSetWindowData(pRval->wSelf, 0, pRval); // a pointer back to me. must do before next part...
411  WBRegisterMenuCallback(pRval->wSelf, MBMenuParentEvent);
412 
413  WBCreateWindowDefaultGC(pRval->wSelf, fg, bg);
414 // WBSetWindowFontStruct(pRval->wSelf, XLoadQueryFont(WBGetDefaultDisplay(), MENU_FONT));
415  WBSetWindowFontStruct(pRval->wSelf, WBCopyFont(pFS));
416 
417  // TODO: should I _NOT_ do this if I clear the input focus flag?
418  XSelectInput(pDisplay, pRval->wSelf,
420 
421  WBAddMenuWindow(wIDParent, pRval->wSelf); // add this menu to this window
422  // this handles menu hotkeys and resize events for me
423 // {
424 // Atom a1, a2;
425 // a1 = XInternAtom(pDisplay, "_NET_WM_WINDOW_TYPE", False);
426 // a2 = XInternAtom(pDisplay, "_NET_WM_WINDOW_TYPE_MENU", False);
427 // XChangeProperty(pDisplay, pRval->wSelf, a1, XA_ATOM, 32, PropModeReplace, (unsigned char *)&a2, 1);
428 // a1 = XInternAtom(pDisplay, "WM_TRANSIENT_FOR", False);
429 // XChangeProperty(pDisplay, pRval->wSelf, a1, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&wIDParent, 1);
430 // }
431 
432  WBMapWindow(pDisplay, pRval->wSelf); // make window visible
433 
434 
435  // TODO: set up font, GS, callback, etc.
436 
437 
438  return(pRval);
439 }
440 
441 static int __FindMenuBarWindowCallback(Window wID, void *pData)
442 {
444 
445  if(pMB && (void *)(pMB->pMenu) == pData)
446  {
447  return 1;
448  }
449 
450  return 0;
451 }
452 
453 WBMenuBarWindow *MBFindMenuBarWindow(WBMenu *pMenu) // find first (active) window that uses 'pMenu'
454 {
455  Window wID = WBLocateWindow(__FindMenuBarWindowCallback, (void *)pMenu);
456 
457  if(wID)
458  {
459  return MBGetMenuBarWindowStruct(wID);
460  }
461 
462  return NULL;
463 }
464 
465 void MBReCalcMenuBarWindow(WBMenuBarWindow *pMenuBar /*, int iX, int iY, int *piWidth, int *piHeight*/)
466 {
467  // TODO: update structure's position/size cache?
468 
469  WBInvalidateGeom(pMenuBar->wSelf, NULL, 1); // force a re-paint
470 }
471 
473 {
474  if(!pMenuBar || pMenuBar->ulTag != MENU_WINDOW_TAG)
475  {
476  return;
477  }
478 
479  // TODO: any special notifications??
480 
481  if(pMenuBar->pMenu)
482  {
483  MBDestroyMenu(pMenuBar->pMenu); // TODO: make this do reference counting?
484  pMenuBar->pMenu = NULL; // by convention, do this
485  }
486 
487  WBDestroyWindow(pMenuBar->wSelf);
488  WBFree(pMenuBar);
489 }
490 
491 void MBSetMenuBarMenuResource(WBMenuBarWindow *pMenuBar, const char *pszResource)
492 {
493 WBMenu *pMenu;
494 
495  if(!pMenuBar || pMenuBar->ulTag != MENU_WINDOW_TAG)
496  {
497  return;
498  }
499 
500  pMenu = MBCreateMenu(-1, 0, pszResource, WBMENU_RESERVE_DEFAULT);
501 
502  if(!pMenu)
503  {
504  WB_ERROR_PRINT("ERROR: %s - unable to create menu\n", __FUNCTION__);
505  return;
506  }
507 
508  if(pMenuBar->pMenu)
509  {
510  MBDestroyMenu(pMenuBar->pMenu);
511  }
512 
513  pMenuBar->pMenu = pMenu;
514 
515  pMenuBar->iSelected = -1;
516  pMenuBar->iPrevSel = -1;
517 
518  MBReCalcMenuBarWindow(pMenuBar);
519 }
520 
521 
522 // this next callback is assigned via WBRegisterMenuCallback
523 int MBMenuParentEvent(Window wIDMenu, XEvent *pEvent)
524 {
525  Display *pDisplay = WBGetWindowDisplay(wIDMenu);
526  WBMenuBarWindow *pSelf = MBGetMenuBarWindowStruct(wIDMenu);
527 
528  if(!pSelf)
529  {
530  WB_ERROR_PRINT("%s - pSelf is NULL\n", __FUNCTION__);
531  return 0;
532  }
533 
534  if(pEvent->type == ConfigureNotify &&
535  pSelf->wOwner == pEvent->xconfigure.window)
536  {
537  XWindowChanges chg;
538  chg.width = pEvent->xconfigure.width;
539  XConfigureWindow(pDisplay, wIDMenu, CWWidth, &chg);
540 
541  WBInvalidateGeom(wIDMenu, NULL, 0);
542 
543  return 0;
544  }
545 
546  // TODO: menu hot keys
547 
548  // TODO: mouse motion ?
549 
550  return 0; // not handled
551 }
552 
553 static void MBMenuHandleMenuItem(Display *pDisplay, WBMenuBarWindow *pSelf, WBMenu *pMenu, WBMenuItem *pItem)
554 {
555 Window wID = pSelf->wSelf;
556 
557  if(pItem->iAction & WBMENU_POPUP_HIGH_BIT)
558  {
559  XClientMessageEvent evt;
560 
561  // post a high-priority message to myself to display the menu
562 
563  bzero(&evt, sizeof(evt));
564  evt.type = ClientMessage;
565  evt.display = pDisplay;
566  evt.window = wID;
567  evt.message_type = aMENU_DISPLAY_POPUP;
568  evt.format = 32;
569  evt.data.l[0] = pItem->iAction & WBMENU_POPUP_MASK;
570  evt.data.l[1] = pItem->iPosition; // minimum extent
571  evt.data.l[2] = pItem->iPosition + pItem->iTextWidth; // maximum extent
572 
573  WBPostPriorityEvent(wID, (XEvent *)&evt);
574 
575  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu,
576  "%s - posting client event message to display popup menu\n", __FUNCTION__);
577  }
578  else
579  {
580  XClientMessageEvent evt;
581 
582  bzero(&evt, sizeof(evt));
583  evt.type = ClientMessage;
584 
585  evt.display = pDisplay;
586  evt.window = pSelf->wOwner;
587  evt.message_type = aMENU_COMMAND;
588  evt.format = 32; // always
589  evt.data.l[0] = pItem->iAction; // menu command message ID
590  evt.data.l[1] = WBCreatePointerHash(pMenu); // hash of pointer to menu object
591  evt.data.l[2] = wID; // window ID of menu bar
592 
593  WBPostEvent(pSelf->wOwner, (XEvent *)&evt);
594 
595  pSelf->iSelected = -1; // after handling the message I de-select the menu item
596 
597  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Menu | DebugSubSystem_Event,
598  "%s - Post Event: %08xH %08xH %pH %08xH\n", __FUNCTION__,
599  (int)aMENU_COMMAND, (int)pItem->iAction,
600  pMenu, (int)wID);
601 
603  }
604 }
605 
606 #if 0 /* currently not used, reserved, consider removing it if not needed */
607 static int MBMenuHandleMenuItemUI(Display *pDisplay, WBMenuBarWindow *pSelf, WBMenu *pMenu, WBMenuItem *pItem)
608 {
609  XClientMessageEvent evt;
610  int iRval;
611 
612  bzero(&evt, sizeof(evt));
613  evt.type = ClientMessage;
614 
615  evt.display = pDisplay;
616  evt.window = pSelf->wOwner;
617  evt.message_type = aMENU_UI_COMMAND;
618  evt.format = 32; // always
619 
620  // sending pointer data - since this is internally 'sent' I don't have to
621  // worry about async causing pointers to become invalid
622 
623  evt.data.l[0] = pItem->iAction; // menu command message ID (needed to identify menu)
624 
625  evt.data.l[1] = WBCreatePointerHash(pMenu);
626  evt.data.l[2] = WBCreatePointerHash(pItem);
627  evt.data.l[3] = 0; // this is a sort of 'flag' saying I'm using a pointer hash
628  evt.data.l[4] = 0;
629 
630  iRval = WBWindowDispatch(pSelf->wOwner, (XEvent *)&evt); // 'send event'
631 
632  WBDestroyPointerHash(pMenu);
633  WBDestroyPointerHash(pItem); // clean them up as I'm done with them now
634 
635  return iRval;
636 }
637 #endif // 0
638 
639 
640 // this next callback is assigned via WBRegisterWindowCallback
641 int MBMenuWinEvent(Window wID, XEvent *pEvent)
642 {
643 Display *pDisplay = WBGetWindowDisplay(wID);
645 WBMenu *pMenu = pSelf ? pSelf->pMenu : NULL;
646 WBMenuItem *pItem;
647 WB_GEOM geom;
648 Window wIDParent;
649 XEvent xevt;
650 int i1; //, iHPos, iVPos;
651 
652 
653  if(pSelf && pEvent->type == Expose)
654  {
655  return MenuBarDoExposeEvent((XExposeEvent *)pEvent, pMenu, pDisplay, wID, pSelf);
656  }
657 
658  if(pSelf && pEvent->type == ClientMessage && pEvent->xclient.message_type == aMENU_RESIZE)
659  {
660  WB_RECT rct;
661  XWindowAttributes xswa;
662 
663  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Menu | DebugSubSystem_Window | DebugSubSystem_Event,
664  "%s - Resize Request Message for %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
665 
666  WBGetClientRect(pSelf->wOwner, &rct);
667 
668  XGetWindowAttributes(pDisplay, wID, &xswa);
669  xswa.width = rct.right - rct.left - xswa.border_width;
670  // assume height doesn't change
671 
672  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Window | DebugSubSystem_Event,
673  "%s - perform resize request for %d (%08xH) %d, %d\n",
674  __FUNCTION__, (int)wID, (int)wID, xswa.width, xswa.height);
675 
676  XResizeWindow(pDisplay, wID, xswa.width, xswa.height);
677  return 0; // still requesting default handling on this one
678  }
679 
680  // process 'destroy' events
681  if(pSelf && pEvent->type == DestroyNotify)
682  {
683  if(pEvent->xdestroywindow.window == wID) // destroying myself?
684  {
685  WBRemoveMenuWindow(-1, wID); // removes from ALL windows
686  return 1; // processed
687  }
688  }
689 
690 
691  wIDParent = WBGetParentWindow(wID); // grab the parent window's ID
692 
693  if(pSelf &&
694  (pEvent->type == KeyPress ||
695  pEvent->type == KeyRelease))
696  {
697  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Window | DebugSubSystem_Event,
698  "%s - keypress/release in menu_bar\n", __FUNCTION__);
699 
700  if(pEvent->type == KeyPress)
701  {
702  // see if this is a hotkey for a menu item
703 // if(pEvent->xkey.
704  }
705 
706  // send to parent window for processing
707 
708  memcpy(&xevt, pEvent, sizeof(*pEvent));
709  xevt.xkey.window = wIDParent;
710 
711  WBWindowDispatch(wIDParent, &xevt); // have the parent process it
712 
713  // NEXT, post a 'set focus' request to the parent so it gets the other events
714 
715  bzero(&xevt, sizeof(xevt));
716  xevt.type = ClientMessage;
717 
718  xevt.xclient.display = pDisplay;
719  xevt.xclient.window = wIDParent;
720  xevt.xclient.message_type = aSET_FOCUS;
721  xevt.xclient.format = 32; // always
722  xevt.xclient.data.l[0] = 0; // default
723 
724  WBPostEvent(wIDParent, &xevt); // post this (parent should fix it now, asynchronously)
725  }
726 
727  if(pSelf &&
728  (pEvent->type == ButtonPress ||
729  pEvent->type == ButtonRelease ||
730  pEvent->type == MotionNotify))
731  {
732  int iX, iY; // mousie coordinates (when needed)
733 
734  // mousie clickie - yay!
735 
736  if(pEvent->type == MotionNotify)
737  {
738  WBXlatCoordPoint(pEvent->xmotion.window, pEvent->xmotion.x, pEvent->xmotion.y,
739  wID, &iX, &iY);
740  if(pEvent->xmotion.state & Button1Mask) // left drag?
741  {
742  // todo - highlight selections by simulating button press, drop into a popup, etc.
743  }
744  }
745  else if(pEvent->type == ButtonPress && pMenu)
746  {
747  WBXlatCoordPoint(pEvent->xbutton.window, pEvent->xbutton.x, pEvent->xbutton.y,
748  wID, &iX, &iY);
749  if(pEvent->xbutton.state & (Button2Mask | Button3Mask | Button4Mask | Button5Mask
750  | ShiftMask | LockMask | ControlMask))
751  {
752  // this click I ignore (for now)
753  }
754  else if(iY >= pSelf->iY && iY <= (pSelf->iY + pSelf->iHeight))
755  {
756  for(i1=0; pMenu->ppItems && i1 < pMenu->nItems; i1++)
757  {
758  WBMenuItem *pItem = pMenu->ppItems[i1];
759  if(!pItem)
760  continue;
761 
762  if(pItem->iPosition <= iX && pItem->iTextWidth + pItem->iPosition >= iX) // between them
763  {
764  WB_GEOM geom;
765 
766  pSelf->iPrevSel = pSelf->iSelected = i1; // indicate the item that's selected
767 
768  geom.x = pItem->iPosition;
769  geom.y = pSelf->iY;
770  geom.width = pItem->iTextWidth;
771  geom.height = pSelf->iHeight;
772  geom.border = 0;
773 
774  WBInvalidateGeom(wID, &geom, TRUE); // force a re-paint (at some point in time)
775 
776  return 1;
777  }
778  }
779 
780  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Menu | DebugSubSystem_Mouse,
781  "%s - couldn't find the menu item - mouse at %d, %d\n", __FUNCTION__, iX, iY);
782 
783 let_parent_process_it:
784 
785  // mouse outside of menu means parent should re-take the focus
786  // So, post a 'set focus' request to the parent so it gets the other events
787 
788  bzero(&xevt, sizeof(xevt));
789  xevt.type = ClientMessage;
790 
791  xevt.xclient.display = pDisplay;
792  xevt.xclient.window = wIDParent;
793  xevt.xclient.message_type = aSET_FOCUS;
794  xevt.xclient.format = 32; // always
795  xevt.xclient.data.l[0] = 0; // default
796 
797  WBPostEvent(wIDParent, &xevt); // post this (parent should fix it now, asynchronously)
798  }
799  else
800  {
801  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Menu | DebugSubSystem_Mouse,
802  "%s - Mouse pointer is out of range - %d, %d, %d, %d, %d, %d\n",
803  __FUNCTION__, iX, iY, pSelf->iX, pSelf->iY, pSelf->iWidth, pSelf->iHeight);
804 
805  goto let_parent_process_it;
806  }
807  }
808  else // if(pEvent->type == ButtonRelease)
809  {
810  i1 = pSelf->iSelected;
811 
812  if(i1 < 0 || i1 >= pMenu->nItems)
813  {
814  pSelf->iSelected = -1;
815  WBInvalidateGeom(wID, NULL, TRUE);
816  return 0; // nothing selected
817  }
818 
819  pItem = pMenu->ppItems[i1];
820  if(!pItem)
821  {
822  pSelf->iSelected = -1;
823  WBInvalidateGeom(wID, NULL, TRUE);
824  return 0;
825  }
826 
827  geom.x = pItem->iPosition;
828  geom.y = pSelf->iY;
829  geom.width = pItem->iTextWidth;
830  geom.height = pSelf->iHeight;
831  geom.border = 0;
832 
833  WBInvalidateGeom(wID, &geom, TRUE); // force a re-paint (at some point in time)
834 
835  // determine what the correct action for this menu item is...
836  if(pItem->iAction == -1)
837  {
838  pSelf->iSelected = -1;
839  WBInvalidateGeom(wID, NULL, TRUE);
840  return 0; // "not handled"
841  }
842 
843  MBMenuHandleMenuItem(pDisplay, pSelf, pMenu, pItem);
844 
845  WBInvalidateGeom(wID, &geom, TRUE); // force re-paint AGAIN
846 
847  return 1;
848  }
849  }
850 
851  if(pEvent && pEvent->type == ClientMessage)
852  {
853  if(((XClientMessageEvent *)pEvent)->message_type == aMENU_ACTIVATE)
854  {
855  int iMenuItemIndex = ((XClientMessageEvent *)pEvent)->data.l[1]; // index, prev, or next
856 
858  // TODO: deal with dynamic menu elements
860 
861  pItem = NULL;
862 
863  if(iMenuItemIndex >= 0 && iMenuItemIndex < pMenu->nItems &&
864  ((XClientMessageEvent *)pEvent)->data.l[0]) // prev/next or 'absolute' indicator - *NOT* equal to 'NULL'
865  {
866  // select the menu item specified by the index in data.l[1]
867 
868  if(pSelf->iSelected != iMenuItemIndex &&
869  pSelf->iSelected >= 0 && pSelf->iSelected < pMenu->nItems)
870  {
871  pItem = pMenu->ppItems[pSelf->iSelected];
872  geom.x = pItem->iPosition;
873  geom.y = pSelf->iY;
874  geom.width = pItem->iTextWidth;
875  geom.height = pSelf->iHeight;
876  geom.border = 0;
877 
878  WBInvalidateGeom(wID, &geom, FALSE); // invalidate rect for currently selected menu item
879  }
880 
881  pSelf->iSelected = iMenuItemIndex;
882  pItem = pMenu->ppItems[pSelf->iSelected];
883 
884  // this next part is a 'self-check' which helps to validate the menu item pointer
885  // it does not actually USE the menu item pointer passed in the event (it's likely to get truncated)
886 #warning potentially dangerous code - this should be reviewed an re-written
887  if((unsigned long)pItem == (unsigned long)(((XClientMessageEvent *)pEvent)->data.l[0]))
888  {
889  geom.x = pItem->iPosition;
890  geom.y = pSelf->iY;
891  geom.width = pItem->iTextWidth;
892  geom.height = pSelf->iHeight;
893  geom.border = 0;
894 
895  pSelf->iPrevSel = pSelf->iSelected;
896 
897  MBMenuHandleMenuItem(pDisplay, pSelf, pMenu, pItem);
898 
899  WBInvalidateGeom(wID, &geom, TRUE); // force window update
900 
901  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
902  "%s - MENU_ACTIVATE event, activate menu item, %d %p\n",
903  __FUNCTION__, iMenuItemIndex, pItem);
904 
905  return 1; // handled
906  }
907  else
908  {
909  pSelf->iSelected = -1;
910  WBInvalidateGeom(wID, NULL, TRUE);
911  }
912  }
913  else if(/*iMenuItemIndex &&*/ !((XClientMessageEvent *)pEvent)->data.l[0])
914  {
915  // select the PREVIOUS or the NEXT menu item
916 
917  int iOldSel = pSelf->iSelected;
918 
919  if(iOldSel < 0 || iOldSel >= pMenu->nItems)
920  {
921  iOldSel = pSelf->iPrevSel;
922  }
923  if(iOldSel >=0 && iOldSel < pMenu->nItems)
924  {
925  pSelf->iSelected = iOldSel; // re-initialize
926 
927  pItem = NULL; // initially
928 
929  if(iMenuItemIndex == 0)
930  {
931  if(pMenu->nItems > 0) // just in case
932  {
933  pSelf->iSelected = 0;
934 
935  goto try_select_next; // a simple way to implement this
936  }
937 
938  pItem = NULL; // make sure
939  }
940  else if(iMenuItemIndex > 0)
941  {
942  while(pSelf->iSelected < (pMenu->nItems - 1))
943  {
944  pSelf->iSelected++;
945 try_select_next:
946  pItem = pMenu->ppItems[pSelf->iSelected];
947 
948  if(pItem->iAction != WBMENU_SEPARATOR)
949  {
950  break;
951  }
952 
953  pItem = NULL;
954  }
955  }
956  else
957  {
958  while(pSelf->iSelected > 0)
959  {
960  pSelf->iSelected--;
961  pItem = pMenu->ppItems[pSelf->iSelected];
962 
963  if(pItem->iAction != WBMENU_SEPARATOR)
964  {
965  break;
966  }
967 
968  pItem = NULL;
969  }
970  }
971 
972  if(!pItem)
973  {
974  pSelf->iSelected = iOldSel;
975  pItem = pMenu->ppItems[pSelf->iSelected];
976  }
977 
978  pSelf->iPrevSel = pSelf->iSelected;
979  }
980  else
981  {
982  pSelf->iSelected = pSelf->iPrevSel = 0;
983  while(pSelf->iSelected < pMenu->nItems)
984  {
985  pItem = pMenu->ppItems[pSelf->iSelected];
986  if(pItem->iAction != WBMENU_SEPARATOR)
987  {
988  break;
989  }
990  pItem = NULL;
991  }
992 
993  if(!pItem)
994  {
995  pSelf->iSelected = -1; // fallback for bad menus
996  // leave pItem set to NULL;
997  }
998  else
999  {
1000  pSelf->iPrevSel = pSelf->iSelected;
1001  }
1002  }
1003 
1004  if(pItem)
1005  {
1006  MBMenuHandleMenuItem(pDisplay, pSelf, pMenu, pItem);
1007  WBInvalidateGeom(wID, NULL, FALSE); // re-paint, at some point in time
1008 
1009  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1010  "%s - MENU_ACTIVATE event, select prev/next, %d %d\n",
1011  __FUNCTION__, iMenuItemIndex, pSelf->iSelected);
1012  return 1; // handled
1013  }
1014  }
1015 
1016  WB_WARN_PRINT("%s - MENU_ACTIVATE event, invalid menu information, %d %d %p %p\n",
1017  __FUNCTION__, iMenuItemIndex, pMenu->nItems,
1018  (void *)pItem, (void *)(((XClientMessageEvent *)pEvent)->data.l[0]));
1019  }
1020  else if(((XClientMessageEvent *)pEvent)->message_type == aMENU_DISPLAY_POPUP)
1021  {
1022  WBMenuPopupWindow *pPopup;
1023  int iMenuItem = ((XClientMessageEvent *)pEvent)->data.l[0];
1024  int iPosition = ((XClientMessageEvent *)pEvent)->data.l[1];
1025  int iRightPos = ((XClientMessageEvent *)pEvent)->data.l[2];
1026 
1027  for(i1=0; i1 < pMenu->nPopups; i1++)
1028  {
1029  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu,
1030  "%s - popup menu id = %d\n", __FUNCTION__,
1031  (pMenu->ppPopups[i1]->iMenuID & WBMENU_POPUP_MASK));
1032 
1033  if(pMenu->ppPopups[i1] &&
1034  (pMenu->ppPopups[i1]->iMenuID & WBMENU_POPUP_MASK) == iMenuItem)
1035  {
1036  // TODO: before moving mouse, see if it's already within "the band"
1037  // move the mouse cursor to the center of the menu item (if it's in another window)
1039  XWarpPointer(pDisplay, None, pSelf->wSelf, 0, 0, 0, 0,
1040  (iPosition + iRightPos) / 2, pSelf->iY + pSelf->iHeight / 2);
1042 
1043  pPopup = MBCreateMenuPopupWindow(pSelf->wSelf, pSelf->wOwner, pMenu->ppPopups[i1],
1044  iPosition, pSelf->iY + pSelf->iHeight, 0);
1045 
1046  if(pPopup)
1047  {
1048 #ifndef NO_DEBUG
1049  int i2;
1050 #endif // NO_DEBUG
1051 
1052  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu,
1053  "%s - Displaying popup menu id %d\n", __FUNCTION__, iMenuItem);
1054 
1055 #ifndef NO_DEBUG
1056  i2 =
1057 #endif // NO_DEBUG
1058  MBMenuDoModal(pPopup);
1059 
1060 #ifndef NO_DEBUG
1061  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu,
1062  "%s - Done with popup menu id %d, return %d\n", __FUNCTION__, iMenuItem, i2);
1063 #endif // NO_DEBUG
1064  }
1065  else
1066  {
1067  WB_WARN_PRINT("%s - Unable to create popup menu %d\n", __FUNCTION__, iMenuItem);
1068  }
1069 
1070  pSelf->iSelected = -1; // necessary after modality returns
1071  WBInvalidateGeom(pSelf->wSelf, NULL, 1); // re-paint (sort of a bug fix)
1072 
1073  break;
1074  }
1075  }
1076 
1077  if(i1 >= pMenu->nPopups)
1078  {
1079  WB_WARN_PRINT("%s - Unable to locate popup menu %d\n", __FUNCTION__, iMenuItem);
1080  }
1081  }
1082  }
1083 
1084 
1085  return 0; // not handled
1086 }
1087 
1088 
1089 static int MenuBarDoExposeEvent(XExposeEvent *pEvent, WBMenu *pMenu, Display *pDisplay,
1090  Window wID, WBMenuBarWindow *pSelf)
1091 {
1092  int i1, i2, iHPos, iVPos;
1093  XWindowAttributes xwa; /* Temp Get Window Attribute struct */
1094  XFontStruct *pOldFont, *pFont;
1095  XPoint xpt[3];
1096  GC gc; // = WBGetWindowDefaultGC(wID);
1097  XGCValues xgc;
1098  WB_GEOM geomPaint;
1099  char tbuf[128];
1100 
1101 // // Remove any other pending Expose events from the queue to avoid multiple repaints.
1102 // // (I don't do this now since I'm already consolidating expose events)
1103 // while(!bQuitFlag && XCheckTypedWindowEvent(pDisplay, wID, Expose, pEvent))
1104 // ;
1105 
1106 // if(pEvent->xexpose.count != 0)
1107 // {
1108 //
1109 // }
1110 
1111  if (XGetWindowAttributes(pDisplay, wID, &xwa) == 0)
1112  {
1113  WB_ERROR_PRINT("%s - * BUG * unable to get window attributes\n", __FUNCTION__);
1114  return 0;
1115  }
1116 
1117  pFont = pOldFont = WBGetWindowFontStruct(wID);
1118 
1119  if(!pDefaultMenuFont && !pOldFont)
1120  {
1121  WB_ERROR_PRINT("%s - * BUG * no font!\n", __FUNCTION__);
1122  return 0;
1123  }
1124  else if(pDefaultMenuFont)
1125  pFont = pDefaultMenuFont;
1126 
1127  // get graphics context copy and begin painting
1128  gc = WBBeginPaint(wID, pEvent, &geomPaint);
1129  if(!gc)
1130  {
1131  WB_ERROR_PRINT("%s - * BUG * no graphics context!\n", __FUNCTION__);
1132  return 0;
1133  }
1134 
1135  WBClearWindow(wID, gc);
1136 
1137  xgc.font = pFont->fid;
1139  XChangeGC(pDisplay, gc, GCFont, &xgc);
1140 
1142 
1143  // paint the 3D-looking border
1144  XSetForeground(pDisplay, gc, clrMenuBorder2.pixel);
1145  xpt[0].x=xwa.border_width;
1146  xpt[0].y=xwa.height-1-2*xwa.border_width - 1; // exclude first point
1147  xpt[1].x=xwa.border_width;
1148  xpt[1].y=xwa.border_width;
1149  xpt[2].x=xwa.width-1-2*xwa.border_width - 1; // exclude last point
1150  xpt[2].y=xwa.border_width;
1151 
1152  XDrawLines(pDisplay, wID, gc, xpt, 3, CoordModeOrigin);
1153 
1154  XSetForeground(pDisplay, gc, clrMenuBorder3.pixel);
1155  xpt[0].x=xwa.width-1-2*xwa.border_width;
1156  xpt[0].y=xwa.border_width + 1; // exclude first point
1157  xpt[1].x=xwa.width-1-2*xwa.border_width;
1158  xpt[1].y=xwa.height-1-2*xwa.border_width;
1159  xpt[2].x=xwa.border_width + 1; // exclude final point
1160  xpt[2].y=xwa.height-1-2*xwa.border_width;
1161 
1162  XDrawLines(pDisplay, wID, gc, xpt, 3, CoordModeOrigin);
1163 
1164  // painting the menu items
1165 
1166  i2 = XTextWidth(pFont, " ", 2); // width of 2 spaces
1167  iVPos = pFont->max_bounds.ascent + pFont->max_bounds.descent; // font height
1168  iVPos = (xwa.height - iVPos) >> 1; // half of the difference - top of text
1169  iVPos += pFont->max_bounds.ascent;
1170 
1171  // update the width and height of the menu bar within the window
1172  pSelf->iX = i2;
1173  pSelf->iY = iVPos - (pFont->max_bounds.ascent + 1);
1174  pSelf->iWidth = i2; // initially
1175  pSelf->iHeight = pFont->max_bounds.ascent + pFont->max_bounds.descent + 1;
1176 
1177  XSetForeground(pDisplay, gc, clrMenuFG.pixel);
1178 
1179  for(i1=0, iHPos = i2; pMenu && pMenu->ppItems && i1 < pMenu->nItems; i1++)
1180  {
1181  WBMenuItem *pItem = pMenu->ppItems[i1];
1182  const char *szText;
1183  int iU1=0, iU2=0;
1184 
1185  if(!pItem)
1186  continue;
1187 
1188 // MBMenuHandleMenuItemUI(pDisplay, pSelf, pMenu, pItem); TODO - grey things maybe?
1189 
1190  if(i1 == pSelf->iSelected) // selected item
1191  {
1192  XSetForeground(pDisplay, gc, clrMenuActiveBG.pixel);
1193  XSetBackground(pDisplay, gc, clrMenuActiveBG.pixel);
1194 
1195  XFillRectangle(pDisplay, wID, gc, pItem->iPosition, pSelf->iY, pItem->iTextWidth, pSelf->iHeight + 2);
1196 
1197  XSetForeground(pDisplay, gc, clrMenuActiveFG.pixel);
1198  }
1199 
1200  szText = pItem->data + pItem->iMenuItemText;
1201 
1202  if(strchr(szText, '_'))
1203  {
1204  char *p1;
1205  strcpy(tbuf, szText);
1206  p1 = tbuf;
1207  while(*p1)
1208  {
1209  if(*p1 == '_')
1210  {
1211  *p1 = 0;
1212 
1213  if(p1 == tbuf)
1214  iU1 = 0;
1215  else
1216  iU1 = XTextWidth(pFont, tbuf, p1 - tbuf);
1217 
1218  if(p1[1])
1219  {
1220  iU2 = XTextWidth(pFont, p1, 1);
1221  strcpy(p1, p1 + 1);
1222  }
1223  else
1224  {
1225  iU2 = iU1; // shouldn't happen
1226  break;
1227  }
1228  }
1229  p1++;
1230  }
1231 
1232  szText = tbuf;
1233  }
1234 
1235  if(pItem->iPosition < 0)
1236  pItem->iPosition = iHPos; // also needed for mousie/clickie
1237  if(pItem->iTextWidth < 0)
1238  pItem->iTextWidth = XTextWidth(pFont, szText, strlen(szText));
1239 
1240  // TODO: change string into a series of XTextItem structures and
1241  // then call XDrawText to draw the array of 'XTextItem's
1242  if(*szText)
1243  XDrawString(pDisplay, wID, gc, iHPos, iVPos, szText, strlen(szText));
1244 
1245  if(strlen(szText) < strlen(pItem->data + pItem->iMenuItemText))
1246  {
1247  xpt[0].x=iHPos + iU1 - 1;
1248  xpt[0].y=pSelf->iY + pSelf->iHeight - 1;
1249  xpt[1].x=iHPos + iU1 + iU2;
1250  xpt[1].y=xpt[0].y;
1251 
1252  XDrawLines(pDisplay, wID, gc, xpt, 2, CoordModeOrigin);
1253  }
1254 
1255  if(i1 == pSelf->iSelected) // selected item
1256  {
1257  XSetForeground(pDisplay, gc, clrMenuFG.pixel);
1258  XSetBackground(pDisplay, gc, clrMenuBG.pixel);
1259  }
1260 
1261  iHPos += pItem->iTextWidth + i2;
1262  }
1263 
1264  pSelf->iWidth = iHPos - i2; // update the width of the menu bar
1265 
1266  // by convention, restore original objects/state
1267 
1268  xgc.font = pOldFont->fid;
1270  XChangeGC(pDisplay, gc, GCFont, &xgc);
1271 
1272  XSetForeground(pDisplay, gc, WBGetWindowFGColor(wID)); // restore it at the end
1274 
1275  WBEndPaint(wID, gc);
1276 
1277  return 1; // processed
1278 }
1279 
1280 
XFontStruct * WBLoadFont(Display *pDisplay, const char *szFontName, int iFontSize, int iFlags)
load a font based on a font name, size, and font flags
Definition: font_helper.c:858
GC WBBeginPaint(Window wID, XExposeEvent *pEvent, WB_GEOM *pgBounds)
&#39;Paint&#39; helper, creates a GC for use in updating the window in an Expose event handler ...
#define WBMENU_POPUP_MASK
Definition: menu.h:80
structure for managing menu items
Definition: menu.h:185
&#39;window helper&#39; main header file for the X11workbench Toolkit API
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
void WBAddMenuWindow(Window wID, Window wIDMenu)
Add a MENU WINDOW to a (frame) window - more than one is possible, only one currently supported...
int MBInitGlobal(void)
Initialize global resources for Menu Bar windows.
Definition: menu_bar.c:177
int iHeight
menu bar height
Definition: menu_bar.h:124
Atom aMENU_DISPLAY_POPUP
Internal Client Message Atom for &#39;DISPLAY POPUP&#39; action.
Definition: menu_bar.c:165
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:196
int iAction
Definition: menu.h:139
bold font weight (mutually exclusive0
Definition: font_helper.h:417
int iPosition
horizontal/vertical position of menu (in pixels; assign &#39;-1&#39; to calculate it)
Definition: menu.h:143
unsigned int ulTag
tag indicating I&#39;m a &#39;Menu Bar&#39; window
Definition: menu_bar.h:115
int nPopups
The number of popup menu entries in the &#39;ppPopups&#39; array.
Definition: menu.h:196
#define WBMENU_SEPARATOR
Definition: menu.h:81
Window WBLocateWindow(WBLocateWindowCallback callback, void *pData)
Locate a window by enumerating with a callback function.
void MBDestroyMenuBarWindow(WBMenuBarWindow *pMenuBar)
Destroy a &#39;Menu Bar&#39; window.
Definition: menu_bar.c:472
Atom aMENU_UI_COMMAND
UI notifications sent by menus to owning Frame windows via ClientMessage using &#39;WBWindowDispatch&#39;.
#define WBMENU_RESERVE_DEFAULT
Definition: menu.h:76
#define WB_MOUSE_INPUT_MASK
&#39;Mouse&#39; input mask, bit flag for window creation
int WBWindowDispatch(Window wID, XEvent *pEvent)
Dispatches a window XEvent. May be called directly.
#define WB_KEYBOARD_INPUT_MASK
&#39;Keyboard&#39; input mask, bit flag for window creation
void MBSetMenuBarMenuResource(WBMenuBarWindow *pMenuBar, const char *pszResource)
Assign a new WBMenu for a Menu Bar window.
Definition: menu_bar.c:491
structure for managing menu items
Definition: menu.h:127
unsigned int border
structure for managing a popup menu window
Definition: menu_popup.h:108
int WBPostPriorityEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the priority (internal) event queue.
int iX
menu bar &#39;X&#39; position within the owner&#39;s client area
Definition: menu_bar.h:121
&#39;configuration helper&#39; main header file for the X11 Work Bench Toolkit API
void MBReCalcMenuBarWindow(WBMenuBarWindow *pMenuBar)
Cause a &#39;layout recalculation&#39; for a Menu Bar window.
Definition: menu_bar.c:465
WB_UINT32 WBCreatePointerHash(void *pPointer)
Create/obtain a 32-bit &#39;secure&#39; hash for a pointer.
int iMenuID
menu identifier specified when menu was created (high bit set for popup)
Definition: menu.h:189
force fixed pitch
Definition: font_helper.h:387
#define WB_STANDARD_INPUT_MASK
&#39;Standard&#39; input mask, bit flag for window creation
void WBSetWindowFontStruct(Window wID, XFontStruct *pFontStruct)
assigns the default XFontStruct to a window
Atom aSET_FOCUS
dialog focus messages
void WBInvalidateGeom(Window wID, const WB_GEOM *pGeom, int bPaintNow)
&#39;Paint&#39; helper, invalidates a geometry for asynchronous Expose event generation
int iTextWidth
width of menu text (in pixels; assign &#39;-1&#39; to calculate it)
Definition: menu.h:142
int iFlags
flags that determine behavior (reserved)
Definition: menu_bar.h:127
void WBClearWindow(Window wID, GC gc)
&#39;Paint&#39; helper, erases background by painting the background color within the clipping region ...
void WBCreateWindowDefaultGC(Window wID, unsigned long clrFG, unsigned long clrBG)
creates a default GC for a window
unsigned long WBGetWindowFGColor(Window wID)
Returns the currently assigned foreground color.
int nItems
The number of menu item entries in the &#39;ppItems&#39; array.
Definition: menu.h:192
WBMenu * MBCreateMenu(int iID, int iPopup, const char *pszResource, int iReserveSpace)
Create a WBMenu from a text menu resource.
Definition: menu.c:107
Atom aMENU_RESIZE
Internal Client Message Atom for &#39;RESIZE&#39; notification (tells menu bar to resize itself) ...
Definition: menu_bar.c:130
WBMenuBarWindow * MBCreateMenuBarWindow(Window wIDParent, const char *pszResource, int iFlags)
Create a Menu Bar windows and its associated WBMenuBarWindow structure.
Definition: menu_bar.c:305
void * WBAlloc(int nSize)
High performance memory sub-allocator &#39;allocate&#39;.
unsigned int width
Window WBGetParentWindow(Window wID)
Returns the window&#39;s parent (or None if there is no parent)
#define END_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro follows the call(s)
structure for defining a menu bar window
Definition: menu_bar.h:113
static __inline__ WBMenuBarWindow * MBGetMenuBarWindowStruct(Window wID)
Obtain a pointer to the WBMenuBarWindow structure from a Window ID of a Menu Bar window.
Definition: menu_bar.h:191
int iMenuItemText
offset in &#39;data&#39; to null-byte terminated strings (-1 if none)
Definition: menu.h:133
void WBSetWindowDefaultCursor(Window wID, int idStandardCursor)
Assigns a default cursor (by ID) to a window.
unsigned int height
#define WBMENU_POPUP_HIGH_BIT
Definition: menu.h:78
#define WB_ERROR_PRINT(...)
Preferred method of implementing an &#39;error level&#39; debug message for all subsystems.
Definition: debug_helper.h:268
WBMenuBarWindow * MBFindMenuBarWindow(WBMenu *pMenu)
Locate the first WBMenuBarWindow that is using a WBMenu structure.
Definition: menu_bar.c:453
void WBFree(void *pBuf)
High performance memory sub-allocator &#39;free&#39;.
int WBPostEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the regular (internal) event queue.
void WBDestroyWindow(Window wID)
Destroy a window.
void MBDestroyMenu(WBMenu *pMenu)
Destroy a WBMenu created by MBCreateMenu(), freeing up its resources.
Definition: menu.c:190
int MBMenuDoModal(WBMenuPopupWindow *pPopup)
display a Menu Popup window in a &#39;modal&#39; loop
Definition: menu_popup.c:469
int iPrevSel
previously selected menu item (for internal-only menu UI purposes)
Definition: menu_bar.h:126
Window wOwner
The window ID of the owning window.
Definition: menu_bar.h:117
force sans-serif
Definition: font_helper.h:393
Atom aMENU_ACTIVATE
Internal Client Message Atom for &#39;ACTIVATE&#39; notification.
Definition: menu_bar.c:150
void WBXlatCoordPoint(Window wIDSrc, int iXSrc, int iYSrc, Window wIDDest, int *piXDest, int *piYDest)
Translate X,Y point coordinates relative to a window.
internal wrapper struct for &#39;rectangle&#39; definition
XColor clrMenuFG
menu foreground color
Definition: menu_bar.c:113
WBMenuItem ** ppItems
An allocated array of menu items.
Definition: menu.h:191
void WBDestroyPointerHash(WB_UINT32 uiHash)
Destroy a 32-bit &#39;secure&#39; hash for a pointer.
#define LOAD_COLOR(X, Y, Z)
macro to load a color with a fallback, mostly for readability
WBMenuPopupWindow * MBCreateMenuPopupWindow(Window wIDBar, Window wIDOwner, WBMenu *pMenu, int iX, int iY, int iFlags)
Create a WBMenuPopupWindow object and associated window.
Definition: menu_popup.c:199
int iSelected
currently selected menu (for internal-only menu UI purposes)
Definition: menu_bar.h:125
int iY
menu bar &#39;Y&#39; position within the owner&#39;s client area
Definition: menu_bar.h:122
int iWidth
menu bar width
Definition: menu_bar.h:123
char data[4]
data follows
Definition: menu.h:146
Atom aMENU_COMMAND
commands sent by menus via ClientMessage
XFontStruct * WBGetDefaultFont(void)
Returns a pointer to the default font&#39;s XFontStruct.
Display * WBGetWindowDisplay(Window wID)
returns the Display associated with a window
void WBSetWindowData(Window wID, int iIndex, void *pData)
assign &#39;data pointer&#39; for a window and specified index value
#define BEGIN_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro precedes the call(s)
WBMenu * pMenu
a pointer to the associated WBMenu structure
Definition: menu_bar.h:119
void WBDumpFontInfo(const char *szFontName)
Dump debug information about fonts according to pSpec.
Definition: font_helper.c:415
void WBRegisterMenuCallback(Window wID, WBWinEvent pMenuCallback)
(internal) Register a MENU callback for a window
Window WBCreateWindow(Display *pDisplay, Window wIDParent, WBWinEvent pProc, const char *szClass, int iX, int iY, int iWidth, int iHeight, int iBorder, int iIO, int iFlags, XSetWindowAttributes *pXSWA)
Create a window.
XFontStruct * MBGetDefaultMenuFont(void)
Get a pointer to the default &#39;Menu Bar&#39; font structure.
Definition: menu_bar.c:300
void WBGetClientRect(Window wID, WB_RECT *pRect)
Returns the WB_RECT (rectangle) defined by the window&#39;s geometry, excluding the border area...
void WBSetWMProperties(Window wID, const char *szTitle, XSizeHints *pNormalHints, XWMHints *pWMHints, XClassHint *pClassHints)
assign standard WM (Window Manager) properties via XSetWMProperties
Window wSelf
The window ID of the menu bar window.
Definition: menu_bar.h:116
XFontStruct * WBGetWindowFontStruct(Window wID)
Returns the current XFontStruct pointer assigned to the window (may be NULL)
XFontStruct * WBCopyFont(XFontStruct *pOldFont)
make a copy of an existing font (best when assigning to a window)
Definition: font_helper.c:97
int WBMapWindow(Display *pDisplay, Window wID)
Wrapper for XMapWindow, makes window visible.
Frame Window API functions and definitions.
internal wrapper struct for X11 &#39;geometry&#39; definition
Atom WBGetAtom(Display *pDisplay, const char *szAtomName)
Lookup and/or allocate an internal Atom for a named string (lookups include X11 atoms) ...
void WBEndPaint(Window wID, GC gc)
&#39;Paint&#39; helper, frees resources and marks the update region &#39;valid&#39;
#define WB_WARN_PRINT(...)
Preferred method of implementing a &#39;warning level&#39; debug message for all subsystems.
Definition: debug_helper.h:261
struct __WBMenu ** ppPopups
An allocated array of &#39;popup&#39; menus contained by this menu.
Definition: menu.h:195
void WBRemoveMenuWindow(Window wID, Window wIDMenu)
Remove (detach) the specified menu window from a (frame) window.