X11workbench Toolkit  1.0
menu.c
Go to the documentation of this file.
1 // _ __ ___ ___ _ __ _ _ ___ //
3 // | '_ ` _ \ / _ \| '_ \ | | | | / __| //
4 // | | | | | || __/| | | || |_| | _| (__ //
5 // |_| |_| |_| \___||_| |_| \__,_|(_)\___| //
6 // //
7 // //
8 // generic menu resource implementation //
9 // //
11 
12 /*****************************************************************************
13 
14  X11workbench - X11 programmer's 'work bench' application and toolkit
15  Copyright (c) 2010-2019 by Bob Frazier (aka 'Big Bad Bombastic Bob')
16 
17 
18  DISCLAIMER: The X11workbench application and toolkit software are supplied
19  'as-is', with no warranties, either implied or explicit.
20  Any claims to alleged functionality or features should be
21  considered 'preliminary', and might not function as advertised.
22 
23  MIT-like license:
24 
25  There is no restriction as to what you can do with this software, so long
26  as you include the above copyright notice and DISCLAIMER for any distributed
27  work that is equal to or derived from this one, along with this paragraph
28  that explains the terms of the license if the source is also being made
29  available. A "derived work" describes a work that uses a significant portion
30  of the source files or algorithms that are included with this one.
31  Specifically excluded from this are files that were generated by the software,
32  or anything that is included with the software that is part of another package
33  (such as files that were created or added during the 'configure' process).
34  Specifically included is the use of part or all of any of the X11 workbench
35  toolkit source or header files in your distributed application. If you do not
36  ship the source, the above copyright statement is still required to be placed
37  in a reasonably prominent place, such as documentation, splash screens, and/or
38  'about the application' dialog boxes.
39 
40  Use and distribution are in accordance with GPL, LGPL, and/or the above
41  MIT-like license. See COPYING and README files for more information.
42 
43 
44  Additional information at http://sourceforge.net/projects/X11workbench
45 
46 ******************************************************************************/
47 
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60 #include <memory.h>
61 #include <string.h>
62 #include <strings.h>
63 #include <signal.h>
64 #include <time.h>
65 #include <ctype.h>
66 #include <X11/cursorfont.h>
67 
68 #ifndef XK_Delete /* moslty for interix */
69 #define XK_MISCELLANY /* mostly for interix */
70 #include <X11/keysymdef.h> // some platforms don't automatically include this with X headers
71 #endif // XK_Delete
72 
73 #include "window_helper.h"
74 #include "platform_helper.h"
75 #include "pixmap_helper.h" // pixmap helpers, including pre-defined icons
76 #include "frame_window.h"
77 #include "menu.h"
78 #include "conf_help.h"
79 
80 
81 // sample menu resource format
82 //
83 // 1\n
84 // _File\tpopup\t2\n
85 // \tseparator\n
86 // _Help\t100\tHelp\tF1\n
87 //
88 // 2\tpopup\n
89 // _Open\t102\tOpen File\tCtrl+O\n
90 // _Save\t103\tSave File\tCtrl+S\n
91 // \tseparator\n
92 // E_xit\t104\Close Application\tAlt+F4\n
93 //
94 // this describes 2 separate menu resource strings. The first has ID 1, the 2nd has ID 2.
95 // The ID values are relative only to the menu itself.
96 
97 // generic description of format:
98 // <menu ID>[<tab>popup]<newline>
99 // <menu text><tab>[separator|popup<tab><submenu ID>|<notify ID>[<ToolTip>[<Hotkey>]]<newline>
100 // <menu text><tab>[separator|popup<tab><submenu ID>|<notify ID>[<ToolTip>[<Hotkey>]]<newline>
101 // (etc.)
102 //
103 
104 static int InternalPopulateMenuFromResource(WBMenu *pMenu, int iID, const char *pszResource);
105  // parsing a menu resource
106 
107 WBMenu *MBCreateMenu(int iID, int iPopup, const char *pszResource, int iReserveSpace)
108 {
109  WBMenu *pRval;
110  unsigned char *pEnd;
111  const char *p1, *p2;
112 
113  pRval = (WBMenu *)WBAlloc(sizeof(WBMenu) + iReserveSpace); // for now just do this
114 
115  if(!pRval)
116  {
117  return NULL;
118  }
119 
120  bzero(pRval, sizeof(WBMenu) + iReserveSpace); // make sure
121  pRval->uiTag = WBMENU_TAG;
122 
123  pEnd = (unsigned char *)(pRval + 1);
124 
125  pRval->ppItems = (WBMenuItem **)pEnd;
126  pRval->nMaxItems = iReserveSpace / (2 * sizeof(WBMenuItem **));
127 
128  pEnd += iReserveSpace / 2;
129 
130  pRval->ppPopups = (WBMenu **)pEnd;
131  pRval->nMaxPopups = iReserveSpace / (2 * sizeof(WBMenu **));
132 
133  if(!pszResource) // no resource?
134  return pRval; // for now, do this (later do I cough?)
135 
136  // now that the memory block has been correcty prepared, parse out the entries
137 
138  if(iID == -1) // which means I'm parsing the entire resource
139  {
140  char tbuf[16];
141 
142  // the menu resource should start with an id as the only entry on a line.
143  // look for the first ID, then create menu entries for it
144 
145  p1 = pszResource;
146  while(*p1 && *p1 <= ' ')
147  p1++;
148 
149  p2 = p1;
150  while(*p1 && *p1 > ' ')
151  p1++;
152 
153  bzero(tbuf, sizeof(tbuf));
154  if(p2 < p1)
155  memcpy(tbuf, p2, sizeof(tbuf) - 1 < (p1 - p2) ? sizeof(tbuf) - 1 : p1 - p2);
156 
157  while(*p1 && *p1 != '\n')
158  {
159  if(*p1 > ' ') // a syntax error
160  {
161  MBDestroyMenu(pRval);
162  return NULL;
163  }
164  p1++;
165  }
166 
167  if(!*tbuf || *p1 != '\n' || // required to end in \n and be a valid #
168  (iID = atoi(tbuf)) <= 0)
169  {
170  MBDestroyMenu(pRval);
171  return NULL;
172  }
173 
174  iPopup = 0; //for now force it
175  }
176 
177  pRval->iMenuID = iID; // store menu's ID at this time
178  if(iPopup)
179  pRval->iMenuID |= WBMENU_POPUP_HIGH_BIT; // mark as popup
180 
181  if(!InternalPopulateMenuFromResource(pRval,iID,pszResource))
182  {
183  MBDestroyMenu(pRval);
184  return NULL;
185  }
186 
187  return pRval;
188 }
189 
190 void MBDestroyMenu(WBMenu *pMenu)
191 {
192 int i1;
193 
194  if(!MBIsMenuValid(pMenu))
195  {
196  WB_ERROR_PRINT("ERROR: %s - invalid menu pointer %p\n", __FUNCTION__, pMenu);
197  return;
198  }
199 
200  if(pMenu->ppItems) // part of 'pMenu' memory block
201  {
202  for(i1=0; i1 < pMenu->nItems; i1++)
203  {
204  MBDestroyMenuItem(pMenu->ppItems[i1]);
205  }
206  }
207 
208  if(pMenu->ppPopups) // part of 'pMenu' memory block
209  {
210  for(i1=0; i1 < pMenu->nPopups; i1++)
211  {
212  MBDestroyMenu(pMenu->ppPopups[i1]);
213  }
214  }
215 
216  WBDestroyPointerHashPtr(pMenu); // in case it was in use
217 
218  WBFree(pMenu);
219 }
220 
221 int MBIsMenuValid(const WBMenu *pMenu)
222 {
223  // first check to see that the menu pointer is valid memory
224 
225  if(!pMenu)
226  {
227  return 0;
228  }
229 
230  // For now, we MUST assume that a WBMenu was allocated using MBCreateMenu()
231  // or else this next part will not work properly
232 
233  if(WBAllocUsableSize((void *)pMenu) < sizeof(WBMenu))
234  {
235  WB_ERROR_PRINT("ERROR: %s - invalid WBMenu pointer %p\n", __FUNCTION__, pMenu);
236  return 0;
237  }
238 
239  // for now, just validate the tag
240 
241  return pMenu->uiTag == WBMENU_TAG;
242 }
243 
244 int MBIsMenuItemValid(const WBMenuItem *pMenuItem)
245 {
246  // first check to see that the menu pointer is valid memory
247 
248  if(!pMenuItem)
249  {
250  return 0;
251  }
252 
253  // For now, we MUST assume that a WBMenuItem was allocated using MBCreateMenuItem()
254  // or else this next part will not work properly
255 
256  if(WBAllocUsableSize((void *)pMenuItem) < sizeof(WBMenuItem))
257  {
258  WB_ERROR_PRINT("ERROR: %s - invalid WBMenuItem pointer %p\n", __FUNCTION__, pMenuItem);
259  return 0;
260  }
261 
262  // for now, just validate the tag
263 
264  return pMenuItem->uiTag == WBMENUITEM_TAG;
265 }
266 
268 {
269  if(!MBIsMenuItemValid(pMenuItem))
270  {
271  WB_ERROR_PRINT("ERROR: %s - invalid menu item pointer %p\n", __FUNCTION__, pMenuItem);
272  return;
273  }
274 
275  WBDestroyPointerHashPtr(pMenuItem); // in case it was in use
276 
277  WBFree(pMenuItem);
278 }
279 
280 int MBAddMenuItem(WBMenu *pMenu, const WBMenuItem *pItem, int iPos)
281 {
282  if(!MBIsMenuValid(pMenu))
283  {
284  WB_ERROR_PRINT("ERROR: %s - invalid menu pointer %p\n", __FUNCTION__, pMenu);
285  return -1;
286  }
287 
288  if(!MBIsMenuItemValid(pItem))
289  {
290  WB_ERROR_PRINT("ERROR: %s - invalid menu item pointer %p\n", __FUNCTION__, pItem);
291  return -1;
292  }
293 
294  return -1;
295 }
296 
297 void MBRemoveMenuItem(WBMenu *pMenu, int iPos)
298 {
299  if(!MBIsMenuValid(pMenu))
300  {
301  WB_ERROR_PRINT("ERROR: %s - invalid menu pointer %p\n", __FUNCTION__, pMenu);
302  return;
303  }
304 
305 }
306 
307 int MBAddPopupMenu(WBMenu *pMenu, const WBMenu *pPopupMenu)
308 {
309  if(!MBIsMenuValid(pMenu))
310  {
311  WB_ERROR_PRINT("ERROR: %s - invalid menu pointer %p\n", __FUNCTION__, pMenu);
312  return -1;
313  }
314 
315  if(!MBIsMenuValid(pPopupMenu))
316  {
317  WB_ERROR_PRINT("ERROR: %s - invalid menu pointer %p\n", __FUNCTION__, pPopupMenu);
318  return -1;
319  }
320 
321  // TODO: implement this
322 
323  return -1;
324 }
325 
326 WBMenu *MBFindPopupMenu(WBMenu *pMenu, int idPopup)
327 {
328  if(!MBIsMenuValid(pMenu))
329  {
330  WB_ERROR_PRINT("ERROR: %s - invalid menu pointer %p\n", __FUNCTION__, pMenu);
331  return NULL;
332  }
333 
334  // TODO: implement this
335 
336  return NULL;
337 }
338 
339 void MBRemovePopupMenu(WBMenu *pMenu, int idPopup)
340 {
341  if(!MBIsMenuValid(pMenu))
342  {
343  WB_ERROR_PRINT("ERROR: %s - invalid menu pointer %p\n", __FUNCTION__, pMenu);
344  return;
345  }
346 
347  // TODO: implement this
348 }
349 
350 WBMenu *MBCopyMenu(const WBMenu *pMenu, int iReserveSpace)
351 {
352  // TODO: make a copy of the main structure, the individual WBMenuItem structures,
353  // and recursively copy the 'popup' WBMenu structures
354 
355  if(!MBIsMenuValid(pMenu))
356  {
357  WB_ERROR_PRINT("ERROR: %s - invalid menu pointer %p\n", __FUNCTION__, pMenu);
358  return NULL;
359  }
360 
361  // TODO: implement this
362 
363  return NULL;
364 }
365 
366 
367 #define NEXT_LINE(X,Y) { while(*X && *X <= ' ' && *X != '\t' && *X != '\n') X++; Y=X; while(*X && *X != '\n') X++; if(*X=='\n') X++; }
368 #define NEXT_COL(X,Y,Z) { while(*X && *X <= ' ' && *X != '\t' && *X != '\n' && X < Z) X++; \
369  Y=X; while(*X && *X != '\t' && *X != '\n' && X < Z) X++; if(*X == '\t' || *X == '\n') X++; }
370 
371 static void InternalLocateMenuInResource(const char *pszResource, int *piID,
372  const char **ppStart, const char **ppEnd)
373 {
374  char tbuf[16];
375  const char *p1, *p2, *p3;
376  char *p4;
377  int i1;
378 
379  p1 = pszResource;
380 
381  while(*p1)
382  {
383  NEXT_LINE(p1, p2);
384 
385 // fprintf(stderr, "found line %-.*s\n", (int)(p1 - p2), p2);
386 
387  p3 = p2;
388  while(p3 < p1 && *p3 != '\n' && *p3 != '\t')
389  {
390  p3++;
391  }
392 
393  if(*p3 == '\t') // line contains a tab
394  {
395  continue;
396  }
397 
398  bzero(tbuf, sizeof(tbuf));
399  memcpy(tbuf, p2, (sizeof(tbuf) - 1) < (p1 - p2) ? sizeof(tbuf) - 1 : (p1 - p2));
400 
401  p4 = tbuf + strlen(tbuf);
402 
403  while(p4 > tbuf && *(p4 - 1) <= ' ')
404  {
405  *(--p4) = 0; // trim trailing white space
406  }
407 
408  i1 = atoi(tbuf);
409 
410  if(i1 <= 0)
411  {
412  continue;
413  }
414 
415  if(*piID == -1)
416  {
417  *piID = i1; // keep track of what I actually found
418  break;
419  }
420 
421  if(*piID == i1) // a match
422  {
423  break;
424  }
425  }
426 
427  *ppStart = p1; // this will be the beginning of the next line after the ID
428  if(!*p1)
429  {
430  *ppEnd = p1;
431  return;
432  }
433 
434  // next, find the end of the menu resource
435 
436  while(*p1)
437  {
438  NEXT_LINE(p1, p2);
439 
440 // fprintf(stderr, "(b) found line %-.*s\n", (int)(p1 - p2), p2);
441 
442  p3 = p2;
443  while(p3 < p1 && *p3 != '\n' && *p3 != '\t')
444  {
445  p3++;
446  }
447 
448  if(*p3 == '\t') // line contains a tab
449  {
450  continue;
451  }
452 
453  bzero(tbuf, sizeof(tbuf));
454  memcpy(tbuf, p2, (sizeof(tbuf) - 1) < (p1 - p2) ? sizeof(tbuf) - 1 : (p1 - p2));
455 
456  p4 = tbuf + strlen(tbuf);
457 
458  while(p4 > tbuf && *(p4 - 1) <= ' ')
459  {
460  *(--p4) = 0; // trim trailing white space
461  }
462 
463  i1 = atoi(tbuf);
464 
465  if(i1 <= 0)
466  {
467  continue;
468  }
469 
470  p1 = p2; // point back at the beginning of the line
471  p2 = *ppStart; // start of section
472 
473  while(p1 > p2 && *(p1 - 1) != '\n')
474  {
475  p1--; // find newline prior to where I'm at right now
476  }
477 
478  break; // and that's it - p1 points to end of section
479  }
480 
481  *ppEnd = p1;
482 
483 }
484 
485 static int InternalPopulateMenuFromResource(WBMenu *pMenu, int iID, const char *pszResource)
486 {
487 const char *p1, *p2; //, *p3;
488 int i1;
489 
490  p1 = pszResource;
491 
492  // Owning menu will be 'iID' so I need that section first
493 
494  InternalLocateMenuInResource(pszResource, &iID, &p1, &p2);
495  if(p1 == p2) // not found
496  {
497  return 0;
498  }
499 
500  // parse out the menu resource and build my structure
501  // (assume it's empty when I begin)
502 
503  while(p1 < p2)
504  {
505  WBMenuItem *pItem;
506 
507  pItem = MBCreateMenuItem(&p1);
508  if(!pItem)
509  {
510  continue;
511  }
512 
513  pMenu->ppItems[(pMenu->nItems)++] = pItem;
514 
515  if(pMenu->nItems >= pMenu->nMaxItems)
516  {
517  break;
518  }
519 
520  while(p1 < p2 && *p1 <= ' ' && *p1 != '\t')
521  {
522  p1++; // skip any blank lines and leading white space I might find, excluding tabs
523  }
524  }
525 
526  // Finally, create menus for all of the popups and populate my structure with pointers to them.
527 
528  for(i1=0; i1 < pMenu->nItems; i1++)
529  {
530  if(!pMenu->ppItems[i1] || // just in case
531  pMenu->ppItems[i1]->iAction == WBMENU_SEPARATOR)
532  {
533  // separator (so I don't try and create a popup for it)
534  }
535  else if(pMenu->ppItems[i1]->iAction & WBMENU_DYNAMIC_HIGH_BIT)
536  {
537  // dynamic menu entry - no popups created, and menu display is created 'on the fly'
538  }
539  else if(pMenu->ppItems[i1]->iAction & WBMENU_POPUP_HIGH_BIT)
540  {
541  WBMenu *pPopup;
542 
543 // fprintf(stderr, "INFO - searching for popup %d\n", pMenu->ppItems[i1]->iAction & WBMENU_POPUP_MASK);
544 
545  pPopup = MBCreateMenu(pMenu->ppItems[i1]->iAction & WBMENU_POPUP_MASK, 1,
546  pszResource, WBMENU_RESERVE_DEFAULT);
547  if(pPopup)
548  {
549  pMenu->ppPopups[(pMenu->nPopups)++] = pPopup;
550 
551  if(pMenu->nPopups >= pMenu->nMaxPopups)
552  {
553  break;
554  }
555  }
556  else
557  {
558  WB_WARN_PRINT("%s - WARNING: Unable to create popup for %d\n",
559  __FUNCTION__, pMenu->ppItems[i1]->iAction & WBMENU_POPUP_MASK);
560  }
561  }
562  }
563 
564  return 1; // for now...
565 }
566 
567 #define ALT_HOTKEY 0x80000000L
568 #define CTRL_HOTKEY 0x40000000L
569 #define SHIFT_HOTKEY 0x20000000L
570 
571 static int TranslateHotKeyText(const char *szText) // translate key or return UTF-8 char
572 {
573  if((szText[0] == 'F' || szText[0] == 'f') &&
574  szText[1] >= '1' && szText[1] <= '9')
575  {
576  int iF = szText[1] - '0';
577  if(szText[2] >= '0' && szText[2] <= '9')
578  {
579  iF = iF * 10 + (szText[2] - '0');
580  }
581  // function keys iF=1 through 35 are supported. Anything else is a bug/feature
582  return (XK_F1 + iF - 1);
583  }
584  else if(!strncasecmp(szText, "home", 4))
585  {
586  return XK_Home;
587  }
588  else if(!strncasecmp(szText, "end", 3))
589  {
590  return XK_End;
591  }
592  else if(!strncasecmp(szText, "left", 4))
593  {
594  return XK_Left;
595  }
596  else if(!strncasecmp(szText, "right", 5))
597  {
598  return XK_Right;
599  }
600  else if(!strncasecmp(szText, "up", 2))
601  {
602  return XK_Up;
603  }
604  else if(!strncasecmp(szText, "down", 4))
605  {
606  return XK_Down;
607  }
608  else if(!strncasecmp(szText, "pgup", 4) || !strncasecmp(szText, "prior", 5))
609  {
610  return XK_Page_Up;
611  }
612  else if(!strncasecmp(szText, "pgdown", 6) || !strncasecmp(szText, "next", 4))
613  {
614  return XK_Page_Down;
615  }
616  else if(!strncasecmp(szText, "del", 3))
617  {
618  return XK_Delete;
619  }
620  else if(!strncasecmp(szText, "ins", 3))
621  {
622  return XK_Insert;
623  }
624 
625  return *szText; // for now (later do UTF-8 translation)
626 }
627 
628 static int ProcessHotKeyText(const char *szText)
629 {
630  // format: 'n' or 'N' or 'Modifier[+Modifier...]+[n|N]'
631  // 'n' is the case-sensitive ASCII code for the keystroke
632  // 'Modifier' is Ctrl, Alt, Shift, or Meta [Meta is usually the same as 'Alt']
633 
634 
635  const char *p1 = strchr(szText, '+');
636  int iBits = 0;
637 
638  while(p1)
639  {
640  int iLen = p1 - szText;
641  if(iLen) // extremely forgiving
642  {
643  if((!strncasecmp(szText, "alt", 3) && iLen == 3) ||
644  (!strncasecmp(szText, "meta", 4) && iLen == 4))
645  {
646  iBits |= ALT_HOTKEY;
647  }
648  else if((!strncasecmp(szText, "ctrl", 4) && iLen == 4) ||
649  (!strncasecmp(szText, "control", 7) && iLen == 7))
650  {
651  iBits |= CTRL_HOTKEY;
652  }
653  else if(!strncasecmp(szText, "shift", 5) && iLen == 5)
654  {
655  iBits |= SHIFT_HOTKEY;
656  }
657  else
658  {
659  WB_WARN_PRINT("%s - Hotkey description %*s not defined\n", __FUNCTION__, iLen, szText);
660  }
661  }
662 
663  szText = p1 + 1; // point just past '+'
664  p1 = strchr(szText, '+'); // location of new '+'
665  }
666 
667  return TranslateHotKeyText(szText) | iBits;
668 }
669 
670 WBMenuItem *MBCreateMenuItem(const char **ppszResource)
671 {
672 char tbuf[256];
673 const char *p1, *p2, *p3, *p5;
674 char *p4;
675 int i1;
676 WBMenuItem *pRval = NULL;
677 
678  p2 = *ppszResource;
679  NEXT_LINE(p2, p1);
680 // fprintf(stderr, " LINE: %-.*s\n", (int)(p2 - p1), p1);
681 
682  i1 = 0;
683  while(p1 < p2)
684  {
685  NEXT_COL(p1, p3, p2); // menu text
686 
687  if(p1 > p3)
688  {
689  i1 += (p1 - p3) + 2;
690  }
691 
692  if(p1 >= p2)
693  {
694  break;
695  }
696 
697  NEXT_COL(p1, p3, p2); // message ID, 'popup', or 'separator', or 'dynamic'
698 
699  if(p1 >= p2)
700  {
701  break;
702  }
703 
704  NEXT_COL(p1, p3, p2); // toolhelp text
705 
706  if(p1 > p3)
707  {
708  i1 += (p1 - p3) + 2;
709  }
710 
711  if(p1 >= p2)
712  {
713  break;
714  }
715 
716  NEXT_COL(p1, p3, p2); // reserved text
717 
718  if(p1 > p3)
719  {
720  i1 += (p1 - p3) + 2;
721  }
722  }
723 
724  pRval = (WBMenuItem *)WBAlloc(sizeof(WBMenuItem) + i1);
725  if(!pRval)
726  {
727  return NULL; // bummer (no memory)
728  }
729 
730  bzero(pRval, sizeof(WBMenuItem) + i1); // make sure
731  pRval->uiTag = WBMENUITEM_TAG;
732 
733  pRval->iMenuItemText = -1; // "none"
734  pRval->iUnderscore = -1; // "none"
735  pRval->iTooltipText = -1; // "none"
736  pRval->iHotKey = -1; // "none"
737  pRval->iAction = 0;
738  pRval->nHotKey = 0; // "none"
739  pRval->iTextWidth = -1; // "calculate it"
740  pRval->iPosition = -1; // "calculate it"
741  pRval->nDataSize = i1 + sizeof(pRval->data); // total size of data
742 
743  p2 = *ppszResource;
744  NEXT_LINE(p2, p1); // one more time!
745  *ppszResource = p2; // end of line pointer, start of next line
746 
747  p4 = (char *)pRval->data; // this is the output buffer where I store copies of the information as text
748 
749  while(p1 < p2)
750  {
751  NEXT_COL(p1, p3, p2); // menu text
752 
753  if(p1 > p3)
754  {
755  pRval->iMenuItemText = (p4 - pRval->data);
756  p5 = p4; // hold onto the position of the menu text in the output buffer
757  // to find the end of this item's descriptive text, look for tab or newline
758  if(p1 > p3 && (*(p1 - 1) == '\t' || *(p1 - 1) == '\n'))
759  {
760  memcpy(p4, p3, (p1 - p3) - 1);
761  p4 += (p1 - p3) - 1;
762  }
763  else if(p1 > p3)
764  {
765  memcpy(p4, p3, p1 - p3);
766  p4 += (p1 - p3);
767  }
768 
769  *(p4++) = 0;
770 
771  while(*p5 && *p5 != '_')
772  {
773  p5++; // find underscore or end of string
774  }
775  if(*p5 == '_')
776  {
777  pRval->iUnderscore = (p5 - pRval->data);
778  }
779  else
780  {
781  pRval->iUnderscore = -1; // to enforce "no underscore" later
782  }
783  }
784 
785  if(p1 >= p2) // checking that I parsed past the end of the string
786  {
787  break;
788  }
789 
790  NEXT_COL(p1, p3, p2); // message ID, 'popup', or 'separator', or 'dynamic'
791 
792  if(p1 > p3) // parse similar to the menu text, store offsets and other info
793  {
794  bzero(tbuf, sizeof(tbuf));
795  memcpy(tbuf, p3, (p1 - p3) > (sizeof(tbuf) - 1) ? sizeof(tbuf) - 1 : p1 - p3);
796 
797  // trim any trailing white space (like line feeds, tabs, ???)
798  i1 = strlen(tbuf);
799  while(i1 > 0 && tbuf[i1 - 1] <= ' ')
800  {
801  tbuf[--i1] = 0;
802  }
803 
804  if(!strcasecmp(tbuf, "separator"))
805  {
806  pRval->iAction = WBMENU_SEPARATOR;
807  }
808  else if(!strcasecmp(tbuf, "dynamic"))
809  {
811  }
812  else if(!strcasecmp(tbuf, "popup"))
813  {
815  }
816  else
817  {
818  i1 = atoi(tbuf);
819 
820  if(i1 > 0)
821  {
822  pRval->iAction = i1;
823  }
824  else
825  {
826  // assume it's an atom, and get its value as an integer (or allocate if not there)
827  pRval->iAction = (int)WBGetAtom(WBGetDefaultDisplay(), tbuf);
828 
829  if(pRval->iAction == (int)None)
830  {
831  WB_WARN_PRINT("%s - WARNING: cannot find menu message atom %s\n", __FUNCTION__, tbuf);
832  }
833  }
834  }
835  }
836 
837  if(p1 >= p2)
838  {
839  break;
840  }
841 
842  NEXT_COL(p1, p3, p2); // toolhelp text or popup menu ID, or menu ID for 'dynamic'
843  if(p1 > p3)
844  {
845  if(pRval->iAction == WBMENU_POPUP_HIGH_BIT ||
847  {
848  bzero(tbuf, sizeof(tbuf));
849  memcpy(tbuf, p3, (p1 - p3) > (sizeof(tbuf) - 1) ? sizeof(tbuf) - 1 : p1 - p3);
850 
851  while(tbuf[0] && tbuf[strlen(tbuf) - 1] <= ' ')
852  {
853  tbuf[strlen(tbuf) - 1] = 0; // right trim
854  }
855 
856  if(pRval->iAction == WBMENU_POPUP_HIGH_BIT)
857  {
858  pRval->iAction |= atoi(tbuf);
859  }
860  else // dynamic menu action
861  {
862  pRval->iAction |= (int)WBGetAtom(WBGetDefaultDisplay(), tbuf);
863  }
864  }
865  else
866  {
867  pRval->iTooltipText = (p4 - pRval->data);
868  if(p1 > p3 && *(p1 - 1) == '\t')
869  {
870  memcpy(p4, p3, (p1 - p3) - 1);
871  p4[p1 - p3 - 1] = 0; // terminate string
872  }
873  else
874  {
875  if(p1 > p3)
876  memcpy(p4, p3, p1 - p3);
877  p4[p1 - p3] = 0; // terminate string
878  }
879 
880  // next, trim the string as much as I can
881  i1=strlen(p4);
882  while(i1 > 0 && p4[i1 - 1] <= ' ')
883  p4[--i1] = 0;
884 
885  p4 += i1 + 1;
886  }
887  }
888 
889  if(p1 >= p2)
890  {
891  break;
892  }
893 
894  NEXT_COL(p1, p3, p2); // hotkey (non-popup) or toolhelp text (popup)
895  if(p1 > p3)
896  {
897  if(pRval->iAction & WBMENU_POPUP_HIGH_BIT)
898  {
899  pRval->iTooltipText = (p4 - pRval->data);
900 
901  if(p1 > p3 && *(p1 - 1) == '\t')
902  {
903  memcpy(p4, p3, (p1 - p3) - 1);
904 
905  p4[p1 - p3 - 1] = 0; // terminate string
906  }
907  else
908  {
909  if(p1 > p3)
910  {
911  memcpy(p4, p3, p1 - p3);
912  }
913 
914  p4[p1 - p3] = 0; // terminate string
915  }
916 
917  // next, trim the string as much as I can
918  i1=strlen(p4);
919 
920  while(i1 > 0 && p4[i1 - 1] <= ' ')
921  {
922  p4[--i1] = 0;
923  }
924 
925  p4 += i1 + 1;
926  }
927  else if(pRval->iAction & ~(WBMENU_POPUP_MASK)) // only for "other than 'regular' menu items"
928  {
929  // TODO: any special thing for these, dynamics, etc.
930  }
931  else
932  {
933  pRval->iHotKey = (p4 - pRval->data);
934 
935  if(p1 > p3 && *(p1 - 1) == '\t')
936  {
937  memcpy(p4, p3, (p1 - p3) - 1);
938  p4[p1 - p3 - 1] = 0; // terminate string
939  }
940  else
941  {
942  if(p1 > p3)
943  {
944  memcpy(p4, p3, p1 - p3);
945  }
946 
947  p4[p1 - p3] = 0; // terminate string
948  }
949 
950  // next, trim the string as much as I can
951  i1=strlen(p4);
952 
953  while(i1 > 0 && p4[i1 - 1] <= ' ')
954  {
955  p4[--i1] = 0;
956  }
957 
958  if(*p4)
959  {
960  pRval->nHotKey = ProcessHotKeyText(p4);
961  }
962  else
963  {
964  pRval->nHotKey = -1; // force NONE
965  }
966 
967  p4 += i1 + 1;
968  }
969  }
970 
971  if(p1 >= p2)
972  {
973  break;
974  }
975 
976  NEXT_COL(p1, p3, p2); // reserved text
977 
978  if(p1 > p3)
979  {
980  if(p1 > p3 && *(p1 - 1) == '\t')
981  {
982  memcpy(p4, p3, (p1 - p3) - 1);
983  p4[p1 - p3 - 1] = 0; // terminate string
984  }
985  else
986  {
987  if(p1 > p3)
988  {
989  memcpy(p4, p3, p1 - p3);
990  }
991 
992  p4[p1 - p3] = 0; // terminate string
993  }
994 
995  // next, trim the string as much as I can
996  i1=strlen(p4);
997 
998  while(i1 > 0 && p4[i1 - 1] <= ' ')
999  {
1000  p4[--i1] = 0;
1001  }
1002 
1003  p4 += i1 + 1;
1004  }
1005  }
1006 
1007 #ifndef NO_DEBUG
1008 // if((WBGetDebugLevel() & DebugLevel_MASK) >= DebugLevel_Chatty ||
1009 // (WBGetDebugLevel() & DebugSubSystem_Menu))
1010  WB_IF_DEBUG_LEVEL(DebugLevel_Chatty | DebugSubSystem_Menu)
1011  {
1012  if(pRval->iAction == WBMENU_SEPARATOR)
1013  {
1014  WBDebugPrint(" MENU ITEM SEPARATOR\n");
1015  }
1016  else if(pRval->iAction & WBMENU_DYNAMIC_HIGH_BIT)
1017  {
1019  WBDebugPrint(" DYNAMIC MENU ITEM %d \"%s\"\n",
1020  pRval->iAction & WBMENU_POPUP_MASK, p1);
1021  if(p4)
1022  {
1023  WBFree(p4);
1024  p4 = NULL;
1025  }
1026  }
1027  else if(pRval->iAction & WBMENU_POPUP_HIGH_BIT)
1028  {
1030 
1031  if(pRval->iHotKey == -1)
1032  {
1033  WBDebugPrint(" POPUP MENU \"%s\" %d \"%s\" \"%s\" %-.2s\n",
1034  pRval->data + pRval->iMenuItemText,
1035  pRval->iAction & WBMENU_POPUP_MASK, p4,
1036  pRval->iTooltipText >= 0 ? pRval->data + pRval->iTooltipText : "",
1037  pRval->iUnderscore >= 0 ? pRval->data + pRval->iUnderscore : "");
1038  }
1039  else
1040  {
1041  WBDebugPrint(" POPUP MENU \"%s\" %d \"%s\" \"%s\" \"%s\"(%xH) %-.2s\n",
1042  pRval->data + pRval->iMenuItemText,
1043  pRval->iAction & WBMENU_POPUP_MASK, p4,
1044  pRval->iTooltipText >= 0 ? pRval->data + pRval->iTooltipText : "",
1045  pRval->iHotKey >= 0 ? pRval->data + pRval->iHotKey : "",
1046  pRval->nHotKey,
1047  pRval->iUnderscore >= 0 ? pRval->data + pRval->iUnderscore : "");
1048  }
1049 
1050  if(p4)
1051  {
1052  WBFree(p4);
1053  p4 = NULL;
1054  }
1055  }
1056  else
1057  {
1059 
1060  if(pRval->iHotKey == -1)
1061  {
1062  WBDebugPrint(" MENU ITEM \"%s\" %d \"%s\" \"%s\" %-.2s\n",
1063  pRval->data + pRval->iMenuItemText,
1064  pRval->iAction & WBMENU_POPUP_MASK, p4,
1065  pRval->iTooltipText >= 0 ? pRval->data + pRval->iTooltipText : "",
1066  pRval->iUnderscore >= 0 ? pRval->data + pRval->iUnderscore : "");
1067  }
1068  else
1069  {
1070  WBDebugPrint(" MENU ITEM \"%s\" %d \"%s\" \"%s\" \"%s\"(%xH) %-.2s\n",
1071  pRval->data + pRval->iMenuItemText,
1072  pRval->iAction & WBMENU_POPUP_MASK, p4,
1073  pRval->iTooltipText >= 0 ? pRval->data + pRval->iTooltipText : "",
1074  pRval->iHotKey >= 0 ? pRval->data + pRval->iHotKey : "",
1075  pRval->nHotKey,
1076  pRval->iUnderscore >= 0 ? pRval->data + pRval->iUnderscore : "");
1077  }
1078 
1079  if(p4)
1080  {
1081  WBFree(p4);
1082  p4 = NULL;
1083  }
1084  }
1085  }
1086 #endif // NO_DEBUG
1087 
1088  return pRval;
1089 }
1090 
1091 
1093 // _ _ _ _ ____ _
1094 // | | | | ___ | |_| | _____ _ _ | _ \ _ __ ___ ___ ___ ___ ___(_)_ __ __ _
1095 // | |_| |/ _ \| __| |/ / _ \ | | | | |_) | '__/ _ \ / __/ _ \/ __/ __| | '_ \ / _` |
1096 // | _ | (_) | |_| < __/ |_| | | __/| | | (_) | (_| __/\__ \__ \ | | | | (_| |
1097 // |_| |_|\___/ \__|_|\_\___|\__, | |_| |_| \___/ \___\___||___/___/_|_| |_|\__, |
1098 // |___/ |___/
1100 
1101 // hot key message processing
1102 //include files placed here for possible re-location to different file
1103 #include "menu_bar.h"
1104 #include "menu_popup.h"
1105 
1106 static void __PostMenuActivateEvent(WBMenu *pMenu, WBMenuItem *pItem, int iIndex, int iIsPopup)
1107 {
1108 XClientMessageEvent evt;
1109 
1110  if(!iIsPopup)
1111  {
1112  WBMenuBarWindow *pMB = MBFindMenuBarWindow(pMenu);
1113 
1114  if(pMB)
1115  {
1116  // post a high-priority message to the menu bar or popup window to activate the menu
1117 
1118  bzero(&evt, sizeof(evt));
1119  evt.type = ClientMessage;
1120  evt.display = WBGetWindowDisplay(pMB->wSelf);
1121  evt.window = pMB->wSelf;
1122  evt.message_type = aMENU_ACTIVATE;
1123  evt.format = 32;
1124  evt.data.l[0] = WBCreatePointerHash(pItem);
1125  evt.data.l[1] = iIndex; // index for menu item
1126 
1127  WBPostPriorityEvent(pMB->wSelf, (XEvent *)&evt);
1128 
1129  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1130  "%s - FOUND '%s', posting client event to menu bar\n",
1131  __FUNCTION__, &(pItem->data[pItem->iMenuItemText]));
1132  }
1133  else
1134  {
1135  // TODO: post to event owner?
1136  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1137  "%s - FOUND '%s', NOT posting client event to menu bar\n",
1138  __FUNCTION__, &(pItem->data[pItem->iMenuItemText]));
1139  }
1140  }
1141  else
1142  {
1144 
1145  if(pMP)
1146  {
1147  // post a high-priority message to the menu bar or popup window to activate the menu
1148 
1149  bzero(&evt, sizeof(evt));
1150  evt.type = ClientMessage;
1151  evt.display = WBGetWindowDisplay(pMP->wSelf);
1152  evt.window = pMP->wSelf;
1153  evt.message_type = aMENU_ACTIVATE;
1154  evt.format = 32;
1155  evt.data.l[0] = WBCreatePointerHash(pItem);
1156  evt.data.l[1] = iIndex; // index for menu item
1157 
1158  WBPostPriorityEvent(pMP->wSelf, (XEvent *)&evt);
1159 
1160  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1161  "%s - FOUND '%s', posting client event to popup\n",
1162  __FUNCTION__, &(pItem->data[pItem->iMenuItemText]));
1163  }
1164  else
1165  {
1166  // TODO: post to event owner?
1167  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1168  "%s - FOUND '%s', NOT posting client event to popup\n",
1169  __FUNCTION__, &(pItem->data[pItem->iMenuItemText]));
1170  }
1171  }
1172 }
1173 
1174 // pItem = __HotKeySearch(pMenu, iHotkey0, iIsPopup, &i1); // recursive hotkey search
1175 static WBMenuItem * __HotKeySearchRecurse(WBMenu *pMenu, int iHotKey, int *piIndex, int iLevel)
1176 {
1177 WBMenuItem *pRval = NULL;
1178 int i1;
1179 
1180  for(i1=0; i1 < pMenu->nItems; i1++)
1181  {
1182  WBMenuItem *pItem = pMenu->ppItems[i1];
1183 
1184  if(pItem->iHotKey < 0)
1185  {
1186  continue; // no hotkey
1187  }
1188 
1189  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1190  "%s - comparing %x to %x '%s' for %s\n",
1191  __FUNCTION__, iHotKey, pItem->nHotKey,
1192  &(pItem->data[pItem->iHotKey]), &(pItem->data[pItem->iMenuItemText]));
1193 
1194  if(pItem->nHotKey == iHotKey)
1195  {
1196  *piIndex = i1;
1197 
1198  if(!iLevel || !(pItem->iAction & WBMENU_POPUP_HIGH_BIT))
1199  {
1200  return pItem;
1201  }
1202 
1203  return NULL; // popups can't be returned except for level 0
1204  }
1205  }
1206 
1207  // now recurse all of the popups
1208  for(i1=0; i1 < pMenu->nPopups; i1++)
1209  {
1210  pRval = __HotKeySearchRecurse(pMenu->ppPopups[i1], iHotKey, piIndex, iLevel + 1);
1211 
1212  if(pRval || *piIndex >= 0) // piIndex >= 0 if found, but pRval will be NULL if popup
1213  {
1214  return pRval;
1215  }
1216  }
1217 
1218  return NULL; // not found
1219 }
1220 
1221 static WBMenuItem * __HotKeySearch(WBMenu *pMenu, int iHotKey, int *piIndex)
1222 {
1223  *piIndex = -1; // necessary
1224 
1225  return __HotKeySearchRecurse(pMenu, iHotKey, piIndex, 0);
1226 }
1227 
1228 int MBMenuProcessHotKey(WBMenu *pMenu, XKeyEvent *pEvent)
1229 {
1230 int iACS = 0, iKey, i1, iIsPopup, nChar;
1231 char tbuf[64];
1232 int iHotKey0;
1233 WBMenuItem *pItem;
1234 
1235 
1236  if(!MBIsMenuValid(pMenu))
1237  {
1238  WB_ERROR_PRINT("ERROR: %s - invalid menu pointer %p\n", __FUNCTION__, pMenu);
1239  return 0;
1240  }
1241 
1242 
1243  // check for hotkey and process (called by KeyEvent processing for frame, etc.)
1244 
1245  // menus use "key press"
1246  if(pEvent->type != KeyPress /* && pEvent->type != KeyRelease */)
1247  {
1248  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1249  "%s - event not KeyPress\n", __FUNCTION__);
1250  return 0;
1251  }
1252 
1253  nChar = sizeof(tbuf);
1254  iKey = WBKeyEventProcessKey((XKeyEvent *)pEvent, tbuf, &nChar, &iACS);
1255 
1256  // pressing or releasing shift, ctrl, alt, or 'meta' must not
1257  // generate an event. Fortunately these are all within a range
1258 
1259  if(iKey >= XK_Shift_L && iKey <= XK_Hyper_R)
1260  {
1261  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1262  "%s - Key %d (%xH) within 'modifier key' range\n", __FUNCTION__, iKey, iKey);
1263 
1264  return 0; // these must be ignored (basically, it's noise)
1265  }
1266 
1267  // check for ascii, check for lower case
1268  // TODO: correct upper case translation for UTF-8 ?
1269 
1270  if((unsigned int)iKey >= ' ' && (unsigned int)iKey <= 0x7f)
1271  {
1272  register int iKOld = iKey;
1273 
1274  iKey = toupper(iKOld); // should compile efficiently this way
1275 
1276  if(iKOld != iKey)
1277  {
1278  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1279  "%s - Converting Key %d (%xH) to %d (%xH)\n", __FUNCTION__, iKOld, iKOld, iKey, iKey);
1280  }
1281  }
1282 
1283  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1284  "%s - POSSIBLE MENU KEY PRESS for KEY %d (%xH) KEYCODE %d ACS=%xH\n",
1285  __FUNCTION__, iKey, iKey, ((XKeyEvent *)pEvent)->keycode, iACS);
1286 
1287  // there are 3 types of hotkeys:
1288  // first is ALT+'underscore' on a menu name (1st level, likely to have 'POPUP' bit set but not required)
1289  // second is a hotkey defined by an underscore on a member of the popup menu (only when popup is displayed)
1290  // (the second can only happen when a popup is visible, handled by menu_popup)
1291  // third is a popup menu defined by a special key combination (always active)
1292 
1293  iIsPopup = pMenu->iMenuID & WBMENU_POPUP_HIGH_BIT; // popup menus will accept keystrokes without modifiers
1294 
1295 
1296  if(!iIsPopup && !(iACS & WB_KEYEVENT_ALT)) // NOT alt and not a popup (main menu ONLY uses 'alt' keys)
1297  {
1298  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1299  "%s - Key %d (%xH) ACS=%xH not valid for non-popup menu\n", __FUNCTION__, iKey, iKey, iACS);
1300 
1301  // don't exit for cursors or ctrl+
1302  if(!(iACS & WB_KEYEVENT_CTRL) && // ctrl key wasn't pressed
1303  (iKey < 0xfd00 || iKey > 0xffff)) // overly simple test for non-printing characters like cursors and F keys
1304  {
1305  // TODO: for 'dead' keys, braille dots, and a number of other 'mode switch' keys the
1306  // simplified range won't prevent them from going through the rest of this code
1307  // so a slightly more comprehensive test may need to be done for best efficiency
1308 
1309  return 0; // reject something that can't be a hotkey (like JUST pressing an alpha key)
1310  }
1311  }
1312 
1314  // TODO: soft-match of XK_KP_* vs XK_* for numbers, cursors, etc.
1316 
1317  // step 1: check for top-level menu accelerators (underscores on menu names)
1318  // for either the current 'top level' menu, or the visible popup menu
1319 
1320  if(!iIsPopup || // i.e. it's a MAIN menu (so I always do an ALT+'underscore char' hotkey)
1321  !iACS) // no ctrl/alt/shift - i'm typing in stuff after displaying a popup, so does char match underscore char?
1322  {
1323  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1324  "%s - menu has %d items\n", __FUNCTION__, pMenu->nItems);
1325 
1326  for(i1=0; i1 < pMenu->nItems; i1++)
1327  {
1328  pItem = pMenu->ppItems[i1];
1329 
1330  if(pItem->iAction == WBMENU_SEPARATOR) // separator{
1331  {
1332  continue;
1333  }
1334  else if(pItem->iAction & WBMENU_DYNAMIC_HIGH_BIT)
1335  {
1336  // dynamic menu items are created on-the-fly
1337 
1339  // TODO: hotkey search of dynamic menu
1341 
1342  WB_ERROR_PRINT("TODO: %s - dynamic menu hotkey search not implemented\n", __FUNCTION__);
1343 
1344  // it would PROBABLY be a good idea to cache the latest hotkeys, instead of re-building
1345  // the dynamic menu every! stinking! time!, or else have the UI handler do the hotkey search
1346  // (the alternative, and simpler way, would be to NOT allow hotkeys in dynamic menus, but
1347  // force the application to handle them on its own instead... i.e. pass the burden BACK)
1348 
1349  continue;
1350  }
1351  else if(pItem->iUnderscore < 0) // no underscore
1352  {
1353  continue;
1354  }
1355 
1356  if(toupper(pItem->data[pItem->iUnderscore + 1])
1357  == iKey) // it's a match?
1358  {
1359 
1360  __PostMenuActivateEvent(pMenu, pItem, i1, iIsPopup);
1361 
1362  return 1;
1363  }
1364 
1365  // TODO: XK_KP_* matches to XK_* for numbers, etc.
1366  }
1367  }
1368 
1369 
1370  // next search for hotkeys defined as part of the menu by first building
1371  // a matching hotkey designation, then recursively checking popup menus
1372  // for "non-popup" items that match, and checking single layer for a
1373  // matching popup menu item (for existing popup menus only).
1374 
1375  iHotKey0 = iKey;
1376 
1377  if(iACS & WB_KEYEVENT_ALT)
1378  {
1379  iHotKey0 |= ALT_HOTKEY;
1380  }
1381  if(iACS & WB_KEYEVENT_CTRL)
1382  {
1383  if(iHotKey0 > 0 && iHotKey0 < 0x1f) // control keys were translated
1384  {
1385  iHotKey0 += 0x40; // convert back to 'alpha'
1386  }
1387 
1388  iHotKey0 |= CTRL_HOTKEY;
1389  }
1390  if(iACS & WB_KEYEVENT_SHIFT)
1391  {
1392  iHotKey0 |= SHIFT_HOTKEY;
1393  }
1394 
1395  i1 = -1;
1396  pItem = __HotKeySearch(pMenu, iHotKey0, &i1); // recursive hotkey search
1397 
1398  if(!pItem)
1399  {
1400  return 0; // not handled (done this way for optimization)
1401  }
1402 
1403  // at this point the hotkey has been identified, so take action on it
1404  // and then return a '1' value indicating that I 'handled' the notification
1405 
1406  if(pItem->iAction & WBMENU_POPUP_HIGH_BIT)
1407  {
1408  // display popup menu corresponding to hotkey
1409  __PostMenuActivateEvent(pMenu, pItem, i1, iIsPopup);
1410  }
1411  else
1412  {
1413  XClientMessageEvent evt;
1414  Window wIDBar, wIDOwner;
1415  Display *pDisplay;
1416 
1417  if(!iIsPopup)
1418  {
1419  WBMenuBarWindow *pMB = MBFindMenuBarWindow(pMenu);
1420 
1421  if(!pMB)
1422  {
1423  WB_WARN_PRINT("%s - menu bar window NULL, hotkey eaten\n", __FUNCTION__);
1424  return 1; // "handled" though I can't do anything with it
1425  }
1426 
1427  wIDBar = pMB->wSelf;
1428  wIDOwner = pMB->wOwner;
1429  }
1430  else
1431  {
1433 
1434  if(!pMP)
1435  {
1436  WB_WARN_PRINT("%s - menu popup window NULL, hotkey eaten\n", __FUNCTION__);
1437  return 1; // "handled" though I can't do anything with it
1438  }
1439 
1440  wIDBar = pMP->wBar;
1441  wIDOwner = pMP->wOwner;
1442  }
1443 
1444  pDisplay = WBGetWindowDisplay(wIDOwner);
1445 
1446  bzero(&evt, sizeof(evt));
1447  evt.type = ClientMessage;
1448 
1449  evt.display = pDisplay;
1450  evt.window = wIDOwner;
1451  evt.message_type = aMENU_COMMAND;
1452  evt.format = 32; // always
1453  evt.data.l[0] = pItem->iAction; // menu command message ID
1454  evt.data.l[1] = WBCreatePointerHash(pMenu); // hash of pointer to menu object
1455  evt.data.l[2] = wIDBar; // window ID of menu bar
1456 
1457  WBPostEvent(wIDOwner, (XEvent *)&evt);
1458  // since I post the event, the framework should delete the pointer hash (but doesn't).
1459  // So, for now I'll allow it to time out instead. This would rarely be a problem.
1460 
1461  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Menu | DebugSubSystem_Event,
1462  "%s - Post Event: %08xH %08xH %pH %08xH\n", __FUNCTION__,
1463  (int)aMENU_COMMAND, (int)pItem->iAction,
1464  pMenu, (int)wIDBar);
1465  }
1466 
1467  // return "that I handled the thing" to caller
1468 
1469  return 1;
1470 }
1471 
1472 
unsigned int uiTag
a 'tag' identifying this as a WBMenu
Definition: menu.h:187
int iTextWidth
width of menu text (in pixels; assign '-1' to calculate it)
Definition: menu.h:142
structure for managing menu items
Definition: menu.h:127
#define WBMENU_POPUP_MASK
Definition: menu.h:80
int nMaxItems
The maximum number of menu item entries that can be stored in 'ppItems'.
Definition: menu.h:193
WBMenu * MBCopyMenu(const WBMenu *pMenu, int iReserveSpace)
Create a copy of a WBMenu from an existing WBMenu.
Definition: menu.c:350
'window helper' main header file for the X11workbench Toolkit API
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
structure for defining a menu bar window
Definition: menu_bar.h:113
int WBKeyEventProcessKey(const XKeyEvent *pEvent, char *pBuf, int *pcbLen, int *piAltCtrlShift)
Generic keyboard event translation utility.
#define WBMENU_DYNAMIC_HIGH_BIT
Definition: menu.h:79
#define WB_KEYEVENT_ALT
'AltCtrlShift' bit flag for ALT modifier for WBKeyEventProcessKey()
#define WBMENU_SEPARATOR
Definition: menu.h:81
WBMenuItem ** ppItems
An allocated array of menu items.
Definition: menu.h:191
WBMenu * MBFindPopupMenu(WBMenu *pMenu, int idPopup)
Locate a WBMenu 'popup' within a menu created by MBCreateMenu()
Definition: menu.c:326
WBMenuPopupWindow * MBFindMenuPopupWindow(WBMenu *pMenu)
Find the first WBMenuPopupWindow that references a WBMenu.
Definition: menu_popup.c:456
#define WBMENU_RESERVE_DEFAULT
Definition: menu.h:76
structure for managing menu items
Definition: menu.h:185
int nHotKey
hotkey character translation (0 if none)
Definition: menu.h:141
struct tagWBMenu ** ppPopups
An allocated array of 'popup' menus contained by this menu.
Definition: menu.h:195
void MBRemovePopupMenu(WBMenu *pMenu, int idPopup)
Remove a WBMenu 'popup' from a menu created by MBCreateMenu(), freeing up its resources.
Definition: menu.c:339
int WBPostPriorityEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the priority (internal) event queue.
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
WB_UINT32 WBCreatePointerHash(void *pPointer)
Create/obtain a 32-bit 'secure' hash for a pointer.
'configuration helper' main header file for the X11 Work Bench Toolkit API
int nItems
The number of menu item entries in the 'ppItems' array.
Definition: menu.h:192
int MBAddPopupMenu(WBMenu *pMenu, const WBMenu *pPopupMenu)
Add a WBMenuItem menu item to an existing WBMenu.
Definition: menu.c:307
unsigned int uiTag
a 'tag' identifying this as a WBMenuItem
Definition: menu.h:129
Atom aMENU_COMMAND
commands sent by menus via ClientMessage
#define WBMENUITEM_TAG
TAG for WBMenuItem structure.
Definition: menu.h:208
WBMenu * MBCreateMenu(int iID, int iPopup, const char *pszResource, int iReserveSpace)
Create a WBMenu from a text menu resource.
Definition: menu.c:107
int MBAddMenuItem(WBMenu *pMenu, const WBMenuItem *pItem, int iPos)
Add a WBMenuItem menu item to an existing WBMenu.
Definition: menu.c:280
void WBDestroyPointerHashPtr(void *pPointer)
Destroy a 32-bit 'secure' hash for a pointer regardless of reference count.
void * WBAlloc(int nSize)
High performance memory sub-allocator 'allocate'.
int nDataSize
total size of data
Definition: menu.h:145
struct tagWBMenuPopupWindow WBMenuPopupWindow
structure for managing a popup menu window
#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
int iUnderscore
offset of (first) 'underscore' within menu text (-1 if none)
Definition: menu.h:134
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.
#define WB_KEYEVENT_SHIFT
'AltCtrlShift' bit flag for Shift modifier for WBKeyEventProcessKey()
void MBDestroyMenu(WBMenu *pMenu)
Destroy a WBMenu created by MBCreateMenu(), freeing up its resources.
Definition: menu.c:190
int WBAllocUsableSize(void *pBuf)
High performance memory sub-allocator, similar to 'malloc_usable_size'.
Atom aMENU_ACTIVATE
Internal Client Message Atom for 'ACTIVATE' notification.
Definition: menu_bar.c:150
Window wSelf
The window ID of the menu bar window.
Definition: menu_bar.h:116
WBMenuItem * MBCreateMenuItem(const char **ppszResource)
Create a single allocated WBMenuItem structure from a text menu resource, advancing the source text p...
Definition: menu.c:670
int nPopups
The number of popup menu entries in the 'ppPopups' array.
Definition: menu.h:196
Atom WBGetAtom(Display *pDisplay, const char *szAtomName)
Lookup and/or allocate an internal Atom for a named string (lookups include X11 atoms)
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
int nMaxPopups
The maximum number of popup menu entries that can be stored in 'ppPopups'.
Definition: menu.h:197
void MBRemoveMenuItem(WBMenu *pMenu, int iPos)
Remove a WBMenuItem from a menu created by MBCreateMenu(), freeing up its resources.
Definition: menu.c:297
int MBMenuProcessHotKey(WBMenu *pMenu, XKeyEvent *pEvent)
Event handler for menu hotkeys.
Definition: menu.c:1228
char data[4]
data follows
Definition: menu.h:146
Display * WBGetWindowDisplay(Window wID)
returns the Display associated with a window
#define WBMENU_TAG
TAG for WBMenu structure.
Definition: menu.h:204
char * WBGetAtomName(Display *pDisplay, Atom aAtom)
Lookup and/or allocate an internal Atom for a named string.
int iPosition
horizontal/vertical position of menu (in pixels; assign '-1' to calculate it)
Definition: menu.h:143
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
#define WB_KEYEVENT_CTRL
'AltCtrlShift' bit flag for Control modifier for WBKeyEventProcessKey()
void MBDestroyMenuItem(WBMenuItem *pMenuItem)
Destroy a WBMenuItem created by MBCreateMenuItem(), freeing up its resources.
Definition: menu.c:267
Definition file for platform-specific utility functions.
int MBIsMenuItemValid(const WBMenuItem *pMenuItem)
Check whether a 'WBMenuItem' pointer is valid.
Definition: menu.c:244
#define WB_IF_DEBUG_LEVEL(L)
Preferred method of implementing conditional debug 'if block' code.
Definition: debug_helper.h:404
int iHotKey
hotkey description (-1 if none)
Definition: menu.h:136
int iTooltipText
offset in 'data' to null-byte terminated strings (-1 if none)
Definition: menu.h:135
Frame Window API functions and definitions.
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
int MBIsMenuValid(const WBMenu *pMenu)
Check whether a 'WBMenu' pointer is valid.
Definition: menu.c:221