X11workbench 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-2018 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  int iY, iU, iV, iR, iG, iB;
238 
239  colormap = DefaultColormap(WBGetDefaultDisplay(), DefaultScreen(WBGetDefaultDisplay()));
240  LOAD_COLOR("*Menu.foreground",szMenuFG,"#000000");
241  LOAD_COLOR("*Menu.background",szMenuBG,"#DCDAD5");
242  LOAD_COLOR("*Menu.activeForeground",szMenuActiveFG,"#FFFFFF");
243  LOAD_COLOR("*Menu.activeBackground",szMenuActiveBG,"#4B6983");
244  LOAD_COLOR("*Menu.disabledForeground",szMenuDisabledFG,"#808080"); // TODO: verify these Menu.xxx strings for DISABLED
245  LOAD_COLOR("*Menu.disabledForeground",szMenuActiveDisabledFG,"#808080");
246  LOAD_COLOR("*borderColor",szMenuBorder1,"#000000");
247 
248  // NOTE: 'DEBUG_VALIDATE' (defined above) simply validates the return and prints a message if it failed.
249  // in a release build, the code is still executed, but no error checks are performed on the return value
250 
251  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuFG, &clrMenuFG));
252  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuFG)); // NOTE: do I need 'XFreeColors' for these ?
253 
254  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuBG, &clrMenuBG));
255  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuBG));
256 
257  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuActiveFG, &clrMenuActiveFG));
258  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuActiveFG)); // NOTE: do I need 'XFreeColors' for these ?
259 
260  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuActiveBG, &clrMenuActiveBG));
261  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuActiveBG));
262 
263  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuDisabledFG, &clrMenuDisabledFG));
264  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuDisabledFG));
265 
266  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuActiveDisabledFG, &clrMenuActiveDisabledFG));
267  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuActiveDisabledFG));
268 
269  DEBUG_VALIDATE(XParseColor(WBGetDefaultDisplay(), colormap, szMenuBorder1, &clrMenuBorder1));
270  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuBorder1));
271 
272 
273  if((clrMenuBG.flags & (DoRed | DoGreen | DoBlue)) != (DoRed | DoGreen | DoBlue))
274  {
276  &(clrMenuBG)); // make sure RGB is correctly assigned
277  }
278 
279  RGB255_FROM_XCOLOR(clrMenuBG, iR, iG, iB);
280 
281  PXM_RGBToYUV(iR, iG, iB, &iY, &iU, &iV);
282 
283  // the highlight color should be 1/3 of the way between the background color and white, using the same U and V
284 
285  PXM_YUVToRGB((2 * iY + 256) / 3, iU, iV, &iR, &iG, &iB);
286 
287  RGB255_TO_XCOLOR(iR, iG, iB, clrMenuBorder2); // assign new RGB values to the XColor struct
289  &(clrMenuBorder2)); // re-assign pixel element from RGB values
290 
291  // the shaded color should be 2/3 of the way between black and the background color, using the same U and V
292 
293  PXM_YUVToRGB((2 * iY) / 3, iU, iV, &iR, &iG, &iB);
294 
295  RGB255_TO_XCOLOR(iR, iG, iB, clrMenuBorder3); // assign new RGB values to the XColor struct
297  &(clrMenuBorder3)); // re-assign pixel element from RGB values
298 
299 
300  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuBorder2));
301  DEBUG_VALIDATE(XAllocColor(WBGetDefaultDisplay(), colormap, &clrMenuBorder3));
302 
303 
304  // TODO: make sure I was able to actually allocate these colors
305 
306  iInitColorFlag = 1;
307  }
308 
309  if(aMENU_RESIZE == None)
310  {
311  aMENU_RESIZE = WBGetAtom(WBGetDefaultDisplay(), "WB_MENU_RESIZE");
312  }
313 
314  if(aMENU_ACTIVATE == None)
315  {
316  aMENU_ACTIVATE = WBGetAtom(WBGetDefaultDisplay(), "WB_MENU_ACTIVATE");
317  }
318 
319  if(aMENU_DISPLAY_POPUP == None)
320  {
321  aMENU_DISPLAY_POPUP = WBGetAtom(WBGetDefaultDisplay(), "WB_MENU_DISPLAY_POPUP");
322  }
323 
324  return 1;
325 }
326 
327 XFontStruct *MBGetDefaultMenuFont(void)
328 {
329  return pDefaultMenuFont;
330 }
331 
332 WBMenuBarWindow *MBCreateMenuBarWindow(Window wIDParent, const char *pszResource,
333 // int iX, int iY, int *piWidth, int *piHeight,
334  int iFlags)
335 {
336  Display *pDisplay;
337  WBMenuBarWindow *pRval = NULL;
338  unsigned long fg, bg, bd; /* Pixel values */
339  XSetWindowAttributes xswa; /* Temporary Set Window Attribute struct */
340  XSizeHints xsh; /* Size hints for window manager - width of owner + 2 * height of font */
341  XWMHints xwmh;
342  WB_RECT rct;
343  XFontStruct *pFS;
344 
345 
346  // initialize global menu objects
347  if(!MBInitGlobal())
348  return NULL;
349 
350  if(wIDParent > 0)
351  pDisplay = WBGetWindowDisplay(wIDParent);
352  else
353  pDisplay = WBGetDefaultDisplay();
354 
355  // step 1: create the window
356 
357  bd = clrMenuBorder1.pixel; // BlackPixel(pDisplay, DefaultScreen(pDisplay));
358  fg = clrMenuFG.pixel;// BlackPixel(pDisplay, DefaultScreen(pDisplay)); // black foreground for menus (always)
359  bg = clrMenuBG.pixel;// WhitePixel(pDisplay, DefaultScreen(pDisplay)); // white background for menus (for now)
360 
361  WBGetClientRect(wIDParent, &rct);
362  pFS = WBGetWindowFontStruct(wIDParent);
363 
364  if(!pDefaultMenuFont && !pFS)
365  {
366  WB_ERROR_PRINT("%s - * BUG * no font!\n", __FUNCTION__);
367  return 0;
368  }
369  else if(pDefaultMenuFont)
370  pFS = pDefaultMenuFont;
371 
372 
373  // set size hints to match client area, upper left corner (always)
374  xsh.flags = (PPosition | PSize);
375  xsh.x = rct.left;
376  xsh.y = rct.top;
377  xsh.width = rct.right - rct.left;
378  if(!pFS)
379  xsh.height = 32;
380  else
381  xsh.height = 2 * (pFS->max_bounds.ascent + pFS->max_bounds.descent);
382 
383  if(xsh.height > (rct.bottom - rct.top))
384  xsh.height = rct.bottom - rct.top;
385 
386  memset(&xswa, 0, sizeof(xswa));
387 
388  xswa.border_pixel = bd;
389  xswa.background_pixel = bg;
390  xswa.colormap = DefaultColormap(pDisplay, DefaultScreen(pDisplay));
391  xswa.bit_gravity = CenterGravity;
392 
393  pRval = (WBMenuBarWindow *)WBAlloc(sizeof(*pRval));
394 
395  if(!pRval)
396  return NULL;
397 
398  pRval->ulTag = MENU_WINDOW_TAG;
399 
400  pRval->pMenu = MBCreateMenu(-1, 0, pszResource, WBMENU_RESERVE_DEFAULT);
401 
402  if(!pRval->pMenu)
403  {
404  WB_WARN_PRINT("%s - WARNING: pMenu is NULL in WBMenuBarWindow object\n", __FUNCTION__);
405  }
406 
407  pRval->wSelf = WBCreateWindow(pDisplay, wIDParent, MBMenuWinEvent, "MenuBar",
408  xsh.x, xsh.y, xsh.width, xsh.height, 0,
409  InputOutput,
410  CWBorderPixel | CWBackPixel | CWColormap | CWBitGravity,
411  &xswa);
412 
413  if(pRval->wSelf == -1)
414  {
415  WBFree(pRval);
416  return NULL;
417  }
418 
419  pRval->wOwner = wIDParent;
420  pRval->iPrevSel = pRval->iSelected = -1; // 'none'
421  pRval->iFlags = iFlags; // make a copy of them (for now)
422 
423  // calculate the initial position and height of the menu bar within the window
424  pRval->iX = xsh.x + 4;
425  pRval->iY = xsh.y + 4;
426  pRval->iWidth = xsh.width - 8;
427  pRval->iHeight = xsh.height - 8;
428 
429  // establish this window as NEVER getting the input focus
430  bzero(&xwmh, sizeof(xwmh));
431  xwmh.flags = InputHint;
432  xwmh.input = 0; // this represents 'None' (or 'Globally Active' if WM_TAKE_FOCUS is present)
433 
434  WBSetWMProperties(pRval->wSelf, NULL, NULL, &xwmh, NULL);
435 
436  WBSetWindowDefaultCursor(pRval->wSelf, XC_hand2); //XC_top_left_arrow);
437  WBSetWindowData(pRval->wSelf, 0, pRval); // a pointer back to me. must do before next part...
438  WBRegisterMenuCallback(pRval->wSelf, MBMenuParentEvent);
439 
440  WBCreateWindowDefaultGC(pRval->wSelf, fg, bg);
441 // WBSetWindowFontStruct(pRval->wSelf, XLoadQueryFont(WBGetDefaultDisplay(), MENU_FONT));
442  WBSetWindowFontStruct(pRval->wSelf, WBCopyFont(pFS));
443 
444  // TODO: should I _NOT_ do this if I clear the input focus flag?
445  XSelectInput(pDisplay, pRval->wSelf,
447 
448  WBAddMenuWindow(wIDParent, pRval->wSelf); // add this menu to this window
449  // this handles menu hotkeys and resize events for me
450 // {
451 // Atom a1, a2;
452 // a1 = XInternAtom(pDisplay, "_NET_WM_WINDOW_TYPE", False);
453 // a2 = XInternAtom(pDisplay, "_NET_WM_WINDOW_TYPE_MENU", False);
454 // XChangeProperty(pDisplay, pRval->wSelf, a1, XA_ATOM, 32, PropModeReplace, (unsigned char *)&a2, 1);
455 // a1 = XInternAtom(pDisplay, "WM_TRANSIENT_FOR", False);
456 // XChangeProperty(pDisplay, pRval->wSelf, a1, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&wIDParent, 1);
457 // }
458 
459  WBMapWindow(pDisplay, pRval->wSelf); // make window visible
460 
461 
462  // TODO: set up font, GS, callback, etc.
463 
464 
465  return(pRval);
466 }
467 
468 static int __FindMenuBarWindowCallback(Window wID, void *pData)
469 {
471 
472  if(pMB && (void *)(pMB->pMenu) == pData)
473  {
474  return 1;
475  }
476 
477  return 0;
478 }
479 
480 WBMenuBarWindow *MBFindMenuBarWindow(WBMenu *pMenu) // find first (active) window that uses 'pMenu'
481 {
482  Window wID = WBLocateWindow(__FindMenuBarWindowCallback, (void *)pMenu);
483 
484  if(wID)
485  {
486  return MBGetMenuBarWindowStruct(wID);
487  }
488 
489  return NULL;
490 }
491 
492 void MBReCalcMenuBarWindow(WBMenuBarWindow *pMenuBar /*, int iX, int iY, int *piWidth, int *piHeight*/)
493 {
494  // TODO: update structure's position/size cache?
495 
496  WBInvalidateGeom(pMenuBar->wSelf, NULL, 1); // force a re-paint
497 }
498 
500 {
501  if(!pMenuBar || pMenuBar->ulTag != MENU_WINDOW_TAG)
502  {
503  return;
504  }
505 
506  // TODO: any special notifications??
507 
508  if(pMenuBar->pMenu)
509  {
510  MBDestroyMenu(pMenuBar->pMenu); // TODO: make this do reference counting?
511  pMenuBar->pMenu = NULL; // by convention, do this
512  }
513 
514  WBDestroyWindow(pMenuBar->wSelf);
515  WBFree(pMenuBar);
516 }
517 
518 void MBSetMenuBarMenuResource(WBMenuBarWindow *pMenuBar, const char *pszResource)
519 {
520 WBMenu *pMenu;
521 
522  if(!pMenuBar || pMenuBar->ulTag != MENU_WINDOW_TAG)
523  {
524  return;
525  }
526 
527  pMenu = MBCreateMenu(-1, 0, pszResource, WBMENU_RESERVE_DEFAULT);
528 
529  if(!pMenu)
530  {
531  WB_ERROR_PRINT("ERROR: %s - unable to create menu\n", __FUNCTION__);
532  return;
533  }
534 
535  if(pMenuBar->pMenu)
536  {
537  MBDestroyMenu(pMenuBar->pMenu);
538  }
539 
540  pMenuBar->pMenu = pMenu;
541 
542  pMenuBar->iSelected = -1;
543  pMenuBar->iPrevSel = -1;
544 
545  MBReCalcMenuBarWindow(pMenuBar);
546 }
547 
548 
549 // this next callback is assigned via WBRegisterMenuCallback
550 int MBMenuParentEvent(Window wIDMenu, XEvent *pEvent)
551 {
552  Display *pDisplay = WBGetWindowDisplay(wIDMenu);
553  WBMenuBarWindow *pSelf = MBGetMenuBarWindowStruct(wIDMenu);
554 
555  if(!pSelf)
556  {
557  WB_ERROR_PRINT("%s - pSelf is NULL\n", __FUNCTION__);
558  return 0;
559  }
560 
561  if(pEvent->type == ConfigureNotify &&
562  pSelf->wOwner == pEvent->xconfigure.window)
563  {
564  XWindowChanges chg;
565  chg.width = pEvent->xconfigure.width;
566  XConfigureWindow(pDisplay, wIDMenu, CWWidth, &chg);
567 
568  WBInvalidateGeom(wIDMenu, NULL, 0);
569 
570  return 0;
571  }
572 
573  // TODO: menu hot keys
574 
575  // TODO: mouse motion ?
576 
577  return 0; // not handled
578 }
579 
580 static void MBMenuHandleMenuItem(Display *pDisplay, WBMenuBarWindow *pSelf, WBMenu *pMenu, WBMenuItem *pItem)
581 {
582 Window wID = pSelf->wSelf;
583 
584  if(pItem->iAction & WBMENU_POPUP_HIGH_BIT)
585  {
586  XClientMessageEvent evt;
587 
588  // post a high-priority message to myself to display the menu
589 
590  bzero(&evt, sizeof(evt));
591  evt.type = ClientMessage;
592  evt.display = pDisplay;
593  evt.window = wID;
594  evt.message_type = aMENU_DISPLAY_POPUP;
595  evt.format = 32;
596  evt.data.l[0] = pItem->iAction & WBMENU_POPUP_MASK;
597  evt.data.l[1] = pItem->iPosition; // minimum extent
598  evt.data.l[2] = pItem->iPosition + pItem->iTextWidth; // maximum extent
599 
600  WBPostPriorityEvent(wID, (XEvent *)&evt);
601 
602  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu,
603  "%s - posting client event message to display popup menu\n", __FUNCTION__);
604  }
605  else
606  {
607  XClientMessageEvent evt;
608 
609  bzero(&evt, sizeof(evt));
610  evt.type = ClientMessage;
611 
612  evt.display = pDisplay;
613  evt.window = pSelf->wOwner;
614  evt.message_type = aMENU_COMMAND;
615  evt.format = 32; // always
616  evt.data.l[0] = pItem->iAction; // menu command message ID
617  evt.data.l[1] = WBCreatePointerHash(pMenu); // hash of pointer to menu object
618  evt.data.l[2] = wID; // window ID of menu bar
619 
620  WBPostEvent(pSelf->wOwner, (XEvent *)&evt);
621 
622  pSelf->iSelected = -1; // after handling the message I de-select the menu item
623 
624  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Menu | DebugSubSystem_Event,
625  "%s - Post Event: %08xH %08xH %pH %08xH\n", __FUNCTION__,
626  (int)aMENU_COMMAND, (int)pItem->iAction,
627  pMenu, (int)wID);
628 
630  }
631 }
632 
633 #if 0 /* currently not used, reserved, consider removing it if not needed */
634 static int MBMenuHandleMenuItemUI(Display *pDisplay, WBMenuBarWindow *pSelf, WBMenu *pMenu, WBMenuItem *pItem)
635 {
636  XClientMessageEvent evt;
637  int iRval;
638 
639  bzero(&evt, sizeof(evt));
640  evt.type = ClientMessage;
641 
642  evt.display = pDisplay;
643  evt.window = pSelf->wOwner;
644  evt.message_type = aMENU_UI_COMMAND;
645  evt.format = 32; // always
646 
647  // sending pointer data - since this is internally 'sent' I don't have to
648  // worry about async causing pointers to become invalid
649 
650  evt.data.l[0] = pItem->iAction; // menu command message ID (needed to identify menu)
651 
652  evt.data.l[1] = WBCreatePointerHash(pMenu);
653  evt.data.l[2] = WBCreatePointerHash(pItem);
654  evt.data.l[3] = 0; // this is a sort of 'flag' saying I'm using a pointer hash
655  evt.data.l[4] = 0;
656 
657  iRval = WBWindowDispatch(pSelf->wOwner, (XEvent *)&evt); // 'send event'
658 
659  WBDestroyPointerHash(pMenu);
660  WBDestroyPointerHash(pItem); // clean them up as I'm done with them now
661 
662  return iRval;
663 }
664 #endif // 0
665 
666 
667 // this next callback is assigned via WBRegisterWindowCallback
668 int MBMenuWinEvent(Window wID, XEvent *pEvent)
669 {
670 Display *pDisplay = WBGetWindowDisplay(wID);
672 WBMenu *pMenu = pSelf ? pSelf->pMenu : NULL;
673 WBMenuItem *pItem;
674 WB_GEOM geom;
675 Window wIDParent;
676 XEvent xevt;
677 int i1; //, iHPos, iVPos;
678 
679 
680  if(pSelf && pEvent->type == Expose)
681  {
682  return MenuBarDoExposeEvent((XExposeEvent *)pEvent, pMenu, pDisplay, wID, pSelf);
683  }
684 
685  if(pSelf && pEvent->type == ClientMessage && pEvent->xclient.message_type == aMENU_RESIZE)
686  {
687  WB_RECT rct;
688  XWindowAttributes xswa;
689 
690  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Menu | DebugSubSystem_Window | DebugSubSystem_Event,
691  "%s - Resize Request Message for %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
692 
693  WBGetClientRect(pSelf->wOwner, &rct);
694 
695  XGetWindowAttributes(pDisplay, wID, &xswa);
696  xswa.width = rct.right - rct.left - xswa.border_width;
697  // assume height doesn't change
698 
699  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Window | DebugSubSystem_Event,
700  "%s - perform resize request for %d (%08xH) %d, %d\n",
701  __FUNCTION__, (int)wID, (int)wID, xswa.width, xswa.height);
702 
703  XResizeWindow(pDisplay, wID, xswa.width, xswa.height);
704  return 0; // still requesting default handling on this one
705  }
706 
707  // process 'destroy' events
708  if(pSelf && pEvent->type == DestroyNotify)
709  {
710  if(pEvent->xdestroywindow.window == wID) // destroying myself?
711  {
712  WBRemoveMenuWindow(-1, wID); // removes from ALL windows
713  return 1; // processed
714  }
715  }
716 
717 
718  wIDParent = WBGetParentWindow(wID); // grab the parent window's ID
719 
720  if(pSelf &&
721  (pEvent->type == KeyPress ||
722  pEvent->type == KeyRelease))
723  {
724  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Window | DebugSubSystem_Event,
725  "%s - keypress/release in menu_bar\n", __FUNCTION__);
726 
727  if(pEvent->type == KeyPress)
728  {
729  // see if this is a hotkey for a menu item
730 // if(pEvent->xkey.
731  }
732 
733  // send to parent window for processing
734 
735  memcpy(&xevt, pEvent, sizeof(*pEvent));
736  xevt.xkey.window = wIDParent;
737 
738  WBWindowDispatch(wIDParent, &xevt); // have the parent process it
739 
740  // NEXT, post a 'set focus' request to the parent so it gets the other events
741 
742  bzero(&xevt, sizeof(xevt));
743  xevt.type = ClientMessage;
744 
745  xevt.xclient.display = pDisplay;
746  xevt.xclient.window = wIDParent;
747  xevt.xclient.message_type = aSET_FOCUS;
748  xevt.xclient.format = 32; // always
749  xevt.xclient.data.l[0] = 0; // default
750 
751  WBPostEvent(wIDParent, &xevt); // post this (parent should fix it now, asynchronously)
752  }
753 
754  if(pSelf &&
755  (pEvent->type == ButtonPress ||
756  pEvent->type == ButtonRelease ||
757  pEvent->type == MotionNotify))
758  {
759  int iX, iY; // mousie coordinates (when needed)
760 
761  // mousie clickie - yay!
762 
763  if(pEvent->type == MotionNotify)
764  {
765  WBXlatCoordPoint(pEvent->xmotion.window, pEvent->xmotion.x, pEvent->xmotion.y,
766  wID, &iX, &iY);
767  if(pEvent->xmotion.state & Button1Mask) // left drag?
768  {
769  // todo - highlight selections by simulating button press, drop into a popup, etc.
770  }
771  }
772  else if(pEvent->type == ButtonPress && pMenu)
773  {
774  WBXlatCoordPoint(pEvent->xbutton.window, pEvent->xbutton.x, pEvent->xbutton.y,
775  wID, &iX, &iY);
776  if(pEvent->xbutton.state & (Button2Mask | Button3Mask | Button4Mask | Button5Mask
777  | ShiftMask | LockMask | ControlMask))
778  {
779  // this click I ignore (for now)
780  }
781  else if(iY >= pSelf->iY && iY <= (pSelf->iY + pSelf->iHeight))
782  {
783  for(i1=0; pMenu->ppItems && i1 < pMenu->nItems; i1++)
784  {
785  WBMenuItem *pItem = pMenu->ppItems[i1];
786  if(!pItem)
787  continue;
788 
789  if(pItem->iPosition <= iX && pItem->iTextWidth + pItem->iPosition >= iX) // between them
790  {
791  WB_GEOM geom;
792 
793  pSelf->iPrevSel = pSelf->iSelected = i1; // indicate the item that's selected
794 
795  geom.x = pItem->iPosition;
796  geom.y = pSelf->iY;
797  geom.width = pItem->iTextWidth;
798  geom.height = pSelf->iHeight;
799  geom.border = 0;
800 
801  WBInvalidateGeom(wID, &geom, TRUE); // force a re-paint (at some point in time)
802 
803  return 1;
804  }
805  }
806 
807  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Menu | DebugSubSystem_Mouse,
808  "%s - couldn't find the menu item - mouse at %d, %d\n", __FUNCTION__, iX, iY);
809 
810 let_parent_process_it:
811 
812  // mouse outside of menu means parent should re-take the focus
813  // So, post a 'set focus' request to the parent so it gets the other events
814 
815  bzero(&xevt, sizeof(xevt));
816  xevt.type = ClientMessage;
817 
818  xevt.xclient.display = pDisplay;
819  xevt.xclient.window = wIDParent;
820  xevt.xclient.message_type = aSET_FOCUS;
821  xevt.xclient.format = 32; // always
822  xevt.xclient.data.l[0] = 0; // default
823 
824  WBPostEvent(wIDParent, &xevt); // post this (parent should fix it now, asynchronously)
825  }
826  else
827  {
828  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Menu | DebugSubSystem_Mouse,
829  "%s - Mouse pointer is out of range - %d, %d, %d, %d, %d, %d\n",
830  __FUNCTION__, iX, iY, pSelf->iX, pSelf->iY, pSelf->iWidth, pSelf->iHeight);
831 
832  goto let_parent_process_it;
833  }
834  }
835  else // if(pEvent->type == ButtonRelease)
836  {
837  i1 = pSelf->iSelected;
838 
839  if(i1 < 0 || i1 >= pMenu->nItems)
840  {
841  pSelf->iSelected = -1;
842  WBInvalidateGeom(wID, NULL, TRUE);
843  return 0; // nothing selected
844  }
845 
846  pItem = pMenu->ppItems[i1];
847  if(!pItem)
848  {
849  pSelf->iSelected = -1;
850  WBInvalidateGeom(wID, NULL, TRUE);
851  return 0;
852  }
853 
854  geom.x = pItem->iPosition;
855  geom.y = pSelf->iY;
856  geom.width = pItem->iTextWidth;
857  geom.height = pSelf->iHeight;
858  geom.border = 0;
859 
860  WBInvalidateGeom(wID, &geom, TRUE); // force a re-paint (at some point in time)
861 
862  // determine what the correct action for this menu item is...
863  if(pItem->iAction == -1)
864  {
865  pSelf->iSelected = -1;
866  WBInvalidateGeom(wID, NULL, TRUE);
867  return 0; // "not handled"
868  }
869 
870  MBMenuHandleMenuItem(pDisplay, pSelf, pMenu, pItem);
871 
872  WBInvalidateGeom(wID, &geom, TRUE); // force re-paint AGAIN
873 
874  return 1;
875  }
876  }
877 
878  if(pEvent && pEvent->type == ClientMessage)
879  {
880  if(((XClientMessageEvent *)pEvent)->message_type == aMENU_ACTIVATE)
881  {
882  int iMenuItemIndex = ((XClientMessageEvent *)pEvent)->data.l[1]; // index, prev, or next
883 
885  // TODO: deal with dynamic menu elements
887 
888  pItem = NULL;
889 
890  if(iMenuItemIndex >= 0 && iMenuItemIndex < pMenu->nItems &&
891  ((XClientMessageEvent *)pEvent)->data.l[0]) // prev/next or 'absolute' indicator - *NOT* equal to 'NULL'
892  {
893  // select the menu item specified by the index in data.l[1]
894 
895  if(pSelf->iSelected != iMenuItemIndex &&
896  pSelf->iSelected >= 0 && pSelf->iSelected < pMenu->nItems)
897  {
898  pItem = pMenu->ppItems[pSelf->iSelected];
899  geom.x = pItem->iPosition;
900  geom.y = pSelf->iY;
901  geom.width = pItem->iTextWidth;
902  geom.height = pSelf->iHeight;
903  geom.border = 0;
904 
905  WBInvalidateGeom(wID, &geom, FALSE); // invalidate rect for currently selected menu item
906  }
907 
908  pSelf->iSelected = iMenuItemIndex;
909  pItem = pMenu->ppItems[pSelf->iSelected];
910 
911  // this next part is a 'self-check' which helps to validate the menu item pointer
912 
913  if(pItem == WBGetPointerFromHash((((XClientMessageEvent *)pEvent)->data.l[0])))
914  {
915  geom.x = pItem->iPosition;
916  geom.y = pSelf->iY;
917  geom.width = pItem->iTextWidth;
918  geom.height = pSelf->iHeight;
919  geom.border = 0;
920 
921  pSelf->iPrevSel = pSelf->iSelected;
922 
923  MBMenuHandleMenuItem(pDisplay, pSelf, pMenu, pItem);
924 
925  WBInvalidateGeom(wID, &geom, TRUE); // force window update
926 
927  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
928  "%s - MENU_ACTIVATE event, activate menu item, %d %p\n",
929  __FUNCTION__, iMenuItemIndex, pItem);
930 
931  return 1; // handled
932  }
933  else
934  {
935  pSelf->iSelected = -1;
936  WBInvalidateGeom(wID, NULL, TRUE);
937  }
938 
939  WBDestroyPointerHash(((XClientMessageEvent *)pEvent)->data.l[0]);
940  }
941  else if(/*iMenuItemIndex &&*/ !((XClientMessageEvent *)pEvent)->data.l[0])
942  {
943  // select the PREVIOUS or the NEXT menu item
944 
945  int iOldSel = pSelf->iSelected;
946 
947  if(iOldSel < 0 || iOldSel >= pMenu->nItems)
948  {
949  iOldSel = pSelf->iPrevSel;
950  }
951  if(iOldSel >=0 && iOldSel < pMenu->nItems)
952  {
953  pSelf->iSelected = iOldSel; // re-initialize
954 
955  pItem = NULL; // initially
956 
957  if(iMenuItemIndex == 0)
958  {
959  if(pMenu->nItems > 0) // just in case
960  {
961  pSelf->iSelected = 0;
962 
963  goto try_select_next; // a simple way to implement this
964  }
965 
966  pItem = NULL; // make sure
967  }
968  else if(iMenuItemIndex > 0)
969  {
970  while(pSelf->iSelected < (pMenu->nItems - 1))
971  {
972  pSelf->iSelected++;
973 try_select_next:
974  pItem = pMenu->ppItems[pSelf->iSelected];
975 
976  if(pItem->iAction != WBMENU_SEPARATOR)
977  {
978  break;
979  }
980 
981  pItem = NULL;
982  }
983  }
984  else
985  {
986  while(pSelf->iSelected > 0)
987  {
988  pSelf->iSelected--;
989  pItem = pMenu->ppItems[pSelf->iSelected];
990 
991  if(pItem->iAction != WBMENU_SEPARATOR)
992  {
993  break;
994  }
995 
996  pItem = NULL;
997  }
998  }
999 
1000  if(!pItem)
1001  {
1002  pSelf->iSelected = iOldSel;
1003  pItem = pMenu->ppItems[pSelf->iSelected];
1004  }
1005 
1006  pSelf->iPrevSel = pSelf->iSelected;
1007  }
1008  else
1009  {
1010  pSelf->iSelected = pSelf->iPrevSel = 0;
1011  while(pSelf->iSelected < pMenu->nItems)
1012  {
1013  pItem = pMenu->ppItems[pSelf->iSelected];
1014  if(pItem->iAction != WBMENU_SEPARATOR)
1015  {
1016  break;
1017  }
1018  pItem = NULL;
1019  }
1020 
1021  if(!pItem)
1022  {
1023  pSelf->iSelected = -1; // fallback for bad menus
1024  // leave pItem set to NULL;
1025  }
1026  else
1027  {
1028  pSelf->iPrevSel = pSelf->iSelected;
1029  }
1030  }
1031 
1032  if(pItem)
1033  {
1034  MBMenuHandleMenuItem(pDisplay, pSelf, pMenu, pItem);
1035  WBInvalidateGeom(wID, NULL, FALSE); // re-paint, at some point in time
1036 
1037  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1038  "%s - MENU_ACTIVATE event, select prev/next, %d %d\n",
1039  __FUNCTION__, iMenuItemIndex, pSelf->iSelected);
1040  return 1; // handled
1041  }
1042  }
1043 
1044  WB_WARN_PRINT("%s - MENU_ACTIVATE event, invalid menu information, %d %d %p %p\n",
1045  __FUNCTION__, iMenuItemIndex, pMenu->nItems,
1046  (void *)pItem, (void *)(((XClientMessageEvent *)pEvent)->data.l[0]));
1047  }
1048  else if(((XClientMessageEvent *)pEvent)->message_type == aMENU_DISPLAY_POPUP)
1049  {
1050  WBMenuPopupWindow *pPopup;
1051  int iMenuItem = ((XClientMessageEvent *)pEvent)->data.l[0];
1052  int iPosition = ((XClientMessageEvent *)pEvent)->data.l[1];
1053  int iRightPos = ((XClientMessageEvent *)pEvent)->data.l[2];
1054 
1055  for(i1=0; i1 < pMenu->nPopups; i1++)
1056  {
1057  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu,
1058  "%s - popup menu id = %d\n", __FUNCTION__,
1059  (pMenu->ppPopups[i1]->iMenuID & WBMENU_POPUP_MASK));
1060 
1061  if(pMenu->ppPopups[i1] &&
1062  (pMenu->ppPopups[i1]->iMenuID & WBMENU_POPUP_MASK) == iMenuItem)
1063  {
1064  // TODO: before moving mouse, see if it's already within "the band"
1065  // move the mouse cursor to the center of the menu item (if it's in another window)
1067  XWarpPointer(pDisplay, None, pSelf->wSelf, 0, 0, 0, 0,
1068  (iPosition + iRightPos) / 2, pSelf->iY + pSelf->iHeight / 2);
1070 
1071  pPopup = MBCreateMenuPopupWindow(pSelf->wSelf, pSelf->wOwner, pMenu->ppPopups[i1],
1072  iPosition, pSelf->iY + pSelf->iHeight, 0);
1073 
1074  if(pPopup)
1075  {
1076 #ifndef NO_DEBUG
1077  int i2;
1078 #endif // NO_DEBUG
1079 
1080  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu,
1081  "%s - Displaying popup menu id %d\n", __FUNCTION__, iMenuItem);
1082 
1083 #ifndef NO_DEBUG
1084  i2 =
1085 #endif // NO_DEBUG
1086  MBMenuDoModal(pPopup);
1087 
1088 #ifndef NO_DEBUG
1089  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu,
1090  "%s - Done with popup menu id %d, return %d\n", __FUNCTION__, iMenuItem, i2);
1091 #endif // NO_DEBUG
1092  }
1093  else
1094  {
1095  WB_WARN_PRINT("%s - Unable to create popup menu %d\n", __FUNCTION__, iMenuItem);
1096  }
1097 
1098  pSelf->iSelected = -1; // necessary after modality returns
1099  WBInvalidateGeom(pSelf->wSelf, NULL, 1); // re-paint (sort of a bug fix)
1100 
1101  break;
1102  }
1103  }
1104 
1105  if(i1 >= pMenu->nPopups)
1106  {
1107  WB_WARN_PRINT("%s - Unable to locate popup menu %d\n", __FUNCTION__, iMenuItem);
1108  }
1109  }
1110  }
1111 
1112 
1113  return 0; // not handled
1114 }
1115 
1116 
1117 static int MenuBarDoExposeEvent(XExposeEvent *pEvent, WBMenu *pMenu, Display *pDisplay,
1118  Window wID, WBMenuBarWindow *pSelf)
1119 {
1120  int i1, i2, iHPos, iVPos;
1121  XWindowAttributes xwa; /* Temp Get Window Attribute struct */
1122  XFontStruct *pOldFont, *pFont;
1123  XPoint xpt[3];
1124  GC gc; // = WBGetWindowDefaultGC(wID);
1125  XGCValues xgc;
1126  WB_GEOM geomPaint;
1127  char tbuf[128];
1128 
1129 // // Remove any other pending Expose events from the queue to avoid multiple repaints.
1130 // // (I don't do this now since I'm already consolidating expose events)
1131 // while(!bQuitFlag && XCheckTypedWindowEvent(pDisplay, wID, Expose, pEvent))
1132 // ;
1133 
1134 // if(pEvent->xexpose.count != 0)
1135 // {
1136 //
1137 // }
1138 
1139  if (XGetWindowAttributes(pDisplay, wID, &xwa) == 0)
1140  {
1141  WB_ERROR_PRINT("%s - * BUG * unable to get window attributes\n", __FUNCTION__);
1142  return 0;
1143  }
1144 
1145  pFont = pOldFont = WBGetWindowFontStruct(wID);
1146 
1147  if(!pDefaultMenuFont && !pOldFont)
1148  {
1149  WB_ERROR_PRINT("%s - * BUG * no font!\n", __FUNCTION__);
1150  return 0;
1151  }
1152  else if(pDefaultMenuFont)
1153  pFont = pDefaultMenuFont;
1154 
1155  // get graphics context copy and begin painting
1156  gc = WBBeginPaint(wID, pEvent, &geomPaint);
1157  if(!gc)
1158  {
1159  WB_ERROR_PRINT("%s - * BUG * no graphics context!\n", __FUNCTION__);
1160  return 0;
1161  }
1162 
1163  WBClearWindow(wID, gc);
1164 
1165  xgc.font = pFont->fid;
1167  XChangeGC(pDisplay, gc, GCFont, &xgc);
1168 
1170 
1171  // paint the 3D-looking border
1172  XSetForeground(pDisplay, gc, clrMenuBorder2.pixel);
1173  xpt[0].x=xwa.border_width;
1174  xpt[0].y=xwa.height-1-2*xwa.border_width - 1; // exclude first point
1175  xpt[1].x=xwa.border_width;
1176  xpt[1].y=xwa.border_width;
1177  xpt[2].x=xwa.width-1-2*xwa.border_width - 1; // exclude last point
1178  xpt[2].y=xwa.border_width;
1179 
1180  XDrawLines(pDisplay, wID, gc, xpt, 3, CoordModeOrigin);
1181 
1182  XSetForeground(pDisplay, gc, clrMenuBorder3.pixel);
1183  xpt[0].x=xwa.width-1-2*xwa.border_width;
1184  xpt[0].y=xwa.border_width + 1; // exclude first point
1185  xpt[1].x=xwa.width-1-2*xwa.border_width;
1186  xpt[1].y=xwa.height-1-2*xwa.border_width;
1187  xpt[2].x=xwa.border_width + 1; // exclude final point
1188  xpt[2].y=xwa.height-1-2*xwa.border_width;
1189 
1190  XDrawLines(pDisplay, wID, gc, xpt, 3, CoordModeOrigin);
1191 
1192  // painting the menu items
1193 
1194  i2 = XTextWidth(pFont, " ", 2); // width of 2 spaces
1195  iVPos = pFont->max_bounds.ascent + pFont->max_bounds.descent; // font height
1196  iVPos = (xwa.height - iVPos) >> 1; // half of the difference - top of text
1197  iVPos += pFont->max_bounds.ascent;
1198 
1199  // update the width and height of the menu bar within the window
1200  pSelf->iX = i2;
1201  pSelf->iY = iVPos - (pFont->max_bounds.ascent + 1);
1202  pSelf->iWidth = i2; // initially
1203  pSelf->iHeight = pFont->max_bounds.ascent + pFont->max_bounds.descent + 1;
1204 
1205  XSetForeground(pDisplay, gc, clrMenuFG.pixel);
1206 
1207  for(i1=0, iHPos = i2; pMenu && pMenu->ppItems && i1 < pMenu->nItems; i1++)
1208  {
1209  WBMenuItem *pItem = pMenu->ppItems[i1];
1210  const char *szText;
1211  int iU1=0, iU2=0;
1212 
1213  if(!pItem)
1214  continue;
1215 
1216 // MBMenuHandleMenuItemUI(pDisplay, pSelf, pMenu, pItem); TODO - grey things maybe?
1217 
1218  if(i1 == pSelf->iSelected) // selected item
1219  {
1220  XSetForeground(pDisplay, gc, clrMenuActiveBG.pixel);
1221  XSetBackground(pDisplay, gc, clrMenuActiveBG.pixel);
1222 
1223  XFillRectangle(pDisplay, wID, gc, pItem->iPosition, pSelf->iY, pItem->iTextWidth, pSelf->iHeight + 2);
1224 
1225  XSetForeground(pDisplay, gc, clrMenuActiveFG.pixel);
1226  }
1227 
1228  szText = pItem->data + pItem->iMenuItemText;
1229 
1230  if(strchr(szText, '_'))
1231  {
1232  char *p1;
1233  strcpy(tbuf, szText);
1234  p1 = tbuf;
1235  while(*p1)
1236  {
1237  if(*p1 == '_')
1238  {
1239  *p1 = 0;
1240 
1241  if(p1 == tbuf)
1242  iU1 = 0;
1243  else
1244  iU1 = XTextWidth(pFont, tbuf, p1 - tbuf);
1245 
1246  if(p1[1])
1247  {
1248  iU2 = XTextWidth(pFont, p1, 1);
1249  strcpy(p1, p1 + 1);
1250  }
1251  else
1252  {
1253  iU2 = iU1; // shouldn't happen
1254  break;
1255  }
1256  }
1257  p1++;
1258  }
1259 
1260  szText = tbuf;
1261  }
1262 
1263  if(pItem->iPosition < 0)
1264  pItem->iPosition = iHPos; // also needed for mousie/clickie
1265  if(pItem->iTextWidth < 0)
1266  pItem->iTextWidth = XTextWidth(pFont, szText, strlen(szText));
1267 
1268  // TODO: change string into a series of XTextItem structures and
1269  // then call XDrawText to draw the array of 'XTextItem's
1270  if(*szText)
1271  XDrawString(pDisplay, wID, gc, iHPos, iVPos, szText, strlen(szText));
1272 
1273  if(strlen(szText) < strlen(pItem->data + pItem->iMenuItemText))
1274  {
1275  xpt[0].x=iHPos + iU1 - 1;
1276  xpt[0].y=pSelf->iY + pSelf->iHeight - 1;
1277  xpt[1].x=iHPos + iU1 + iU2;
1278  xpt[1].y=xpt[0].y;
1279 
1280  XDrawLines(pDisplay, wID, gc, xpt, 2, CoordModeOrigin);
1281  }
1282 
1283  if(i1 == pSelf->iSelected) // selected item
1284  {
1285  XSetForeground(pDisplay, gc, clrMenuFG.pixel);
1286  XSetBackground(pDisplay, gc, clrMenuBG.pixel);
1287  }
1288 
1289  iHPos += pItem->iTextWidth + i2;
1290  }
1291 
1292  pSelf->iWidth = iHPos - i2; // update the width of the menu bar
1293 
1294  // by convention, restore original objects/state
1295 
1296  xgc.font = pOldFont->fid;
1298  XChangeGC(pDisplay, gc, GCFont, &xgc);
1299 
1300  XSetForeground(pDisplay, gc, WBGetWindowFGColor(wID)); // restore it at the end
1302 
1303  WBEndPaint(wID, gc);
1304 
1305  return 1; // processed
1306 }
1307 
1308 
void * WBGetPointerFromHash(WB_UINT32 uiHash)
Obtain a pointer from a 32-bit &#39;secure&#39; pointer hash value.
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
static __inline__ XStandardColormap * PXM_StandardColormapFromColormap(Display *pDisplay, Colormap colormap)
create temporary XStandardColormap from a Colormap
structure for managing menu items
Definition: menu.h:185
Atom aMENU_UI_COMMAND
UI notifications sent by menus to owning Frame windows via ClientMessage using &#39;WBWindowDispatch&#39;.
void PXM_RGBToYUV(int iR, int iG, int iB, int *piY, int *piU, int *piV)
Convert R, G, B values to Y, U, V with 0-255 range.
&#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:299
int iAction
Definition: menu.h:139
bold font weight (mutually exclusive0
Definition: font_helper.h:439
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
void PXM_YUVToRGB(int iY, int iU, int iV, int *piR, int *piG, int *piB)
Convert Y, U, V values to R, G, B with 0-255 range.
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:499
#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:518
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
WB_UINT32 WBCreatePointerHash(void *pPointer)
Create/obtain a 32-bit &#39;secure&#39; hash for a pointer.
&#39;configuration helper&#39; main header file for the X11 Work Bench Toolkit API
void PXM_RGBToPixel(XStandardColormap *pMap, XColor *pColor)
Icon Registration for application &#39;large&#39; and &#39;small&#39; icons.
#define RGB255_TO_XCOLOR(R, G, B, X)
Simple RGB assignment to pixel, 0-255 RGB.
Definition: pixmap_helper.h:95
void PXM_PixelToRGB(XStandardColormap *pMap, XColor *pColor)
Convert the pixel menber of an XColor to RGB.
void MBReCalcMenuBarWindow(WBMenuBarWindow *pMenuBar)
Cause a &#39;layout recalculation&#39; for a Menu Bar window.
Definition: menu_bar.c:492
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:409
#define WB_STANDARD_INPUT_MASK
&#39;Standard&#39; input mask, bit flag for window creation
Atom aMENU_COMMAND
commands sent by menus via ClientMessage
void WBSetWindowFontStruct(Window wID, XFontStruct *pFontStruct)
assigns the default XFontStruct to a window
#define RGB255_FROM_XCOLOR(X, R, G, B)
Simple RGB assignment from pixel, 0-255 RGB.
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:332
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:356
WBMenuBarWindow * MBFindMenuBarWindow(WBMenu *pMenu)
Locate the first WBMenuBarWindow that is using a WBMenu structure.
Definition: menu_bar.c:480
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:415
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
Atom WBGetAtom(Display *pDisplay, const char *szAtomName)
Lookup and/or allocate an internal Atom for a named string (lookups include X11 atoms) ...
#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
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 WBDestroyPointerHash(WB_UINT32 uiHash)
Destroy a 32-bit &#39;secure&#39; hash for a pointer.
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:327
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
Atom aSET_FOCUS
dialog focus messages
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
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:349
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.