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