X11 Work Bench 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-2016 by Bob Frazier (aka 'Big Bad Bombastic Bob')
16  all rights reserved
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  BSD-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  BSD-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  {
1011  if(pRval->iAction == WBMENU_SEPARATOR)
1012  {
1013  WBDebugPrint(" MENU ITEM SEPARATOR\n");
1014  }
1015  else if(pRval->iAction & WBMENU_DYNAMIC_HIGH_BIT)
1016  {
1018  WBDebugPrint(" DYNAMIC MENU ITEM %d \"%s\"\n",
1019  pRval->iAction & WBMENU_POPUP_MASK, p1);
1020  if(p4)
1021  {
1022  WBFree(p4);
1023  p4 = NULL;
1024  }
1025  }
1026  else if(pRval->iAction & WBMENU_POPUP_HIGH_BIT)
1027  {
1029 
1030  if(pRval->iHotKey == -1)
1031  {
1032  WBDebugPrint(" POPUP MENU \"%s\" %d \"%s\" \"%s\" %-.2s\n",
1033  pRval->data + pRval->iMenuItemText,
1034  pRval->iAction & WBMENU_POPUP_MASK, p4,
1035  pRval->iTooltipText >= 0 ? pRval->data + pRval->iTooltipText : "",
1036  pRval->iUnderscore >= 0 ? pRval->data + pRval->iUnderscore : "");
1037  }
1038  else
1039  {
1040  WBDebugPrint(" POPUP MENU \"%s\" %d \"%s\" \"%s\" \"%s\"(%xH) %-.2s\n",
1041  pRval->data + pRval->iMenuItemText,
1042  pRval->iAction & WBMENU_POPUP_MASK, p4,
1043  pRval->iTooltipText >= 0 ? pRval->data + pRval->iTooltipText : "",
1044  pRval->iHotKey >= 0 ? pRval->data + pRval->iHotKey : "",
1045  pRval->nHotKey,
1046  pRval->iUnderscore >= 0 ? pRval->data + pRval->iUnderscore : "");
1047  }
1048 
1049  if(p4)
1050  {
1051  WBFree(p4);
1052  p4 = NULL;
1053  }
1054  }
1055  else
1056  {
1058 
1059  if(pRval->iHotKey == -1)
1060  {
1061  WBDebugPrint(" MENU ITEM \"%s\" %d \"%s\" \"%s\" %-.2s\n",
1062  pRval->data + pRval->iMenuItemText,
1063  pRval->iAction & WBMENU_POPUP_MASK, p4,
1064  pRval->iTooltipText >= 0 ? pRval->data + pRval->iTooltipText : "",
1065  pRval->iUnderscore >= 0 ? pRval->data + pRval->iUnderscore : "");
1066  }
1067  else
1068  {
1069  WBDebugPrint(" MENU ITEM \"%s\" %d \"%s\" \"%s\" \"%s\"(%xH) %-.2s\n",
1070  pRval->data + pRval->iMenuItemText,
1071  pRval->iAction & WBMENU_POPUP_MASK, p4,
1072  pRval->iTooltipText >= 0 ? pRval->data + pRval->iTooltipText : "",
1073  pRval->iHotKey >= 0 ? pRval->data + pRval->iHotKey : "",
1074  pRval->nHotKey,
1075  pRval->iUnderscore >= 0 ? pRval->data + pRval->iUnderscore : "");
1076  }
1077 
1078  if(p4)
1079  {
1080  WBFree(p4);
1081  p4 = NULL;
1082  }
1083  }
1084  }
1085 #endif // NO_DEBUG
1086 
1087  return pRval;
1088 }
1089 
1090 
1092 // _ _ _ _ ____ _
1093 // | | | | ___ | |_| | _____ _ _ | _ \ _ __ ___ ___ ___ ___ ___(_)_ __ __ _
1094 // | |_| |/ _ \| __| |/ / _ \ | | | | |_) | '__/ _ \ / __/ _ \/ __/ __| | '_ \ / _` |
1095 // | _ | (_) | |_| < __/ |_| | | __/| | | (_) | (_| __/\__ \__ \ | | | | (_| |
1096 // |_| |_|\___/ \__|_|\_\___|\__, | |_| |_| \___/ \___\___||___/___/_|_| |_|\__, |
1097 // |___/ |___/
1099 
1100 // hot key message processing
1101 //include files placed here for possible re-location to different file
1102 #include "menu_bar.h"
1103 #include "menu_popup.h"
1104 
1105 static void __PostMenuActivateEvent(WBMenu *pMenu, WBMenuItem *pItem, int iIndex, int iIsPopup)
1106 {
1107 XClientMessageEvent evt;
1108 
1109  if(!iIsPopup)
1110  {
1111  WBMenuBarWindow *pMB = MBFindMenuBarWindow(pMenu);
1112 
1113  if(pMB)
1114  {
1115  // post a high-priority message to the menu bar or popup window to activate the menu
1116 
1117  bzero(&evt, sizeof(evt));
1118  evt.type = ClientMessage;
1119  evt.display = WBGetWindowDisplay(pMB->wSelf);
1120  evt.window = pMB->wSelf;
1121  evt.message_type = aMENU_ACTIVATE;
1122  evt.format = 32;
1123 #warning this is potentially dangerous code. find another way to pass a pointer, if I even need it
1124  evt.data.l[0] = (long)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 #warning this is potentially dangerous code. find another way to pass a pointer, if I even need it
1156  evt.data.l[0] = (long)pItem;
1157  evt.data.l[1] = iIndex; // index for menu item
1158 
1159  WBPostPriorityEvent(pMP->wSelf, (XEvent *)&evt);
1160 
1161  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1162  "%s - FOUND '%s', posting client event to popup\n",
1163  __FUNCTION__, &(pItem->data[pItem->iMenuItemText]));
1164  }
1165  else
1166  {
1167  // TODO: post to event owner?
1168  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1169  "%s - FOUND '%s', NOT posting client event to popup\n",
1170  __FUNCTION__, &(pItem->data[pItem->iMenuItemText]));
1171  }
1172  }
1173 }
1174 
1175 // pItem = __HotKeySearch(pMenu, iHotkey0, iIsPopup, &i1); // recursive hotkey search
1176 static WBMenuItem * __HotKeySearchRecurse(WBMenu *pMenu, int iHotKey, int *piIndex, int iLevel)
1177 {
1178 WBMenuItem *pRval = NULL;
1179 int i1;
1180 
1181  for(i1=0; i1 < pMenu->nItems; i1++)
1182  {
1183  WBMenuItem *pItem = pMenu->ppItems[i1];
1184 
1185  if(pItem->iHotKey < 0)
1186  {
1187  continue; // no hotkey
1188  }
1189 
1190  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1191  "%s - comparing %x to %x '%s' for %s\n",
1192  __FUNCTION__, iHotKey, pItem->nHotKey,
1193  &(pItem->data[pItem->iHotKey]), &(pItem->data[pItem->iMenuItemText]));
1194 
1195  if(pItem->nHotKey == iHotKey)
1196  {
1197  *piIndex = i1;
1198 
1199  if(!iLevel || !(pItem->iAction & WBMENU_POPUP_HIGH_BIT))
1200  {
1201  return pItem;
1202  }
1203 
1204  return NULL; // popups can't be returned except for level 0
1205  }
1206  }
1207 
1208  // now recurse all of the popups
1209  for(i1=0; i1 < pMenu->nPopups; i1++)
1210  {
1211  pRval = __HotKeySearchRecurse(pMenu->ppPopups[i1], iHotKey, piIndex, iLevel + 1);
1212 
1213  if(pRval || *piIndex >= 0) // piIndex >= 0 if found, but pRval will be NULL if popup
1214  {
1215  return pRval;
1216  }
1217  }
1218 
1219  return NULL; // not found
1220 }
1221 
1222 static WBMenuItem * __HotKeySearch(WBMenu *pMenu, int iHotKey, int *piIndex)
1223 {
1224  *piIndex = -1; // necessary
1225 
1226  return __HotKeySearchRecurse(pMenu, iHotKey, piIndex, 0);
1227 }
1228 
1229 int MBMenuProcessHotKey(WBMenu *pMenu, XKeyEvent *pEvent)
1230 {
1231 int iACS = 0, iKey, i1, iIsPopup, nChar;
1232 char tbuf[64];
1233 int iHotKey0;
1234 WBMenuItem *pItem;
1235 
1236 
1237  if(!MBIsMenuValid(pMenu))
1238  {
1239  WB_ERROR_PRINT("ERROR: %s - invalid menu pointer %p\n", __FUNCTION__, pMenu);
1240  return 0;
1241  }
1242 
1243 
1244  // check for hotkey and process (called by KeyEvent processing for frame, etc.)
1245 
1246  // menus use "key press"
1247  if(pEvent->type != KeyPress /* && pEvent->type != KeyRelease */)
1248  {
1249  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1250  "%s - event not KeyPress\n", __FUNCTION__);
1251  return 0;
1252  }
1253 
1254  nChar = sizeof(tbuf);
1255  iKey = WBKeyEventProcessKey((XKeyEvent *)pEvent, tbuf, &nChar, &iACS);
1256 
1257  // pressing or releasing shift, ctrl, alt, or 'meta' must not
1258  // generate an event. Fortunately these are all within a range
1259 
1260  if(iKey >= XK_Shift_L && iKey <= XK_Hyper_R)
1261  {
1262  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1263  "%s - Key %d (%xH) within 'modifier key' range\n", __FUNCTION__, iKey, iKey);
1264 
1265  return 0; // these must be ignored (basically, it's noise)
1266  }
1267 
1268  // check for ascii, check for lower case
1269  // TODO: correct upper case translation for UTF-8 ?
1270 
1271  if((unsigned int)iKey >= ' ' && (unsigned int)iKey <= 0x7f)
1272  {
1273  register int iKOld = iKey;
1274 
1275  iKey = toupper(iKOld); // should compile efficiently this way
1276 
1277  if(iKOld != iKey)
1278  {
1279  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1280  "%s - Converting Key %d (%xH) to %d (%xH)\n", __FUNCTION__, iKOld, iKOld, iKey, iKey);
1281  }
1282  }
1283 
1284  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1285  "%s - POSSIBLE MENU KEY PRESS for KEY %d (%xH) KEYCODE %d ACS=%xH\n",
1286  __FUNCTION__, iKey, iKey, ((XKeyEvent *)pEvent)->keycode, iACS);
1287 
1288  // there are 3 types of hotkeys:
1289  // first is ALT+'underscore' on a menu name (1st level, likely to have 'POPUP' bit set but not required)
1290  // second is a hotkey defined by an underscore on a member of the popup menu (only when popup is displayed)
1291  // (the second can only happen when a popup is visible, handled by menu_popup)
1292  // third is a popup menu defined by a special key combination (always active)
1293 
1294  iIsPopup = pMenu->iMenuID & WBMENU_POPUP_HIGH_BIT; // popup menus will accept keystrokes without modifiers
1295 
1296 
1297  if(!iIsPopup && !(iACS & WB_KEYEVENT_ALT)) // NOT alt and not a popup (main menu ONLY uses 'alt' keys)
1298  {
1299  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1300  "%s - Key %d (%xH) ACS=%xH not valid for non-popup menu\n", __FUNCTION__, iKey, iKey, iACS);
1301 
1302  // don't exit for cursors or ctrl+
1303  if(!(iACS & WB_KEYEVENT_CTRL) && // ctrl key wasn't pressed
1304  (iKey < 0xfd00 || iKey > 0xffff)) // overly simple test for non-printing characters like cursors and F keys
1305  {
1306  // TODO: for 'dead' keys, braille dots, and a number of other 'mode switch' keys the
1307  // simplified range won't prevent them from going through the rest of this code
1308  // so a slightly more comprehensive test may need to be done for best efficiency
1309 
1310  return 0; // reject something that can't be a hotkey (like JUST pressing an alpha key)
1311  }
1312  }
1313 
1315  // TODO: soft-match of XK_KP_* vs XK_* for numbers, cursors, etc.
1317 
1318  // step 1: check for top-level menu accelerators (underscores on menu names)
1319  // for either the current 'top level' menu, or the visible popup menu
1320 
1321  if(!iIsPopup || // i.e. it's a MAIN menu (so I always do an ALT+'underscore char' hotkey)
1322  !iACS) // no ctrl/alt/shift - i'm typing in stuff after displaying a popup, so does char match underscore char?
1323  {
1324  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Keyboard,
1325  "%s - menu has %d items\n", __FUNCTION__, pMenu->nItems);
1326 
1327  for(i1=0; i1 < pMenu->nItems; i1++)
1328  {
1329  pItem = pMenu->ppItems[i1];
1330 
1331  if(pItem->iAction == WBMENU_SEPARATOR) // separator{
1332  {
1333  continue;
1334  }
1335  else if(pItem->iAction & WBMENU_DYNAMIC_HIGH_BIT)
1336  {
1337  // dynamic menu items are created on-the-fly
1338 
1340  // TODO: hotkey search of dynamic menu
1342 
1343  WB_ERROR_PRINT("TODO: %s - dynamic menu hotkey search not implemented\n", __FUNCTION__);
1344 
1345  // it would PROBABLY be a good idea to cache the latest hotkeys, instead of re-building
1346  // the dynamic menu every! stinking! time!, or else have the UI handler do the hotkey search
1347  // (the alternative, and simpler way, would be to NOT allow hotkeys in dynamic menus, but
1348  // force the application to handle them on its own instead... i.e. pass the burden BACK)
1349 
1350  continue;
1351  }
1352  else if(pItem->iUnderscore < 0) // no underscore
1353  {
1354  continue;
1355  }
1356 
1357  if(toupper(pItem->data[pItem->iUnderscore + 1])
1358  == iKey) // it's a match?
1359  {
1360 
1361  __PostMenuActivateEvent(pMenu, pItem, i1, iIsPopup);
1362 
1363  return 1;
1364  }
1365 
1366  // TODO: XK_KP_* matches to XK_* for numbers, etc.
1367  }
1368  }
1369 
1370 
1371  // next search for hotkeys defined as part of the menu by first building
1372  // a matching hotkey designation, then recursively checking popup menus
1373  // for "non-popup" items that match, and checking single layer for a
1374  // matching popup menu item (for existing popup menus only).
1375 
1376  iHotKey0 = iKey;
1377 
1378  if(iACS & WB_KEYEVENT_ALT)
1379  {
1380  iHotKey0 |= ALT_HOTKEY;
1381  }
1382  if(iACS & WB_KEYEVENT_CTRL)
1383  {
1384  if(iHotKey0 > 0 && iHotKey0 < 0x1f) // control keys were translated
1385  {
1386  iHotKey0 += 0x40; // convert back to 'alpha'
1387  }
1388 
1389  iHotKey0 |= CTRL_HOTKEY;
1390  }
1391  if(iACS & WB_KEYEVENT_SHIFT)
1392  {
1393  iHotKey0 |= SHIFT_HOTKEY;
1394  }
1395 
1396  i1 = -1;
1397  pItem = __HotKeySearch(pMenu, iHotKey0, &i1); // recursive hotkey search
1398 
1399  if(!pItem)
1400  {
1401  return 0; // not handled (done this way for optimization)
1402  }
1403 
1404  // at this point the hotkey has been identified, so take action on it
1405  // and then return a '1' value indicating that I 'handled' the notification
1406 
1407  if(pItem->iAction & WBMENU_POPUP_HIGH_BIT)
1408  {
1409  // display popup menu corresponding to hotkey
1410  __PostMenuActivateEvent(pMenu, pItem, i1, iIsPopup);
1411  }
1412  else
1413  {
1414  XClientMessageEvent evt;
1415  Window wIDBar, wIDOwner;
1416  Display *pDisplay;
1417 
1418  if(!iIsPopup)
1419  {
1420  WBMenuBarWindow *pMB = MBFindMenuBarWindow(pMenu);
1421 
1422  if(!pMB)
1423  {
1424  WB_WARN_PRINT("%s - menu bar window NULL, hotkey eaten\n", __FUNCTION__);
1425  return 1; // "handled" though I can't do anything with it
1426  }
1427 
1428  wIDBar = pMB->wSelf;
1429  wIDOwner = pMB->wOwner;
1430  }
1431  else
1432  {
1434 
1435  if(!pMP)
1436  {
1437  WB_WARN_PRINT("%s - menu popup window NULL, hotkey eaten\n", __FUNCTION__);
1438  return 1; // "handled" though I can't do anything with it
1439  }
1440 
1441  wIDBar = pMP->wBar;
1442  wIDOwner = pMP->wOwner;
1443  }
1444 
1445  pDisplay = WBGetWindowDisplay(wIDOwner);
1446 
1447  bzero(&evt, sizeof(evt));
1448  evt.type = ClientMessage;
1449 
1450  evt.display = pDisplay;
1451  evt.window = wIDOwner;
1452  evt.message_type = aMENU_COMMAND;
1453  evt.format = 32; // always
1454  evt.data.l[0] = pItem->iAction; // menu command message ID
1455  evt.data.l[1] = WBCreatePointerHash(pMenu); // hash of pointer to menu object
1456  evt.data.l[2] = wIDBar; // window ID of menu bar
1457 
1458  WBPostEvent(wIDOwner, (XEvent *)&evt);
1459  // since I post the event, the framework should delete the pointer hash (but doesn't).
1460  // So, for now I'll allow it to time out instead. This would rarely be a problem.
1461 
1462  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Menu | DebugSubSystem_Event,
1463  "%s - Post Event: %08xH %08xH %pH %08xH\n", __FUNCTION__,
1464  (int)aMENU_COMMAND, (int)pItem->iAction,
1465  pMenu, (int)wIDBar);
1466  }
1467 
1468  // return "that I handled the thing" to caller
1469 
1470  return 1;
1471 }
1472 
1473 
#define WBMENU_POPUP_MASK
Definition: menu.h:80
structure for managing menu items
Definition: menu.h:185
WBMenu * MBCopyMenu(const WBMenu *pMenu, int iReserveSpace)
Create a copy of a WBMenu from an existing WBMenu.
Definition: menu.c:350
int iTooltipText
offset in &#39;data&#39; to null-byte terminated strings (-1 if none)
Definition: menu.h:135
&#39;window helper&#39; main header file for the X11workbench Toolkit API
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
static __inline__ unsigned int WBGetDebugLevel(void)
Returns the current debug level assigned by WBSetDebugLevel.
Definition: debug_helper.h:68
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:196
int iAction
Definition: menu.h:139
int WBKeyEventProcessKey(const XKeyEvent *pEvent, char *pBuf, int *pcbLen, int *piAltCtrlShift)
Generic keyboard event translation utility.
int iPosition
horizontal/vertical position of menu (in pixels; assign &#39;-1&#39; to calculate it)
Definition: menu.h:143
int nPopups
The number of popup menu entries in the &#39;ppPopups&#39; array.
Definition: menu.h:196
#define WBMENU_DYNAMIC_HIGH_BIT
Definition: menu.h:79
#define WB_KEYEVENT_ALT
&#39;AltCtrlShift&#39; bit flag for ALT modifier for WBKeyEventProcessKey()
#define WBMENU_SEPARATOR
Definition: menu.h:81
WBMenu * MBFindPopupMenu(WBMenu *pMenu, int idPopup)
Locate a WBMenu &#39;popup&#39; 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:457
#define WBMENU_RESERVE_DEFAULT
Definition: menu.h:76
int nDataSize
total size of data
Definition: menu.h:145
int iHotKey
hotkey description (-1 if none)
Definition: menu.h:136
structure for managing menu items
Definition: menu.h:127
void MBRemovePopupMenu(WBMenu *pMenu, int idPopup)
Remove a WBMenu &#39;popup&#39; from a menu created by MBCreateMenu(), freeing up its resources.
Definition: menu.c:339
structure for managing a popup menu window
Definition: menu_popup.h:108
int WBPostPriorityEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the priority (internal) event queue.
&#39;configuration helper&#39; main header file for the X11 Work Bench Toolkit API
WB_UINT32 WBCreatePointerHash(void *pPointer)
Create/obtain a 32-bit &#39;secure&#39; hash for a pointer.
int MBAddPopupMenu(WBMenu *pMenu, const WBMenu *pPopupMenu)
Add a WBMenuItem menu item to an existing WBMenu.
Definition: menu.c:307
int iMenuID
menu identifier specified when menu was created (high bit set for popup)
Definition: menu.h:189
int nMaxItems
The maximum number of menu item entries that can be stored in &#39;ppItems&#39;.
Definition: menu.h:193
int nMaxPopups
The maximum number of popup menu entries that can be stored in &#39;ppPopups&#39;.
Definition: menu.h:197
int iTextWidth
width of menu text (in pixels; assign &#39;-1&#39; to calculate it)
Definition: menu.h:142
int nItems
The number of menu item entries in the &#39;ppItems&#39; array.
Definition: menu.h:192
WBMenu * MBCreateMenu(int iID, int iPopup, const char *pszResource, int iReserveSpace)
Create a WBMenu from a text menu resource.
Definition: menu.c:107
int MBAddMenuItem(WBMenu *pMenu, const WBMenuItem *pItem, int iPos)
Add a WBMenuItem menu item to an existing WBMenu.
Definition: menu.c:280
void * WBAlloc(int nSize)
High performance memory sub-allocator &#39;allocate&#39;.
structure for defining a menu bar window
Definition: menu_bar.h:113
int iMenuItemText
offset in &#39;data&#39; to null-byte terminated strings (-1 if none)
Definition: menu.h:133
int nHotKey
hotkey character translation (0 if none)
Definition: menu.h:141
unsigned int uiTag
a &#39;tag&#39; identifying this as a WBMenuItem
Definition: menu.h:129
#define WBMENU_POPUP_HIGH_BIT
Definition: menu.h:78
#define WB_ERROR_PRINT(...)
Preferred method of implementing an &#39;error level&#39; debug message for all subsystems.
Definition: debug_helper.h:268
WBMenuBarWindow * MBFindMenuBarWindow(WBMenu *pMenu)
Locate the first WBMenuBarWindow that is using a WBMenu structure.
Definition: menu_bar.c:453
void WBFree(void *pBuf)
High performance memory sub-allocator &#39;free&#39;.
int WBPostEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the regular (internal) event queue.
#define WB_KEYEVENT_SHIFT
&#39;AltCtrlShift&#39; 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
void WBDestroyPointerHashPtr(void *pPointer)
Destroy a 32-bit &#39;secure&#39; hash for a pointer regardless of reference count.
Window wOwner
The window ID of the owning window.
Definition: menu_bar.h:117
int WBAllocUsableSize(void *pBuf)
High performance memory sub-allocator, similar to &#39;malloc_usable_size&#39;.
Atom aMENU_ACTIVATE
Internal Client Message Atom for &#39;ACTIVATE&#39; notification.
Definition: menu_bar.c:150
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
WBMenuItem ** ppItems
An allocated array of menu items.
Definition: menu.h:191
int iUnderscore
offset of (first) &#39;underscore&#39; within menu text (-1 if none)
Definition: menu.h:134
Window wOwner
window ID for the owner window
Definition: menu_popup.h:113
Window wBar
window ID for the associated Menu Bar window
Definition: menu_popup.h:112
void MBRemoveMenuItem(WBMenu *pMenu, int iPos)
Remove a WBMenuItem from a menu created by MBCreateMenu(), freeing up its resources.
Definition: menu.c:297
char data[4]
data follows
Definition: menu.h:146
Window wSelf
window ID for the Menu Popup window
Definition: menu_popup.h:111
Atom aMENU_COMMAND
commands sent by menus via ClientMessage
int MBMenuProcessHotKey(WBMenu *pMenu, XKeyEvent *pEvent)
Event handler for menu hotkeys.
Definition: menu.c:1229
Display * WBGetWindowDisplay(Window wID)
returns the Display associated with a window
unsigned int uiTag
a &#39;tag&#39; identifying this as a WBMenu
Definition: menu.h:187
char * WBGetAtomName(Display *pDisplay, Atom aAtom)
Lookup and/or allocate an internal Atom for a named string.
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
#define WB_KEYEVENT_CTRL
&#39;AltCtrlShift&#39; 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 &#39;WBMenuItem&#39; pointer is valid.
Definition: menu.c:244
Window wSelf
The window ID of the menu bar window.
Definition: menu_bar.h:116
Frame Window API functions and definitions.
Atom WBGetAtom(Display *pDisplay, const char *szAtomName)
Lookup and/or allocate an internal Atom for a named string (lookups include X11 atoms) ...
#define WB_WARN_PRINT(...)
Preferred method of implementing a &#39;warning level&#39; debug message for all subsystems.
Definition: debug_helper.h:261
struct __WBMenu ** ppPopups
An allocated array of &#39;popup&#39; menus contained by this menu.
Definition: menu.h:195
int MBIsMenuValid(const WBMenu *pMenu)
Check whether a &#39;WBMenu&#39; pointer is valid.
Definition: menu.c:221