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