source: trunk/athena/lib/Xj/Menu.c @ 14944

Revision 14944, 69.7 KB checked in by ghudson, 24 years ago (diff)
Add failsafe to undo menu bar scrolling when the last menu is closed. (Normally we wait until the parent of the responsible menu is closed, but that doesn't work if the scrolling is due to the first menu opened.)
Line 
1/*
2 * $Id: Menu.c,v 1.4 2000-07-17 21:12:02 ghudson Exp $
3 *
4 * Copyright 1990, 1991 by the Massachusetts Institute of Technology.
5 *
6 * For copying and distribution information, please see the file
7 * <mit-copyright.h>.
8 *
9 */
10
11#if  (!defined(lint))  &&  (!defined(SABER))
12static char *rcsid =
13"$Id: Menu.c,v 1.4 2000-07-17 21:12:02 ghudson Exp $";
14#endif
15
16#include "mit-copyright.h"
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <X11/Xos.h>
21#include <ctype.h>
22#include <X11/Xlib.h>
23#include <X11/Xutil.h>
24#include <X11/cursorfont.h>
25#include <errno.h>
26#include <sys/file.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include "Jets.h"
30#include "Window.h"
31#include "Menu.h"
32#include "Button.h"
33#include "warn.h"
34#include <X11/Xatom.h>
35
36#include <sys/time.h>
37
38extern int DEBUG;
39
40/* #define DEBUGPARSING */
41
42/* #define DEBUGMEM */
43
44extern XrmDatabase rdb;
45
46/* oops! broken abstraction! but it's just a string. :-) */
47#define NOMENU "\
48menu deadroot: \"empty file\" {none} [NULL];\
49item quit: \"Quit Dash\" [none] -verify quit();"
50
51/* come to think of it, should this be a class variable? */
52static XContext context;
53static int gotContextType = 0;
54int me_moving = 0;
55
56#define offset(field) XjOffset(MenuJet,field)
57
58static XjResource resources[] = {
59  { XjNfont, XjCFont, XjRFontStruct, sizeof(XFontStruct *),
60     offset(menu.font), XjRString, XjDefaultFont },
61  { XjNx, XjCX, XjRInt, sizeof(int),
62      offset(core.x), XjRString, XjInheritValue },
63  { XjNy, XjCY, XjRInt, sizeof(int),
64      offset(core.y), XjRString, XjInheritValue },
65  { XjNwidth, XjCWidth, XjRInt, sizeof(int),
66      offset(core.width), XjRString, XjInheritValue },
67  { XjNheight, XjCHeight, XjRInt, sizeof(int),
68      offset(core.height), XjRString, XjInheritValue },
69  { XjNhMenuPadding, XjCMenuPadding, XjRInt, sizeof(int),
70      offset(menu.hMenuPadding), XjRString, "10" },
71  { XjNvMenuPadding, XjCMenuPadding, XjRInt, sizeof(int),
72      offset(menu.vMenuPadding), XjRString, "4" },
73  { XjNitems, XjCItems, XjRString, sizeof(char *),
74      offset(menu.items), XjRString, "" },
75  { XjNfile, XjCFile, XjRString, sizeof(char *),
76      offset(menu.file), XjRString, "" },
77  { XjNfallback, XjCFallback, XjRString, sizeof(char *),
78      offset(menu.fallback), XjRString, "" },
79  { XjNuserItems, XjCItems, XjRString, sizeof(char *),
80      offset(menu.userItems), XjRString, "" },
81  { XjNuserFile, XjCFile, XjRString, sizeof(char *),
82      offset(menu.userFile), XjRString, "" },
83  { XjNforeground, XjCForeground, XjRColor, sizeof(int),
84      offset(menu.foreground), XjRString, XjDefaultForeground },
85  { XjNbackground, XjCBackground, XjRColor, sizeof(int),
86      offset(menu.background), XjRString, XjDefaultBackground },
87  { XjNreverseVideo, XjCReverseVideo, XjRBoolean, sizeof(Boolean),
88      offset(menu.reverseVideo), XjRBoolean, (caddr_t)False },
89  { XjNscreenWidth, XjCScreenWidth, XjRBoolean, sizeof(Boolean),
90      offset(menu.screenWidth), XjRBoolean, (caddr_t) False },
91  { XjNhelpPixmap, XjCPixmap, XjRPixmap, sizeof(XjPixmap *),
92      offset(menu.helpPixmap), XjRString, "help.bits" },
93  { XjNsubmenuPixmap, XjCPixmap, XjRPixmap, sizeof(XjPixmap *),
94      offset(menu.submenuPixmap), XjRString, "submenu.bits" },
95  { XjNshowHelp, XjCShowHelp, XjRBoolean, sizeof(Boolean),
96      offset(menu.showHelp), XjRBoolean, (caddr_t)True },
97  { XjNgrey, XjCGrey, XjRPixmap, sizeof(XjPixmap *),
98      offset(menu.grey), XjRString, NULL },
99  { XjNrude, XjCRude, XjRBoolean, sizeof(Boolean),
100      offset(menu.rude), XjRBoolean, (caddr_t)True },
101  { XjNverifyProc, XjCVerifyProc, XjRCallback, sizeof(XjCallback *),
102      offset(menu.verifyProc), XjRString, NULL },
103  { XjNverify, XjCVerify, XjRBoolean, sizeof(Boolean),
104      offset(menu.verify), XjRBoolean, (caddr_t)True },
105  { XjNautoRaise, XjCAutoRaise, XjRBoolean, sizeof(Boolean),
106      offset(menu.autoRaise), XjRBoolean, (caddr_t)False },
107  { XjNmoveMenus, XjCMoveMenus, XjRBoolean, sizeof(Boolean),
108      offset(menu.moveMenus), XjRBoolean, (caddr_t)False },
109  { XjNrightJet, XjCJet, XjRString, sizeof(Jet),
110      offset(menu.rightJet), XjRString, NULL },
111};
112
113#undef offset
114
115#define BORDER 1
116#define SHADOW 1
117#define SAME (Menu *)-1
118
119static void initialize(), realize();
120static Boolean event_handler();
121void computeMenuSize();
122static void closeMenuAndAncestorsToLevel();
123
124MenuClassRec menuClassRec = {
125  {
126    /* class name */            "Menu",
127    /* jet size   */            sizeof(MenuRec),
128    /* classInitialize */       NULL,
129    /* classInitialized? */     1,
130    /* initialize */            initialize,
131    /* prerealize */            NULL,
132    /* realize */               realize,
133    /* event */                 event_handler,
134    /* expose */                NULL,
135    /* querySize */             NULL,
136    /* move */                  NULL,
137    /* resize */                NULL,
138    /* destroy */               NULL,
139    /* resources */             resources,
140    /* number of 'em */         XjNumber(resources)
141  }
142};
143
144JetClass menuJetClass = (JetClass)&menuClassRec;
145
146
147/************************************************************************
148 *
149 *  drawHelp  -- draws text in a help box when it is popped up.
150 *
151 ************************************************************************/
152static void drawHelp(me, menu)
153     MenuJet me;
154     Menu *menu;
155{
156  char *ptr, *end;
157  int y = 0;
158
159  ptr = menu->child->title;
160  while (*ptr != '\0')
161    {
162      end = ptr;
163      while (*end != '\n' && *end != '\0')
164        end++;
165
166      if (end > ptr)
167        XDrawString(me->core.display, menu->menuPane,
168                    me->menu.gc,
169                    me->menu.hMenuPadding / 2,
170                    y + me->menu.vMenuPadding / 2 +
171                        me->menu.font->ascent,
172                    ptr, (int)(end - ptr));
173
174      y += me->menu.font->ascent + me->menu.font->descent;
175
176      if (*end != '\0')
177        ptr = end + 1;
178      else
179        ptr = end;
180    }
181}
182
183
184/************************************************************************
185 *
186 *  computeHelpSize  -- figures out how big a help box is, based on the
187 *      text that it contains.
188 *
189 ************************************************************************/
190static void computeHelpSize(me, menu)
191     MenuJet me;
192     Menu *menu;
193{
194  char *ptr, *end;
195
196  menu->pane_width = 0;
197  menu->pane_height = me->menu.vMenuPadding;
198
199  if (menu->child->title_width != 0)
200    {
201      menu->pane_width += me->menu.hMenuPadding + menu->child->title_width;
202      menu->pane_height += menu->child->title_height;
203    }
204  else
205    {
206      ptr = menu->child->title;
207      while (*ptr != '\0')
208        {
209          end = ptr;
210          while (*end != '\n' && *end != '\0')
211            end++;
212
213          if (end > ptr)
214            menu->pane_width = MAX(menu->pane_width,
215                                   me->menu.hMenuPadding +
216                                   XTextWidth(me->menu.font,
217                                              ptr,
218                                              (int)(end - ptr)));
219
220          menu->pane_height +=  me->menu.font->ascent +
221                                me->menu.font->descent;
222
223          if (*end != '\0')
224            ptr = end + 1;
225          else
226            ptr = end;
227        }
228    }
229
230  menu->child->label_x = menu->child->label_y = BORDER;
231  menu->child->label_width = menu->pane_width;
232  menu->child->label_height = menu->pane_height;
233
234  menu->pane_width += 2 * BORDER + SHADOW;
235  menu->pane_height += 2 * BORDER + SHADOW;
236}
237
238
239/************************************************************************
240 *
241 *  computeMenuSize  -- figures out how big a menu is, based on the
242 *      items that it contains.
243 *
244 ************************************************************************/
245void computeMenuSize(me, menu)
246     MenuJet me;
247     Menu *menu;
248{
249  int x, y;
250  static int height=0;
251  int maxPixmap = 0;
252  Menu *m;
253
254  if (height==0)
255    height = me->menu.font->ascent + me->menu.font->descent
256      + me->menu.vMenuPadding;
257
258  if (menu->parent && menu->parent->paneType == HELP)
259    {
260      computeHelpSize(me, menu->parent);
261      return;
262    }
263
264  if (menu->title_width == 0)
265    {
266      menu->title_width = XTextWidth(me->menu.font,
267                                     menu->title,
268                                     strlen(menu->title));
269    }
270  menu->label_width = me->menu.hMenuPadding + menu->title_width;
271  menu->label_height = height;
272
273
274  x = y = BORDER;
275
276  if (menu->orientation == VERTICAL)
277    x += 10;
278
279  menu->pane_width = 0;
280  menu->pane_height = 0;
281
282  for (m = menu->child; m != NULL; m = m->sibling)
283    {
284      m->label_x = x;
285      m->label_y = y;
286
287      switch(menu->orientation)
288        {
289        case HORIZONTAL:
290          menu->pane_width += m->label_width;
291          if (m->label_height > menu->pane_height)
292            menu->pane_height = m->label_height;
293          x += m->label_width;
294          break;
295
296        case VERTICAL:
297          if (m->child != NULL)
298            {
299              if (m->paneType != HELP)
300                {
301                  int tmp;
302                  tmp = me->menu.submenuPixmap->width + me->menu.hMenuPadding;
303                  if (tmp > maxPixmap)
304                    maxPixmap = tmp;
305
306                  tmp = me->menu.submenuPixmap->height + me->menu.vMenuPadding;
307                  if (tmp > m->label_height)
308                    m->label_height = tmp;
309                }
310              else
311                if (me->menu.showHelp)
312                  {
313                    int tmp;
314                    tmp = me->menu.helpPixmap->width + me->menu.hMenuPadding;
315                    if (tmp > maxPixmap)
316                      maxPixmap = tmp;
317
318                    tmp = me->menu.helpPixmap->height + me->menu.vMenuPadding;
319                    if (tmp > m->label_height)
320                      m->label_height = tmp;
321                  }
322            }
323             
324          if (m->label_width > menu->pane_width)
325            menu->pane_width = m->label_width;
326/*
327          menu->pane_width = MAX(menu->pane_width,
328                                 m->label_width + m->label_x);
329*/
330          menu->pane_height += m->label_height;
331          y += m->label_height;
332
333          break;
334        }
335    }
336
337  if (menu->orientation == VERTICAL)
338    menu->pane_width += x;
339
340  menu->pane_open_x = menu->pane_width + BORDER;
341
342  menu->pane_width += 2 * BORDER + SHADOW + maxPixmap;
343  menu->pane_height += 2 * BORDER + SHADOW;
344}
345
346
347/************************************************************************
348 *
349 *  computeAllMenuSizes  --
350 *      Iteratively traverse the menu tree (bottom up) and
351 *      recompute it. This function is usefully called
352 *      when help mode is toggled.
353 *
354 ************************************************************************/
355void computeAllMenuSizes(me, menu)
356     MenuJet me;
357     Menu *menu;
358{
359  Menu *m;
360
361  struct timeval start, end;
362
363  if (DEBUG)
364    {
365      gettimeofday(&start, NULL);
366      printf("computeAllMenuSizes: - %d.%d + \n", start.tv_sec, start.tv_usec);
367    }
368
369  m = menu;
370
371  while (m != NULL)
372    {
373      while (m->child != NULL)
374        m = m->child;
375
376      computeMenuSize(me, m);
377
378      while (m != NULL && m->sibling == NULL)
379        {
380          m = m->parent;
381          if (m != NULL)
382            computeMenuSize(me, m);
383        }
384
385      if (m != NULL /* && m->sibling != NULL */)
386        m = m->sibling;
387    }     
388  if (DEBUG)
389    {
390      gettimeofday(&end, NULL);
391      printf("computeAllMenuSizes: %d.%d = %d.%06.6d\n",
392             end.tv_sec, end.tv_usec,
393             (end.tv_usec > start.tv_usec)
394             ? end.tv_sec - start.tv_sec
395             : end.tv_sec - start.tv_sec - 1,
396             (end.tv_usec > start.tv_usec)
397             ? end.tv_usec - start.tv_usec
398             : end.tv_usec + 1000000 - start.tv_usec );
399    }
400}
401
402
403/************************************************************************
404 *
405 *  computeRootMenuSize  --  figure out how big the root menu is.
406 *
407 ************************************************************************/
408void computeRootMenuSize(me, size)
409     MenuJet me;
410     XjSize *size;
411{
412  computeMenuSize(me, me->menu.rootMenu);
413
414  size->width  = me->menu.rootMenu->pane_width;
415  size->height = me->menu.rootMenu->pane_height;
416
417  if (me->menu.screenWidth == 1 &&
418      me->menu.rootMenu->orientation == HORIZONTAL)
419    size->width = MAX(DisplayWidth(me->core.display,
420                                   DefaultScreen(me->core.display)),
421                      me->menu.rootMenu->pane_width);
422
423#ifdef notdefined
424  me->core.width = me->menu.rootMenu->pane_width;
425  me->core.height = me->menu.rootMenu->pane_height;
426
427  if (me->menu.screenWidth == 1 &&
428      me->menu.rootMenu->orientation == HORIZONTAL)
429    me->core.width = MAX(DisplayWidth(me->core.display,
430                                      DefaultScreen(me->core.display)),
431                         me->menu.rootMenu->pane_width);
432#endif
433}
434
435
436/************************************************************************
437 *
438 *  stringToQuark  --  quarkifies first string on comma-seperated list
439 *      and returns it, leaving string pointing to next item.
440 *
441 ************************************************************************/
442static XrmQuark stringToQuark(string)
443     char **string;
444{
445  char *ptr, tmp;
446  XrmQuark q;
447
448  ptr = *string;
449  while (isalnum(*ptr)) ptr++;
450  if (ptr == *string)
451    return (XrmQuark) NULL;
452  tmp = *ptr;
453  *ptr = '\0';
454  q = XrmStringToQuark(*string);
455#ifdef DEBUGPARSING
456  fprintf(stdout, "%s ", *string);
457  fflush(stdout);
458#endif
459  *ptr = tmp;
460
461  if (*ptr == ',') ptr++;
462  while (isspace(*ptr)) ptr++;
463  *string = ptr;
464  return q;
465}
466
467
468/************************************************************************
469 *
470 *  skippast  --  skip past all text until first occurrence of 'what',
471 *      except if 'inquotes' is true, then just find the end of the quote.
472 *
473 ************************************************************************/
474static void skippast(ptr, inquotes, what)
475     char **ptr;
476     int inquotes;
477     char what;
478{
479  while (1)
480    {
481      if (inquotes)
482        {
483          while (**ptr != '\0' && **ptr != '"')
484            (*ptr)++;
485          if (**ptr == '\0')
486            break;
487          (*ptr)++;
488          inquotes = 0;
489        }
490      else
491        {
492          while (**ptr != '\0' && **ptr != '"' && **ptr != what)
493            (*ptr)++;
494          if (**ptr == '\0')
495            break;
496          if (**ptr == what)
497            {
498              (*ptr)++;
499              break;
500            }
501          (*ptr)++;
502          inquotes = 1;
503        }
504    }
505}
506
507
508/************************************************************************
509 *
510 *  countMenuEntries  --  count the number of menu entries in a string.
511 *
512 ************************************************************************/
513static int countMenuEntries(me, string)
514     MenuJet me;
515     char *string;
516{
517  int count = 0;
518
519  struct timeval start, end;
520
521  if (DEBUG)
522    {
523      gettimeofday(&start, NULL);
524      printf("countMenuEntries: - %d.%d + \n", start.tv_sec, start.tv_usec);
525    }
526
527  while (*string != '\0')
528    {
529      if ((!strncmp(string, "item", 4)) ||
530          (!strncmp(string, "menu", 4)) ||
531          (!strncmp(string, "title", 5)) ||
532          (!strncmp(string, "separator", 9)))
533        count++;
534      skippast(&string, False, ';');
535      while (isspace(*string)) string++;
536    }
537
538  if (DEBUG)
539    {
540      gettimeofday(&end, NULL);
541      printf("countMenuEntries: %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec,
542             (end.tv_usec > start.tv_usec)
543             ? end.tv_sec - start.tv_sec
544             : end.tv_sec - start.tv_sec - 1,
545             (end.tv_usec > start.tv_usec)
546             ? end.tv_usec - start.tv_usec
547             : end.tv_usec + 1000000 - start.tv_usec );
548    }
549  return count;
550}
551
552
553
554/************************************************************************
555 *
556 *  parseMenuEntry  --  parse entry in 'string' and shove it into 'info'.
557 *
558 ************************************************************************/
559/*
560 * Some of this code is *sooooo* gross
561 */
562static Boolean parseMenuEntry(me, string, info)
563     MenuJet me;
564     char **string;
565     Item *info;
566{
567  int qnum, done, got_one;
568  int inquotes = 0;
569  char *ptr, *end, *beginning, *hosttype;
570  Item *lookup;
571  Boolean unfinished = True;
572  char errtext[100];
573
574  beginning = ptr = *string;    /* remember beginning for better diagnostics */
575
576  /*
577   * Zeroes flags for us, too.
578   */
579  memset(info, 0, sizeof(Item));
580
581  /*
582   * Parse menu/item field
583   */
584  if (*ptr == '!')
585    goto skipit;
586
587  if (!strncmp(ptr, "item", 4))
588    {
589      info->type = ItemITEM;
590      info->u.i.supported = 1;
591      ptr += 5;
592    }
593  else if (!strncmp(ptr, "menu", 4))
594    {
595      info->type = MenuITEM;
596      ptr += 5;
597    }
598  else if (!strncmp(ptr, "help", 4))
599    {
600      info->type = HelpITEM;
601      ptr += 5;
602    }
603  else if (!strncmp(ptr, "title", 5))
604    {
605      info->type = TitleITEM;
606      ptr += 6;
607    }
608  else if (!strncmp(ptr, "separator", 9))
609    {
610      info->type = SeparatorITEM;
611      ptr += 10;
612    }
613  else
614    {
615      sprintf(errtext,
616              "menu line -\n %.70s\n- %s%s",
617              beginning,
618              "does not begin with menu, item, title, separator, ",
619              "or help; ignoring");
620      XjWarning(errtext);
621      goto youlose;
622    }
623
624  while (isspace(*ptr)) ptr++;
625
626  /*
627   * Parse label field
628   */
629  if (0 == (end = strchr(ptr, ':')))
630    {
631      sprintf(errtext,
632              "menu line -\n %.70s\n- %s",
633              beginning,
634              "does not contain ':' terminated label; ignoring");
635      XjWarning(errtext);
636      goto youlose;
637    }
638  *end = '\0';
639  info->name = XrmStringToQuark(ptr);
640  *end = ':';
641  ptr = end + 1;
642
643  /* The rest may come in any order */
644  while (unfinished)
645    {
646      while (isspace(*ptr)) ptr++;
647      switch(*ptr)
648        {
649 /*
650  *  Parse hosttype or menu orientation
651  */
652        case '+': /* should do this right someday... */
653        case '-':
654          if (info->type == ItemITEM)
655            {
656              hosttype = getenv("HOSTTYPE");
657              if (!hosttype)
658                  hosttype = HOSTTYPE;
659              if (strncasecmp(ptr + 1, hosttype, strlen(hosttype)) == 0)
660                info->u.i.supported = (*ptr == '+');
661
662              if (strncasecmp(ptr + 1, "verify", 6) == 0)
663                {
664                  info->flags |= verifyFLAG;
665                  info->u.i.verify = (*ptr == '+');
666                }
667
668              ptr++;
669              while (*ptr && !isspace(*ptr))
670                ptr++;
671              break;
672            }
673          /*
674           * Menu orientation flags
675           */
676          if ((info->type == MenuITEM) &&
677              (*(ptr + 1) == 'h'))
678            {
679              info->u.m.orientation = HORIZONTAL;
680              info->flags |= orientationFLAG;
681              ptr += 2;
682              break;
683            }
684
685          if ((info->type == MenuITEM) &&
686              (*(ptr + 1) == 'v'))
687            {
688              info->u.m.orientation = VERTICAL;
689              info->flags |= orientationFLAG;
690              ptr += 2;
691              break;
692            }
693
694          if (DEBUG)
695            {
696              sprintf(errtext, "'%s' contains unknown flag; ignored",
697                      XrmQuarkToString(info->name));
698              XjWarning(errtext);
699            }
700          while (*ptr != '\0' &&
701                 !isspace(*ptr) &&
702                 *ptr != ';') ptr++;
703          break;
704
705 /*
706  *  Parse title field
707  */
708        case '"':
709          ptr++;
710
711                                /* Deal with star BS... */
712          if (ptr[0] == ' '  &&  ptr[1] == ' '  &&  ptr[2] == ' ')
713            ptr += 3;
714          if (ptr[0] == '*'  &&  ptr[1] == ' ')
715            ptr += 2;
716                                /* Rip this code out in a future */
717                                /* release... */
718
719          inquotes = 1;
720          if (0 == (end = strchr(ptr, '"')))
721            {
722              if (info->type == HelpITEM)
723                {
724                  sprintf(errtext,
725                          "'%s' help does not have closing quote; ignoring",
726                          XrmQuarkToString(info->name));
727                  XjWarning(errtext);
728                }
729              else
730                {
731                  sprintf(errtext,
732       "'%s' menu definition title does not have closing quote; ignoring",
733                          XrmQuarkToString(info->name));
734                  XjWarning(errtext);
735                }
736              goto youlose;
737            }
738          *end = '\0';
739          info->title = ptr;
740          ptr = end + 1;
741          inquotes = 0;
742          info->flags |= titleFLAG;
743
744          if (info->type == HelpITEM)
745            {
746              /*
747               * This help is to be appended to an
748               * existing menu item
749               */
750              lookup = (Item *)hash_lookup(me->menu.Names, info->name);
751              if (lookup == NULL)
752                {
753                  sprintf(errtext,
754                          "'%s' help entry references nonexistent item",
755                          XrmQuarkToString(info->name));
756                  XjWarning(errtext);
757                  goto youlose;
758                }
759              if (lookup->type == MenuITEM)
760                {
761                  sprintf(errtext,
762                          "'%s' help entry cannot be added to a menu",
763                          XrmQuarkToString(info->name));
764                  XjWarning(errtext);
765                  goto youlose;
766                }
767              lookup->u.i.help = info->title;
768              goto skipit;
769            }
770          break;
771
772 /*
773  *  Parse (possibly) child field
774  */
775        case '{':
776          ptr++;
777          if (info->type == MenuITEM)
778            {
779              qnum = 0; done = 0;
780              while ((qnum < MAXCHILDREN) && !done)
781                {
782                  info->u.m.children[qnum++] = stringToQuark(&ptr);
783                  if (info->u.m.children[qnum - 1] == (XrmQuark) NULL)
784                    done = 1;
785                }
786              if (!done)
787                {
788                  if ((XrmQuark) NULL != stringToQuark(&ptr))
789                    {
790                      while (stringToQuark(&ptr) != (XrmQuark) NULL);
791                      sprintf(errtext,
792                              "'%s' - more than %d child types; truncated",
793                              XrmQuarkToString(info->name), MAXCHILDREN);
794                      XjWarning(errtext);
795                    }
796                }
797              if (info->u.m.children[0] != (XrmQuark) NULL)
798                info->flags |= childrenFLAG;
799            }
800          else /* type != MenuITEM */
801            {
802              sprintf(errtext, "'%s' non-menu specifies children",
803                      XrmQuarkToString(info->name));
804              XjWarning(errtext);
805            }
806
807          if (0 == (end = strchr(ptr, '}')))
808            {
809              sprintf(errtext,
810                  "%s: child specifier does not have '}'; trying to be smart",
811                      XrmQuarkToString(info->name));
812            }
813          else
814            ptr = end + 1;
815          break;
816
817 /*
818  *  Parse parent field
819  *  qnum always points to an empty slot...
820  */
821        case '[':
822          qnum = 0; done = 0; got_one = 0;
823          while (!done)
824            {
825              if (*ptr != '[')
826                {
827                  done = 1;
828                  if (!got_one)
829                    {
830                      sprintf(errtext,
831                              "'%s' missing '[' in menu definition; ignoring",
832                              XrmQuarkToString(info->name));
833                      XjWarning(errtext);
834                      goto youlose;
835                    }
836                  else
837                    info->weight[qnum - 1] = 0; /* true termination */
838                }
839              else
840                {
841                  ptr++;
842
843                  while ((qnum < MAXPARENTS) && !done)
844                    {
845                      info->parents[qnum++] = stringToQuark(&ptr);
846                      if (info->parents[qnum - 1] == (XrmQuark) NULL)
847                        done = 1;
848                      else
849                        {
850                          /* if weight not specified, use 0 */
851                          if (*ptr == '/')
852                            {
853                              ptr++;
854                              info->weight[qnum - 1] = atoi(ptr);
855                              while (isdigit(*ptr) || *ptr == '-')
856                                ptr++;
857                              while (isspace(*ptr)) ptr++;
858                            }
859                          else
860                            info->weight[qnum - 1] = 0;
861
862                          if (*ptr == ',') ptr++;
863                        }
864                    }
865
866                  if (!done) /* out of space... */
867                    {
868                      if (isalnum(*ptr)) /* we weren't done yet... */
869                        {
870                          sprintf(errtext,
871                                  "'%s' more than %d parent types; truncated",
872                                  XrmQuarkToString(info->name), MAXCHILDREN);
873                          XjWarning(errtext);
874                          if (0 == (end = strchr(ptr, ']')))
875                            {
876                              XjWarning(
877                              "and mismatched brackets, too! ignoring");
878                              goto youlose;
879                            }
880                          done = 1;
881
882                          if (0 != (end = strchr(ptr, ';')))
883                            ptr = end + 1;
884                          else
885                            {
886                              sprintf(errtext,
887                                      "'%s' missing semicolon; ignoring",
888                                      XrmQuarkToString(info->name));
889                              XjWarning(errtext);
890                              goto youlose;
891                            }
892                        }
893                    }
894                  else /* we finished the group; do next */
895                    {
896                      done = 0; got_one = 1;
897                      info->parents[qnum - 1] = (XrmQuark) NULL;
898                      info->weight[qnum - 1] = 1; /* short termination */
899                      if (*ptr == ']') ptr++;
900                      else
901                        {
902                          sprintf(errtext, "'%s' missing ']'; ignoring",
903                                  XrmQuarkToString(info->name));
904                          XjWarning(errtext);
905                          goto youlose;
906                        }
907                    }
908                }
909            }
910
911/*
912          if (info->parents[0] != NULL)
913*/
914            info->flags |= parentsFLAG;
915          break;
916
917 /*
918  *  End of line?
919  */
920        case ';':
921          ptr++;
922          unfinished = False;
923          break;
924
925 /*
926  *  Default case
927  */
928        default:
929          if (info->type == ItemITEM)
930            {
931              if (NULL ==
932                  (info->u.i.activateProc = XjConvertStringToCallback(&ptr)))
933                {
934                  sprintf(errtext,
935                          "'%s' - couldn't grok callback; ignoring entry",
936                          XrmQuarkToString(info->name));
937                  XjWarning(errtext);
938                  goto youlose;
939                }
940
941              *(ptr-1) = '\0';
942              info->flags |= activateFLAG;
943            }
944          else
945            {
946              sprintf(errtext, "'%s' - garbage at end of line; ignoring entry",
947                      XrmQuarkToString(info->name));
948              XjWarning(errtext);
949              goto youlose;
950            }
951          break;
952        } /* switch */
953    } /* while */
954
955  while (isspace(*ptr)) ptr++;
956  *string = ptr;
957
958/*
959  fprintf(stdout, "%s: ", XrmQuarkToString(info->name));
960
961  if (info->flags & titleFLAG)
962    fprintf(stdout, "title ");
963
964  if (info->flags & activateFLAG)
965    fprintf(stdout, "callback ");
966
967  if (info->flags & orientationFLAG)
968    fprintf(stdout, "orientation ");
969
970  if (info->flags & parentsFLAG)
971    fprintf(stdout, "parents ");
972
973  if (info->flags & childrenFLAG)
974    fprintf(stdout, "children");
975
976  fprintf(stdout, "\n");
977*/
978
979  return True;
980
981 youlose:
982 skipit:
983  skippast(&ptr, inquotes, ';');
984  while (isspace(*ptr)) ptr++;
985  *string = ptr;
986  return False;
987}
988
989
990
991/************************************************************************
992 *
993 *  addMenuEntry  --  add a menu entry to the menu struct.
994 *
995 ************************************************************************/
996static Boolean addMenuEntry(me, info, i)
997     MenuJet me;
998     Item *info, *i; /* location to put it if new */
999{
1000  Item *j;
1001  TypeDef *t;
1002  int m, n;
1003
1004  j = (Item *)hash_lookup(me->menu.Names, info->name);
1005  if (j != NULL)
1006    i = j;
1007
1008  if (j != NULL &&
1009      j->type != info->type)
1010    {
1011      char errtext[100];
1012
1013      sprintf(errtext, "'%s' - type %s can't override type %s",
1014              XrmQuarkToString(info->name),
1015              &"item\000menu"[info->type*5],
1016              &"item\000menu"[j->type*5]);
1017      XjWarning(errtext);
1018      return False;
1019    }
1020
1021  /*
1022   * Register the item's name and zero its structure if not an override
1023   */
1024  if (j == NULL)
1025    {
1026      (void)hash_store(me->menu.Names, info->name, i);
1027      memset(i, 0, sizeof(Item)); /* if new, init to zeroes */
1028      i->u.i.supported = 1;
1029      i->u.i.verify = True;
1030    }
1031
1032  /*
1033   * Put into list if not override...
1034   */
1035  if (j == NULL)
1036    switch(info->type)
1037      {
1038      case ItemITEM:
1039        i->next = me->menu.firstItem;
1040        me->menu.firstItem = i;
1041        break;
1042      case MenuITEM:
1043        i->next = me->menu.firstMenu;
1044        me->menu.firstMenu = i;
1045        break;
1046      }
1047
1048  /*
1049   * Now merge in information
1050   */
1051  i->name = info->name;
1052  i->type = info->type;
1053  i->flags |= info->flags; /* hey, what the heck! */
1054
1055  if (info->flags & titleFLAG)
1056    i->title = info->title;
1057
1058  if (info->flags & activateFLAG)
1059    i->u.i.activateProc = info->u.i.activateProc;
1060
1061  if (info->flags & orientationFLAG)
1062    i->u.m.orientation = info->u.m.orientation;
1063
1064  if (info->flags & verifyFLAG)
1065    i->u.i.verify = info->u.i.verify;
1066
1067  i->u.i.supported = i->u.i.supported && info->u.i.supported;
1068
1069  if (info->flags & parentsFLAG)
1070    {
1071      memcpy(i->parents, info->parents, MAXPARENTS * sizeof(XrmQuark));
1072      memcpy(i->weight, info->weight, MAXPARENTS * sizeof(int));
1073    }
1074
1075  if (info->flags & childrenFLAG)
1076    {
1077      /*
1078       * First, remove all the old ones. Then add all the new ones.
1079       * There may be wasted work here, but at least the computer
1080       * will be doing it and not me.
1081       */
1082      for (n = 0; i->u.m.children[n] != 0 && n < MAXCHILDREN; n++)
1083        {
1084          t = (TypeDef *)hash_lookup(me->menu.Types, i->u.m.children[n]);
1085          if (t != NULL) /* better not be NULL! */
1086            {
1087              for (m = 0;
1088                   m < MAXMENUSPERTYPE &&
1089                     t->menus[m] != 0 && /* shouldn't happen */
1090                     t->menus[m] != i;
1091                   m++);
1092              if (m < MAXMENUSPERTYPE && t->menus[m] == i)
1093                {
1094                  for (; m < MAXMENUSPERTYPE - 1; m++)
1095                    t->menus[m] = t->menus[m + 1];
1096                  t->menus[m] = 0;
1097                }
1098            }
1099        }
1100
1101      memcpy(i->u.m.children,
1102            info->u.m.children,
1103            MAXCHILDREN * sizeof(XrmQuark));
1104
1105      /*
1106       * Add the new ones...
1107       */
1108      for (n = 0; i->u.m.children[n] != 0 && n < MAXCHILDREN; n++)
1109        {
1110          t = (TypeDef *)hash_lookup(me->menu.Types, i->u.m.children[n]);
1111          if (t == NULL)
1112            {
1113              t = (TypeDef *)XjMalloc(sizeof(TypeDef));
1114              memset(t, 0, sizeof(TypeDef));
1115              (void)hash_store(me->menu.Types, i->u.m.children[n], t);
1116              t->type = i->u.m.children[n];
1117              t->menus[0] = i;
1118            }
1119          else
1120            {
1121              for (m = 0; m < MAXMENUSPERTYPE && t->menus[m] != 0; m++);
1122              if (m == MAXMENUSPERTYPE)
1123                {
1124                  char errtext[100];
1125
1126                  sprintf(errtext, "'%s' not typed as %s due to overflow",
1127                          XrmQuarkToString(i->name),
1128                          XrmQuarkToString(t->type));
1129                  XjWarning(errtext);
1130                }
1131              else
1132                t->menus[m] = i;
1133            }
1134        }
1135    }
1136
1137  if (j == NULL)
1138    return True;
1139  else
1140    return False;
1141}
1142
1143
1144
1145#ifdef notdefined
1146/************************************************************************
1147 *
1148 *  printTable  --  prints out the menu table - for debugging...
1149 *
1150 ************************************************************************/
1151static void printTable(t)
1152     Item *t;
1153{
1154  int i, done;
1155
1156  while (t != NULL)
1157    {
1158      fprintf(stdout, "%d %s %d %d %d %s\n",
1159              t->type,
1160              XrmQuarkToString(t->name),
1161              t->flags,
1162              (t->type == MenuITEM) ? t->u.m.orientation : t->u.i.supported,
1163              (t->type == ItemITEM) ? t->u.i.verify : 0,
1164              t->title);
1165
1166      if (t->type == MenuITEM)
1167        {
1168          for (i = 0; i < MAXCHILDREN && t->u.m.children[i] != 0; i++)
1169            {
1170              fprintf(stdout, "%s", XrmQuarkToString(t->u.m.children[i]));
1171              if ((i < MAXCHILDREN - 1) && t->u.m.children[i+1] != 0)
1172                fprintf(stdout, " ");
1173            }
1174          fprintf(stdout, "\n");
1175        }
1176
1177
1178      done = 0; i = 0;
1179      while (!done)
1180        {
1181          if (t->parents[i] == 0)
1182            done = 1;
1183          while (!done)
1184            {
1185              if (t->parents[i] != 0)
1186                {
1187                  fprintf(stdout, "%s", XrmQuarkToString(t->parents[i]));
1188                  fprintf(stdout, " %d", t->weight[i]);
1189                  if ((i < MAXPARENTS - 1) && t->parents[i+1] != 0)
1190                    fprintf(stdout, " ");
1191                  else
1192                    {
1193                      done = 1;
1194                      fprintf(stdout, "\n");
1195                    }
1196                  i++;
1197                }
1198            }
1199          if ((i < MAXPARENTS - 1) &&
1200              t->weight[i] == 0)
1201            done = 1;
1202          else
1203            {
1204              i++;
1205              done = 0;
1206            }
1207        }
1208      fprintf(stdout, "!\n");
1209
1210
1211      if (t->type == ItemITEM)
1212        {
1213          printf("%s\n", "activateStringHere");
1214          printf("%s\n",t->u.i.help);
1215          printf("!\n");
1216        }
1217
1218
1219      t = t->next;
1220    }
1221}
1222#endif
1223
1224
1225/************************************************************************
1226 *
1227 *  createTablesFromString  --  create menu tables from a menu string.
1228 *
1229 ************************************************************************/
1230static void createTablesFromString(me, string)
1231     MenuJet me;
1232     char *string;
1233{
1234  int counter = 0;
1235  char *ptr;
1236  Item info, *location;
1237  int num;
1238
1239  struct timeval start, end;
1240
1241  if (DEBUG)
1242    {
1243      gettimeofday(&start, NULL);
1244      printf("createTablesFromString: - %d.%d + \n", start.tv_sec, start.tv_usec);
1245    }
1246
1247  ptr = string;
1248  num = countMenuEntries(me, ptr);
1249  location = (Item *)XjMalloc(num * sizeof(Item));
1250
1251  while (*ptr != '\0')
1252    {
1253      if (parseMenuEntry(me, &ptr, &info))
1254        {
1255          if (addMenuEntry(me, &info, location))
1256            location++;
1257          counter++;
1258        }
1259    }
1260
1261  if (DEBUG)
1262    {
1263      gettimeofday(&end, NULL);
1264      printf("createTablesFromString: %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec,
1265             (end.tv_usec > start.tv_usec)
1266             ? end.tv_sec - start.tv_sec
1267             : end.tv_sec - start.tv_sec - 1,
1268             (end.tv_usec > start.tv_usec)
1269             ? end.tv_usec - start.tv_usec
1270             : end.tv_usec + 1000000 - start.tv_usec );
1271    }
1272}
1273
1274
1275
1276/************************************************************************
1277 *
1278 *  createItem  --  stick an item into the menu struct...
1279 *
1280 ************************************************************************/
1281static Boolean createItem(me, what)
1282     MenuJet me;
1283     Item *what;
1284{
1285  Menu *menuParent = NULL, *item, *here, **hereptr;
1286  Item *parent;
1287  TypeDef *list;
1288  int created = 0, qnum = 0, pnum;
1289
1290  while (1) /* this loops through the list of possible parents */
1291    {
1292      /* skip past any breaks... */
1293      if (what->parents[qnum] == 0)
1294        {
1295          if (what->weight[qnum] == 0) /* end of possibilities... we're done */
1296            break;
1297          qnum++; /* soft break... possibly create more */
1298          if (qnum >= MAXPARENTS)
1299            break;
1300        }
1301
1302      /* see if there are any possible parents of this type */
1303      list = (TypeDef *)hash_lookup(me->menu.Types, what->parents[qnum]);
1304      if (list == NULL &&
1305          (0 != strcmp("NULL", XrmQuarkToString(what->parents[qnum]))))
1306        qnum++; /* type is not NULL and is otherwise undefined */
1307      else
1308        { /* there are possible parents */
1309          if (list != NULL)
1310            {
1311              pnum = 0;
1312              while (1) /* on exit from this loop, menuParent points to */
1313                {       /* parent, NULL if none */
1314                  if ((parent = list->menus[pnum]) == 0)
1315                    break; /* couldn't create *any* menus of this type */
1316
1317                  if ((menuParent = parent->u.m.m) != NULL)
1318                    break;
1319                  else
1320                    { /* this menu doesn't exist... try to create it */
1321                      if (createItem(me, parent))
1322                        { /* yay! we created our parent! */
1323                          menuParent = parent->u.m.m;
1324                          break;
1325                        }
1326                    }
1327                  pnum++;
1328                  if (pnum >= MAXMENUSPERTYPE)
1329                    break;
1330                }
1331            }
1332
1333          if (menuParent == NULL && list != NULL)
1334            qnum++; /* no possible parent of this type */
1335          else /* menuParent == NULL && list == NULL */
1336            {
1337              /*
1338               * We win! Create this item!
1339               */
1340              created++;
1341              /* fprintf(stdout, "Created %s\n", what->title); */
1342              item = (Menu *)XjMalloc(sizeof(Menu));
1343              if (what->type == MenuITEM)
1344                what->u.m.m = item;
1345              memset(item, 0, sizeof(Menu));
1346
1347              item->parent = menuParent;
1348              if (menuParent == NULL)
1349                me->menu.rootMenu = item;
1350              else
1351                {
1352                  here = menuParent->child;
1353                  hereptr = &(menuParent->child);
1354
1355                  while (here != NULL &&
1356                         ((what->weight[qnum] > here->weight) ||
1357                          ((what->weight[qnum] == here->weight) &&
1358                           strcasecmp(what->title, here->title) > 0)))
1359                    {
1360                      hereptr = &(here->sibling);
1361                      here = here->sibling;
1362                    }
1363
1364                  item->sibling = here;
1365                  *hereptr = item;
1366                }
1367              item->state = CLOSED;
1368              item->paneType = NORMAL;
1369              item->title = what->title;
1370              item->title_width = what->title_width;
1371              item->weight = what->weight[qnum];
1372
1373              if (what->type == MenuITEM)
1374                {
1375                  item->orientation = what->u.m.orientation;
1376                  item->child = NULL;
1377                  item->supported = 1;
1378                }
1379              else
1380                {
1381                  item->activateProc = what->u.i.activateProc;
1382                  item->supported = what->u.i.supported;
1383                  item->verify = what->u.i.verify;
1384
1385                  if (NULL != what->u.i.help)
1386                    {
1387                      item->paneType = HELP;
1388                      item->child = (Menu *)XjMalloc(sizeof(Menu));
1389                      memset(item->child, 0, sizeof(Menu));
1390                      item->child->title = what->u.i.help;
1391                      item->child->title_width = what->u.i.help_width;
1392                      item->child->title_height = what->u.i.help_height;
1393                      item->child->parent = item;
1394                      item->child->state = CLOSED;
1395                      item->child->activateProc = item->activateProc;
1396                    }
1397                }
1398              if (what->type == MenuITEM)
1399                return True; /* can currently create only one instance of
1400                                a menu... just break here? */
1401              while (qnum < MAXPARENTS && what->parents[qnum] != 0)
1402                qnum++; /* skip past other same-set candidates */
1403            }
1404          if (qnum >= MAXPARENTS)
1405            break;
1406        }
1407    }
1408
1409  if (created != 0)
1410    return True;
1411  else
1412    return False;
1413}
1414
1415
1416
1417/************************************************************************
1418 *
1419 *  createMenuFromTables  --  build the menu struct.
1420 *
1421 ************************************************************************/
1422static void createMenuFromTables(me)
1423     MenuJet me;
1424{
1425  Item *i;
1426
1427  struct timeval start, end;
1428
1429  if (DEBUG)
1430    {
1431      gettimeofday(&start, NULL);
1432      printf("createMenuFromTables: - %d.%d + \n", start.tv_sec, start.tv_usec);
1433    }
1434
1435  for (i = me->menu.firstItem; i != NULL; i = i->next)
1436    (void)createItem(me, i);
1437
1438  if (DEBUG)
1439    {
1440      gettimeofday(&end, NULL);
1441      printf("createMenuFromTables: %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec,
1442             (end.tv_usec > start.tv_usec)
1443             ? end.tv_sec - start.tv_sec
1444             : end.tv_sec - start.tv_sec - 1,
1445             (end.tv_usec > start.tv_usec)
1446             ? end.tv_usec - start.tv_usec
1447             : end.tv_usec + 1000000 - start.tv_usec );
1448    }
1449}
1450
1451
1452/************************************************************************
1453 *
1454 *  loadFile  --  read in a file and return it as one big ol' string.
1455 *
1456 ************************************************************************/
1457static char *loadFile(filename)
1458     char *filename;
1459{
1460    int fd, size;
1461    struct stat buf;
1462    char *info;
1463    char errtext[100];
1464
1465    if (-1 == (fd = open(filename, O_RDONLY, 0)))
1466      {
1467        sprintf(errtext, "opening '%s': %s", filename, strerror(errno));
1468
1469        XjWarning(errtext);
1470        return NULL;
1471      }
1472
1473    if (-1 == fstat(fd, &buf)) /* could only return EIO */
1474      {
1475        sprintf(errtext, "fstat '%s': %s", filename, strerror(errno));
1476
1477        XjWarning(errtext);
1478        close(fd);
1479        return NULL;
1480      }
1481
1482    size = (int)buf.st_size;
1483
1484    info = (char *)XjMalloc(size + 1);  /* one extra for the NULL */
1485
1486    if (-1 == read(fd, info, size))
1487      {
1488        sprintf(errtext, "reading '%s': %s", filename, strerror(errno));
1489
1490        XjWarning(errtext);
1491        close(fd);
1492        XjFree(info);
1493        return NULL;
1494      }
1495    close(fd);
1496
1497    info[size] = '\0';
1498    return info;
1499}
1500
1501
1502/************************************************************************
1503 *
1504 *  destroyChildren  --  mass murder...
1505 *
1506 ************************************************************************/
1507static void destroyChildren(reap)
1508     Menu *reap;
1509{
1510  Menu *anal;
1511
1512  while (reap != NULL)
1513    {
1514      if (reap->child != NULL)
1515        destroyChildren(reap->child);
1516      anal = reap;
1517      reap = reap->sibling;
1518      XjFree(anal);
1519    }
1520}
1521   
1522
1523/************************************************************************
1524 *
1525 *  destroyMenu  --  destroy yourself!  and your little dog too!
1526 *
1527 ************************************************************************/
1528static void destroyMenu(me)
1529     MenuJet me;
1530{
1531  destroyChildren(me->menu.rootMenu);
1532  XDeleteContext(me->core.display, me->menu.rootMenu->menuPane, context);
1533  XjFree(me->menu.rootMenu);
1534  me->menu.rootMenu = NULL;
1535}
1536
1537
1538
1539/************************************************************************
1540 *
1541 *  loadNewMenus  --  load some new menus into the menu struct.
1542 *
1543 ************************************************************************/
1544/*
1545 * A lot of this procedure is duplicated from other places -
1546 * these things ought to be done in common routines. Prime opportunity
1547 * to introduce bugs by changing the other stuff and forgetting to
1548 * do it here.
1549 */
1550int loadNewMenus(me, file)
1551     MenuJet me;
1552     char *file;
1553{
1554  XjSize size;
1555  char *newItems;
1556  Item *i;
1557
1558  newItems = loadFile(file);
1559
1560  if (newItems != NULL && newItems[0] != '\0')
1561    {
1562      closeMenuAndAncestorsToLevel(me, me->menu.deepestOpened,
1563                                   me->menu.rootMenu, False);
1564
1565      me->menu.deepestOpened = me->menu.rootMenu;
1566
1567      if (me->menu.rude == True)
1568        {
1569          if (me->menu.grabbed == True)
1570            XUngrabPointer(me->core.display, CurrentTime);
1571          me->menu.grabbed = False;
1572        }
1573
1574      if (me->menu.buttonDown == 1)
1575        me->menu.buttonDown = 0;
1576
1577      createTablesFromString(me, newItems);
1578      destroyMenu(me);
1579      for (i = me->menu.firstMenu; i != NULL; i = i->next)
1580        i->u.m.m = NULL;
1581
1582      createMenuFromTables(me);
1583
1584      me->menu.rootMenu->state = OPENED;
1585
1586      me->menu.rootMenu->menuPane = me->core.window;
1587      me->menu.rootMenu->pane_x = 0;
1588      me->menu.rootMenu->pane_y = 0;
1589
1590      me->menu.same = False;
1591      me->menu.inside = False;
1592      me->menu.buttonDown = 0;
1593
1594      if (XCNOMEM == XSaveContext(me->core.display,
1595                                  me->menu.rootMenu->menuPane,
1596                                  context, (caddr_t)me->menu.rootMenu))
1597        XjFatalError("out of memory in XSaveContext");
1598
1599      me->menu.rootMenu->paneType = NORMAL;
1600
1601      computeAllMenuSizes(me, me->menu.rootMenu);
1602      computeRootMenuSize(me, &size);
1603      me->core.width  = size.width;
1604      me->core.height = size.height;
1605
1606#ifdef notdefined
1607      size.width = me->core.width; /* this is an official hack */
1608      size.height = me->core.height;
1609#endif
1610      XjResize(XjParent(me), &size);
1611
1612      return 0;
1613    }
1614  else
1615    return 1;
1616}
1617
1618
1619
1620/************************************************************************
1621 *
1622 *  initialize  --  the mother of all routines...
1623 *
1624 ************************************************************************/
1625static void initialize(me)
1626     MenuJet me;
1627{
1628  struct timeval start, end;
1629  unsigned long foo;
1630  char *fontname;
1631  XjSize size;
1632  Bool font_prop;
1633  if (DEBUG)
1634    {
1635      gettimeofday(&start, NULL);
1636      printf("initialize: - %d.%d + \n", start.tv_sec, start.tv_usec);
1637    }
1638
1639  font_prop = XGetFontProperty(me->menu.font, XA_FONT, &foo);
1640  if (font_prop)
1641    fontname = XGetAtomName(me->core.display, foo);
1642
1643  me->menu.Names = create_hash(HASHSIZE);
1644  me->menu.Types = create_hash(HASHSIZE);
1645  me->menu.firstItem = NULL;
1646  me->menu.firstMenu = NULL;
1647
1648  me->menu.items = loadFile(me->menu.file);
1649
1650  if (me->menu.items == NULL  &&  me->menu.fallback[0] != '\0')
1651    {
1652      Jet root=(Jet)me;
1653
1654      XjWarning("trying fallback file");
1655      while (XjParent(root))
1656        root=XjParent(root);
1657
1658      XjUserWarning(root, NULL, False,
1659                    "Unable to load menu file, trying fallback file.",
1660                    "Try restarting dash later to get up-to-date menus.");
1661      me->menu.items = loadFile(me->menu.fallback);
1662    }
1663
1664  if (me->menu.items != NULL &&
1665      me->menu.items[0] != '\0') /* a bit of a lose, but... */
1666    createTablesFromString(me, me->menu.items);
1667  else
1668    createTablesFromString(me, NOMENU);
1669
1670  if (font_prop)
1671    XjFree(fontname);
1672
1673  /*
1674   * load up user-defined stuff...
1675   */
1676  if (me->menu.userFile[0] != '\0')
1677    me->menu.userItems = loadFile(me->menu.userFile);
1678
1679  if (me->menu.userItems != NULL &&
1680      me->menu.userItems[0] != '\0') /* a bit of a lose, but... */
1681    createTablesFromString(me, me->menu.userItems);
1682
1683  /*
1684   * create menus from everything...
1685   */
1686  createMenuFromTables(me);
1687
1688#ifdef notdefined
1689  if (print)
1690    {
1691      struct timeval start, end;
1692
1693      gettimeofday(&start, NULL);
1694      printf("initialize(printTable): - %d.%d + \n", start.tv_sec, start.tv_usec);
1695
1696      printTable(me->menu.firstMenu);
1697      printTable(me->menu.firstItem);
1698      print=0;
1699
1700      gettimeofday(&end, NULL);
1701      printf("initialize(printTable): %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec,
1702             (end.tv_usec > start.tv_usec)
1703             ? end.tv_sec - start.tv_sec
1704             : end.tv_sec - start.tv_sec - 1,
1705             (end.tv_usec > start.tv_usec)
1706             ? end.tv_usec - start.tv_usec
1707             : end.tv_usec + 1000000 - start.tv_usec );
1708    }
1709#endif
1710
1711  /*
1712   * Here all of the menu tree has been created and filled in
1713   * with titles, callbacks, etc. Now we go through and compute the
1714   * necessary sizes of all of the menu panes.
1715   */
1716
1717  me->menu.rootMenu->paneType = NORMAL;
1718
1719  computeAllMenuSizes(me, me->menu.rootMenu);
1720
1721  computeRootMenuSize(me, &size);
1722  (XjParent(me))->core.width  = me->core.width  = size.width;
1723  (XjParent(me))->core.height = me->core.height = size.height;
1724
1725  me->menu.grabbed = False;
1726  me->menu.scrollx = 0;
1727  if (DEBUG)
1728    {
1729      gettimeofday(&end, NULL);
1730      printf("initialize: %d.%d = %d.%06.6d\n", end.tv_sec, end.tv_usec,
1731             (end.tv_usec > start.tv_usec)
1732             ? end.tv_sec - start.tv_sec
1733             : end.tv_sec - start.tv_sec - 1,
1734             (end.tv_usec > start.tv_usec)
1735             ? end.tv_usec - start.tv_usec
1736             : end.tv_usec + 1000000 - start.tv_usec );
1737    }
1738}
1739
1740
1741
1742/************************************************************************
1743 *
1744 *  realize  --  think about it...
1745 *
1746 ************************************************************************/
1747static void realize(me)
1748     MenuJet me;
1749{
1750  unsigned long valuemask;
1751  XGCValues values;
1752  int temp;
1753
1754  /*
1755   * Initialize GC's
1756   */
1757
1758  if (me->menu.reverseVideo)
1759    {
1760      temp = me->menu.foreground;
1761      me->menu.foreground = me->menu.background;
1762      me->menu.background = temp;
1763    }
1764
1765  values.function = GXcopy;
1766  values.line_width = 0;
1767  values.foreground = me->menu.foreground;
1768  values.background = me->menu.background;
1769  values.font = me->menu.font->fid;
1770  values.graphics_exposures = False;
1771  valuemask = ( GCForeground | GCBackground | GCFunction | GCFont
1772               | GCLineWidth | GCGraphicsExposures );
1773
1774  me->menu.gc = XjCreateGC(me->core.display,
1775                           me->core.window,
1776                           valuemask,
1777                           &values);
1778
1779  values.line_width = 0;
1780  values.line_style = LineOnOffDash;
1781  values.dashes = (char)1;
1782  valuemask |= GCLineStyle | GCDashList;
1783
1784  me->menu.dash_gc = XjCreateGC(me->core.display,
1785                                me->core.window,
1786                                valuemask,
1787                                &values);
1788
1789  me->menu.dot_gc = XjCreateGC(me->core.display,
1790                               me->core.window,
1791                               valuemask,
1792                               &values);
1793
1794  XSetDashes(me->core.display, me->menu.dash_gc, 1, "\001\002", 2);
1795  XSetDashes(me->core.display, me->menu.dot_gc, 1, "\001\002", 2);
1796
1797  valuemask &= ~(GCLineStyle | GCDashList);
1798
1799  me->menu.dim_gc = XjCreateGC(me->core.display,
1800                               me->core.window,
1801                               valuemask,
1802                               &values);
1803
1804  if (me->menu.grey != NULL)
1805    { /* Turn into a toolkit routine... */
1806      Pixmap p;
1807
1808      p = XjCreatePixmap(me->core.display,
1809                         me->core.window,
1810                         me->menu.grey->width,
1811                         me->menu.grey->height,
1812                         DefaultDepth(me->core.display, /* wrong... sigh. */
1813                                      DefaultScreen(me->core.display)));
1814
1815      XCopyPlane(me->core.display,
1816                 me->menu.grey->pixmap,
1817                 p,
1818                 me->menu.gc,
1819                 0, 0,
1820                 me->menu.grey->width,
1821                 me->menu.grey->height,
1822                 0, 0, 1);
1823
1824      valuemask = GCFillStyle | GCTile;
1825      values.fill_style = FillTiled;
1826      values.tile = p;
1827
1828      XChangeGC(me->core.display,
1829                me->menu.dim_gc,
1830                valuemask,
1831                &values);
1832    }
1833
1834  values.line_width = 0;
1835  values.foreground = me->menu.background;
1836  valuemask = ( GCForeground | GCBackground | GCFunction | GCFont
1837               | GCLineWidth | GCGraphicsExposures );
1838  me->menu.background_gc = XjCreateGC(me->core.display,
1839                                      me->core.window,
1840                                      valuemask,
1841                                      &values);
1842
1843  values.function = GXxor;
1844  values.foreground = (me->menu.foreground ^
1845                       me->menu.background);
1846
1847  me->menu.invert_gc = XjCreateGC(me->core.display,
1848                                  me->core.window,
1849                                  valuemask,
1850                                  &values);
1851  /*
1852   * Do some context initialization
1853   */
1854
1855  if (!gotContextType)
1856    {
1857      context = XUniqueContext();
1858      gotContextType ++;
1859    }
1860
1861  /* rootMenu is always opened */
1862  me->menu.deepestOpened = me->menu.rootMenu;
1863  me->menu.rootMenu->state = OPENED;
1864
1865  me->menu.rootMenu->menuPane = me->core.window;
1866  me->menu.rootMenu->pane_x = 0;
1867  me->menu.rootMenu->pane_y = 0;
1868
1869  me->menu.same = False;
1870  me->menu.inside = False;
1871  me->menu.buttonDown = 0;
1872
1873  XjSelectInput(me->core.display, me->core.window,
1874               ExposureMask | ButtonPressMask | ButtonReleaseMask |
1875               ButtonMotionMask | EnterWindowMask /* | LeaveWindowMask */
1876                /*| StructureNotifyMask */);
1877
1878  /*
1879   * Send the events for this window to us
1880   */
1881
1882  XjRegisterWindow(me->core.window, me);
1883
1884  /*
1885   * We also want to remember what part of the tree
1886   * this menu belongs to
1887   */
1888  if (XCNOMEM == XSaveContext(me->core.display, me->menu.rootMenu->menuPane,
1889                              context, (caddr_t)me->menu.rootMenu))
1890    XjFatalError("out of memory in XSaveContext");
1891
1892  /*
1893   * futz around with "rightJet"...
1894   */
1895  if (me->menu.rightJet)
1896    {
1897      me->menu.right_jet = XjFindJet(me->menu.rightJet, XjParent(me));
1898      if (me->menu.right_jet)
1899        {
1900          XjSize size;
1901          int x = me->menu.rootMenu->pane_width
1902            + me->menu.rootMenu->pane_x;
1903
1904          size.width  = me->core.width - x;
1905          size.height = me->core.height;
1906          XjMove(me->menu.right_jet, x, me->menu.rootMenu->pane_y);
1907          XjResize(me->menu.right_jet, &size);
1908        }
1909    }
1910}
1911
1912
1913
1914
1915static int (*def_handler)();
1916static int menu_error=0;
1917static int handler(display, error)
1918     Display *display;
1919     XErrorEvent *error;
1920{
1921  if (error->error_code == BadWindow  ||  error->error_code == BadAtom)
1922    {
1923      menu_error=1;
1924      return 0;
1925    }
1926
1927  def_handler(display, error);
1928  return 0;                     /* it'll never get this far anyway... */
1929}
1930
1931/************************************************************************
1932 *
1933 *  findMenu  --  figure out where the user clicked...
1934 *
1935 ************************************************************************/
1936void findMenu(me, who, what, start, window, rx, ry)
1937     MenuJet me;
1938     Menu **who;        /* what menu we found the mouse to be in */
1939     int *what;         /* what state that menu ought to be in */
1940     Menu *start;       /* where to start scanning the menu tree */
1941     Window window;     /* window mouse location reported relative to */
1942     int rx, ry;        /* where the mouse is */
1943{
1944  int x, y;
1945  Menu *level, *ptr;
1946  Window scratch;
1947
1948  menu_error=0;
1949  def_handler = XSetErrorHandler(handler);
1950  if (window != me->core.window)
1951    (void)XTranslateCoordinates(me->core.display,
1952                                window,
1953                                me->core.window,
1954                                rx, ry,
1955                                &rx, &ry, &scratch);
1956  (void) XSetErrorHandler(def_handler);
1957  if (menu_error)
1958    {
1959      *who=NULL;
1960      menu_error=0;
1961      return;
1962    }
1963  menu_error=0;
1964
1965#ifdef DEBUGEVENTS
1966      fprintf(stderr, "find: %d, %d\n",rx, ry);
1967#endif
1968
1969  for (level = start; level != NULL; level = level->parent)
1970    {
1971      x = rx - level->pane_x;
1972      y = ry - level->pane_y;
1973
1974      if (level->state == OPENED)
1975        {
1976          switch(level->orientation)
1977            {
1978            case HORIZONTAL:
1979              for (ptr = level->child; ptr != NULL; ptr = ptr->sibling)
1980                if (x >= ptr->label_x && y >= BORDER &&
1981                    x < ptr->label_x + ptr->label_width &&
1982                    y < level->pane_height - BORDER - SHADOW)
1983                  {
1984                    *who = ptr;
1985                    if (ptr->paneType == HELP ||
1986                        ptr->child == NULL)
1987                      *what = SELECTED;
1988                    else
1989                      *what = OPENED;
1990                    return;
1991                  }
1992              break;
1993
1994            case VERTICAL:
1995              for (ptr = level->child; ptr != NULL; ptr = ptr->sibling)
1996                if (x >= BORDER && y >= ptr->label_y &&
1997                    x < level->pane_width - BORDER - SHADOW &&
1998                    y < ptr->label_y + ptr->label_height)
1999                  {
2000                    *who = ptr;
2001
2002                    *what = CLOSED;
2003
2004/*                  if (ptr->paneType == HELP &&
2005                        me->menu.showHelp == 0)
2006                      *what = SELECTED;
2007                    else */
2008                      if (x >= level->pane_open_x)
2009                        {
2010                          if (ptr->child != NULL &&
2011                              (ptr->paneType != HELP ||
2012                               (ptr->paneType == HELP &&
2013                                me->menu.showHelp != 0)))
2014                            *what = OPENED;
2015                        }
2016                      else
2017                        *what = SELECTED;
2018/*
2019                    if (*what == SELECTED &&
2020                        ptr->activateProc == NULL)
2021                      *what = CLOSED; */
2022
2023                    return;
2024                  }
2025              break;
2026            }
2027
2028          if (x >= 0 && x < level->pane_width &&
2029              y >= 0 && y < level->pane_height)
2030            {
2031              *who = SAME;
2032              return;
2033            }
2034        }
2035    }
2036
2037  *who = NULL;
2038  return;
2039}
2040
2041
2042
2043/************************************************************************
2044 *
2045 *  highlightMenu  --  highlight the current item.
2046 *
2047 ************************************************************************/
2048static void highlightMenu(me, menu, state, gc)
2049     MenuJet me;
2050     Menu *menu;
2051     int state;
2052     GC gc;
2053{
2054#ifdef DEBUGSETTINGS
2055  fprintf(stderr, "Invert %s\n", menu->title);
2056#endif
2057
2058  if (menu->parent->paneType == HELP)
2059    return;
2060
2061  switch(menu->parent->orientation)
2062    {
2063    case HORIZONTAL:
2064      XDrawRectangle(me->core.display, menu->parent->menuPane,
2065                     gc,
2066                     menu->label_x + me->menu.hMenuPadding / 4,
2067                     BORDER + me->menu.vMenuPadding / 4,
2068                     menu->label_width - me->menu.hMenuPadding / 2 - 1,
2069                     menu->parent->pane_height -
2070                       2 * BORDER - SHADOW - 1 - me->menu.vMenuPadding / 2);
2071      break;
2072
2073    case VERTICAL: /* make sure pane_open_x gets inited */
2074      if (state == OPENED)
2075        XDrawRectangle(me->core.display, menu->parent->menuPane,
2076                       gc,
2077                       menu->parent->pane_open_x + me->menu.hMenuPadding / 4,
2078                       menu->label_y + me->menu.vMenuPadding / 4,
2079                       menu->parent->pane_width - BORDER - SHADOW -
2080                    menu->parent->pane_open_x - 1 - me->menu.hMenuPadding / 2,
2081                       menu->label_height - 1 - me->menu.vMenuPadding / 2);
2082      else
2083        if (state == SELECTED)
2084        XDrawRectangle(me->core.display, menu->parent->menuPane,
2085                       gc,
2086                       BORDER + me->menu.hMenuPadding / 4,
2087                       menu->label_y + me->menu.vMenuPadding / 4,
2088          menu->parent->pane_open_x - BORDER - 1 - me->menu.hMenuPadding / 2,
2089                       menu->label_height - 1 - me->menu.vMenuPadding / 2);
2090      break;
2091    }
2092}
2093
2094
2095
2096/************************************************************************
2097 *
2098 *  offset  --  move the menu bar...  there be beasties here...
2099 *
2100 ************************************************************************/
2101static void offset(me, menu, delta)
2102     MenuJet me;
2103     Menu *menu;
2104     int delta;
2105{
2106  if (menu->parent != NULL)
2107    offset(me, menu->parent, delta);
2108  else
2109    me->menu.scrollx += delta;
2110
2111/*   Not quite right...  close...
2112  if (menu->menuPane == me->core.window)
2113    {
2114      me_moving = 1;
2115      XMoveWindow(me->core.display, menu->menuPane,
2116                  me->core.x + delta, me->core.y);
2117    }
2118  else
2119  */
2120  menu->x += delta;
2121  me_moving = 1;
2122  XMoveWindow(me->core.display, menu->menuPane, menu->x, menu->y);
2123}
2124
2125
2126/************************************************************************
2127 *
2128 *  woffset  --  offset and Warp!  (more beasties...)
2129 *
2130 ************************************************************************/
2131static void woffset(me, menu, delta, warp)
2132     MenuJet me;
2133     Menu *menu;
2134     int delta;
2135     Boolean warp;
2136{
2137  offset(me, menu, delta);
2138
2139  if (warp)
2140    XWarpPointer(me->core.display, None, None,
2141                 0, 0, 0, 0,
2142                 delta, 0);
2143}
2144
2145
2146
2147/************************************************************************
2148 *
2149 *  openMenu  --  user clicked on another menu...  open up!
2150 *
2151 ************************************************************************/
2152/*
2153 * Add window caching code?
2154 */
2155static void openMenu(bar, menu)
2156     MenuJet bar;
2157     Menu *menu;
2158{
2159  Menu *checkMenu;
2160  int x, y, delta;
2161  Window parentwindow, scratch;
2162  int screen;
2163  WindowJet wj;
2164
2165#ifdef DEBUGSETTINGS
2166  fprintf(stderr, "Open %s\n", menu->title);
2167#endif
2168
2169  if (menu->child && !menu->menuPane /* twm problems... */)
2170    {
2171      XSetWindowAttributes attributes;
2172      int attribsMask;
2173
2174      attributes.override_redirect = True;
2175      attribsMask = CWOverrideRedirect;
2176
2177      attributes.background_pixel = bar->menu.background;
2178      attribsMask |= CWBackPixel;
2179
2180      screen = DefaultScreen(bar->core.display);
2181
2182      /*
2183       * Create the menu pane
2184       */
2185      parentwindow = RootWindow(bar->core.display, screen);
2186
2187      /*
2188       * This is a hack to figure out where to put the window -
2189       * it's definitely the wrong way to go, but I get anxious
2190       * sometimes. I'll fix it later. Honest.
2191       * (He's lying.)
2192       */
2193      XTranslateCoordinates(bar->core.display,
2194                            menu->parent->menuPane,
2195                            parentwindow,
2196                            0, 0,
2197                            &x, &y,
2198                            &scratch);
2199
2200      switch(menu->parent->orientation)
2201        {
2202        case HORIZONTAL:
2203          x += menu->label_x;
2204          y += menu->parent->pane_height/* - BORDER*/;
2205
2206          menu->pane_x = menu->parent->pane_x + menu->label_x;
2207          menu->pane_y = menu->parent->pane_y + menu->parent->pane_height;
2208          break;
2209
2210        case VERTICAL:
2211          x += menu->parent->pane_width/* - BORDER */;
2212          y += menu->label_y;
2213
2214          menu->pane_x = menu->parent->pane_x + menu->parent->pane_width;
2215          menu->pane_y = menu->parent->pane_y + menu->label_y;
2216          break;
2217        }
2218
2219      delta = 0;
2220      if (bar->menu.moveMenus)
2221        {
2222          for (checkMenu = menu->parent->child;
2223               checkMenu != NULL;
2224               checkMenu = checkMenu->sibling)
2225            delta = MAX(delta, checkMenu->pane_width);
2226
2227          if ((delta =
2228               (DisplayWidth(bar->core.display,
2229                             DefaultScreen(bar->core.display))
2230                - (x + delta))) < 0)
2231            {
2232              woffset(bar, menu->parent, delta, True);
2233              bar->menu.responsibleParent = menu->parent;
2234            }
2235        }
2236      menu->x = x + (delta < 0 ? delta : 0);
2237      menu->y = y;
2238
2239      menu->menuPane =
2240        XCreateWindow(bar->core.display,
2241                      parentwindow,
2242                      menu->x, menu->y,
2243                      menu->pane_width,
2244                      menu->pane_height,
2245                      0,
2246                      DefaultDepth(bar->core.display,
2247                                   DefaultScreen(bar->core.display)),
2248                      InputOutput,
2249                      CopyFromParent,
2250                      attribsMask,
2251                      &attributes);
2252
2253      wj = (WindowJet) XjParent(bar);
2254      if (wj->window.cursor != (Cursor) NULL)
2255        XDefineCursor(bar->core.display, menu->menuPane, wj->window.cursor);
2256
2257      XSelectInput(bar->core.display, menu->menuPane,
2258                   ExposureMask | StructureNotifyMask | ButtonPressMask |
2259                   ButtonReleaseMask | ButtonMotionMask);
2260
2261      XMapWindow(bar->core.display, menu->menuPane);
2262
2263      /*
2264       * Send the events for this window to us
2265       */
2266
2267      XjRegisterWindow(menu->menuPane, bar);
2268
2269      /*
2270       * We also want to remember what part of the tree
2271       * this menu belongs to
2272       */
2273
2274      if (XCNOMEM == XSaveContext(bar->core.display,
2275                                  menu->menuPane,
2276                                  context, (caddr_t)menu))
2277        XjFatalError("out of memory in XSaveContext");
2278    }
2279}
2280
2281
2282
2283/************************************************************************
2284 *
2285 *  closeMenu  --  user closed a menu...
2286 *
2287 ************************************************************************/
2288static void closeMenu(bar, menu, warp)
2289     MenuJet bar;
2290     Menu *menu;
2291     Boolean warp;
2292{
2293  int freespace;
2294#ifdef DEBUGSETTINGS
2295  fprintf(stderr, "Close %s\n", menu->title);
2296#endif
2297
2298  if (menu->menuPane != (Window) NULL)
2299    {
2300      XDestroyWindow(bar->core.display, menu->menuPane);
2301      menu->menuPane = (Window) NULL;
2302
2303      if (bar->menu.scrollx != 0 && menu->parent != NULL &&
2304          bar->menu.responsibleParent == menu)
2305        {
2306          menu = menu->parent;
2307          bar->menu.responsibleParent = menu;
2308          freespace = DisplayWidth(bar->core.display,
2309                                   DefaultScreen(bar->core.display)) -
2310                      (menu->x + menu->pane_width);
2311          woffset(bar, menu, MIN(freespace, -bar->menu.scrollx), warp);
2312        }
2313      else if (bar->menu.scrollx != 0 && menu->parent == bar->menu.rootMenu)
2314        woffset(bar, menu->parent, -bar->menu.scrollx, warp);
2315    }
2316}
2317
2318
2319
2320/************************************************************************
2321 *
2322 *  setMenu  --  I have no idea...
2323 *
2324 ************************************************************************/
2325static void setMenu(me, menu, newState, warp)
2326     MenuJet me;
2327     Menu *menu;
2328     int newState;
2329     Boolean warp;
2330{
2331  switch(newState)
2332    {
2333    case CLOSED:
2334      switch(menu->state)
2335        {
2336        case CLOSED:
2337          break;
2338        case OPENED:
2339          closeMenu(me, menu, warp);
2340          highlightMenu(me, menu, OPENED, me->menu.background_gc);
2341          break;
2342        case SELECTED:
2343          highlightMenu(me, menu, SELECTED, me->menu.background_gc);
2344          break;
2345        }
2346      menu->state = CLOSED;
2347      break;
2348
2349    case SELECTED:
2350      switch(menu->state)
2351        {
2352        case CLOSED:
2353          highlightMenu(me, menu, SELECTED,
2354                        (menu->activateProc && menu->supported) ?
2355                        me->menu.gc : me->menu.dot_gc);
2356          break;
2357        case OPENED:
2358          closeMenu(me, menu, warp);
2359          highlightMenu(me, menu, OPENED, me->menu.background_gc);
2360          highlightMenu(me, menu, SELECTED,
2361                        (menu->activateProc && menu->supported) ?
2362                        me->menu.gc : me->menu.dot_gc);
2363          break;
2364        case SELECTED:
2365          break;
2366        }
2367      menu->state = SELECTED;
2368      break;
2369
2370    case OPENED:
2371      switch(menu->state)
2372        {
2373        case CLOSED:
2374          highlightMenu(me, menu, OPENED, me->menu.dot_gc);
2375          openMenu(me, menu);
2376          break;
2377        case OPENED:
2378          break;
2379        case SELECTED:
2380          highlightMenu(me, menu, SELECTED, me->menu.background_gc);
2381          highlightMenu(me, menu, OPENED, me->menu.dot_gc);
2382          openMenu(me, menu);
2383          break;
2384        }
2385      menu->state = OPENED;
2386      break;
2387    }
2388}
2389
2390
2391
2392/************************************************************************
2393 *
2394 *  closeMenuAndAncestorsToLevel  --  close menu and ancestors up to
2395 *      level specified...
2396 *
2397 ************************************************************************/
2398static void closeMenuAndAncestorsToLevel(bar, start, end, warp)
2399     MenuJet bar;
2400     Menu *start, *end;
2401     Boolean warp;
2402{
2403  Menu *ptr;
2404
2405  for (ptr = start; ptr != end && ptr != end->parent; ptr = ptr->parent)
2406    setMenu(bar, ptr, CLOSED, warp);
2407  XFlush(bar->core.display);
2408}
2409
2410
2411/************************************************************************
2412 *
2413 *  drawBackground  --  draw background of menu window...
2414 *
2415 ************************************************************************/
2416static void drawBackground(me, menu)
2417     MenuJet me;
2418     Menu *menu;
2419{
2420  int width = menu->pane_width;
2421  int height = menu->pane_height;
2422
2423  if (me->core.window == menu->menuPane)
2424    width = me->core.width;
2425/*
2426  XFillRectangle(me->core.display, menu->menuPane,
2427                 me->menu.background_gc,
2428                 BORDER, BORDER,
2429                 width - 2 * BORDER - SHADOW,
2430                 height - 2 * BORDER - SHADOW);
2431*/
2432  XDrawRectangle(me->core.display, menu->menuPane,
2433                 me->menu.gc,
2434                 0, 0,
2435                 width - SHADOW - 1,
2436                 height - SHADOW - 1);
2437
2438  XDrawLine(me->core.display, menu->menuPane,
2439            me->menu.gc,
2440            BORDER, height - 1,
2441            width - 1, height - 1);
2442
2443  XDrawLine(me->core.display, menu->menuPane,
2444            me->menu.gc,
2445            width - 1, BORDER,
2446            width - 1, height - 1);
2447}
2448
2449
2450
2451/************************************************************************
2452 *
2453 *  drawPane  --  draw the items in a menu pane.
2454 *
2455 ************************************************************************/
2456static void drawPane(me, eventMenu)
2457     MenuJet me;
2458     Menu *eventMenu;
2459{
2460  Menu *m;
2461
2462  drawBackground(me, eventMenu);
2463
2464  if (eventMenu->paneType == HELP)
2465    drawHelp(me, eventMenu);
2466  else
2467    {
2468      if (eventMenu->orientation == VERTICAL)
2469        XDrawLine(me->core.display,
2470                  eventMenu->menuPane,
2471                  me->menu.dash_gc,
2472                  eventMenu->pane_open_x,
2473                  BORDER,
2474                  eventMenu->pane_open_x,
2475                  eventMenu->pane_height - BORDER);
2476
2477      for (m = eventMenu->child; m != NULL; m = m->sibling)
2478        {
2479          if (m->activateProc != NULL)
2480            XDrawString(me->core.display, eventMenu->menuPane,
2481                        m->supported ? me->menu.gc : me->menu.dim_gc,
2482                        m->label_x + me->menu.hMenuPadding / 2 - 10,
2483                        m->label_y + me->menu.vMenuPadding / 2 +
2484                        me->menu.font->ascent,
2485                        "*", 1);
2486
2487          XDrawString(me->core.display, eventMenu->menuPane,
2488                      m->supported ? me->menu.gc : me->menu.dim_gc,
2489                      m->label_x + me->menu.hMenuPadding / 2,
2490                      m->label_y + me->menu.vMenuPadding / 2 +
2491                      me->menu.font->ascent,
2492                      m->title, strlen(m->title));
2493
2494          switch(m->state)
2495            {
2496            case OPENED:
2497              highlightMenu(me, m, OPENED, me->menu.dot_gc);
2498              break;
2499
2500            case SELECTED:
2501              highlightMenu(me, m, SELECTED,
2502                            (m->activateProc && m->supported) ?
2503                            me->menu.gc : me->menu.dot_gc);
2504              break;
2505
2506            case CLOSED:
2507              break;
2508            }
2509
2510          if (eventMenu->orientation == VERTICAL)
2511            {
2512              if (m->paneType == HELP)
2513                {
2514                  if (me->menu.showHelp)
2515                    XCopyPlane(me->core.display,
2516                               me->menu.helpPixmap->pixmap,
2517                               eventMenu->menuPane,
2518                               me->menu.gc,
2519                               0, 0,
2520                               me->menu.helpPixmap->width,
2521                               me->menu.helpPixmap->height,
2522                               eventMenu->pane_open_x +
2523                               me->menu.hMenuPadding / 2,
2524                               m->label_y +
2525                               me->menu.vMenuPadding / 2,
2526                               1);
2527                }
2528              else
2529                if (m->child != NULL)
2530                  XCopyPlane(me->core.display,
2531                             me->menu.submenuPixmap->pixmap,
2532                             eventMenu->menuPane,
2533                             me->menu.gc,
2534                             0, 0,
2535                             me->menu.submenuPixmap->width,
2536                             me->menu.submenuPixmap->height,
2537                             eventMenu->pane_open_x +
2538                             me->menu.hMenuPadding / 2,
2539                             m->label_y +
2540                             me->menu.vMenuPadding / 2,
2541                             1);
2542            }
2543        }
2544    }
2545}
2546
2547
2548
2549/************************************************************************
2550 *
2551 *  event_handler  --  deal with all the x events that get thrown at us.
2552 *
2553 ************************************************************************/
2554static Boolean event_handler(me, event)
2555     MenuJet me;
2556     XEvent *event;
2557{
2558  MenuInfo info;
2559  Menu *eventMenu;
2560  Menu *m;
2561  int state, newState;
2562
2563  if (XCNOENT == XFindContext(me->core.display, event->xany.window,
2564                              context, (caddr_t *)&eventMenu))
2565    return False;
2566
2567  switch(event->type)
2568    {
2569    case GraphicsExpose:
2570    case Expose:
2571      if (DEBUG)
2572      fprintf(stderr, "Exposure\n");
2573
2574      if (event->xexpose.count != 0)
2575        break;
2576      if (eventMenu->menuPane != (Window) NULL)
2577        {
2578          drawPane(me, eventMenu);
2579        }
2580
2581      /* hack to support clock in menu bar */
2582      if (me->menu.right_jet)
2583        XjExpose(me->menu.right_jet, event);
2584#ifdef notdefined         
2585      if (me->core.sibling != NULL &&
2586          me->core.sibling->core.window == event->xany.window)
2587        XjExpose(me->core.sibling, event);
2588#endif
2589      break;
2590
2591    case ButtonRelease:
2592#ifdef DEBUGEVENTS
2593      fprintf(stderr, "ButtonRelease to %d (%d) %d\n",
2594              event->xbutton.button, me->menu.buttonDown - 1,
2595              event->xbutton.state);
2596#endif
2597
2598      findMenu(me, &m, &newState, me->menu.deepestOpened,
2599               event->xbutton.window,
2600               event->xbutton.x, event->xbutton.y);
2601
2602      if (me->menu.buttonDown == 1 &&
2603          (me->menu.deepestOpened->state == SELECTED ||
2604           m == NULL))
2605        {
2606          state = me->menu.deepestOpened->state;
2607          closeMenuAndAncestorsToLevel(me, me->menu.deepestOpened,
2608                                       me->menu.rootMenu, False);
2609
2610          if (me->menu.inside == True || me->menu.same == True)
2611            {
2612              info.menubar = me;
2613              /* there's potentially a bug here, but I don't want
2614                 to think about it right now. */
2615              if ((me->menu.deepestOpened->parent != NULL &&
2616                  me->menu.deepestOpened->parent->paneType == HELP) ||
2617                  state != SELECTED)
2618                info.menu = me->menu.deepestOpened->parent; /* NOP */
2619              else
2620                {
2621                  info.null = NULL;
2622                  info.menu = me->menu.deepestOpened;
2623                  if (me->menu.deepestOpened->supported &&
2624                      me->menu.deepestOpened->activateProc)
2625                    if (me->menu.verifyProc != NULL &&
2626                        me->menu.deepestOpened->verify)
2627                      XjCallCallbacks(&info,
2628                                      me->menu.verifyProc, NULL);
2629                    else
2630                      XjCallCallbacks(&info,
2631                                      me->menu.deepestOpened->activateProc,
2632                                      event);
2633                }
2634            }
2635
2636          me->menu.deepestOpened = me->menu.rootMenu;
2637
2638          me->menu.inside = False;
2639          me->menu.buttonDown = 0;
2640
2641          if (me->menu.rude == True)
2642            {
2643              if (me->menu.grabbed == True)
2644                XUngrabPointer(me->core.display, CurrentTime);
2645              me->menu.grabbed = False;
2646            }
2647        }
2648      else
2649        if (me->menu.buttonDown == 1)
2650          me->menu.buttonDown = 0;
2651
2652      break;
2653
2654    case ButtonPress:
2655      if (me->core.window == event->xany.window)
2656        {
2657          if (WindowVisibility(XjParent(me)) != VisibilityUnobscured)
2658            XRaiseWindow(me->core.display, me->core.window);
2659          else
2660            {
2661              Jet rj = me->menu.right_jet;
2662              if (rj
2663                  && ((event->xbutton.x > rj->core.x)
2664                      && (event->xbutton.x < rj->core.x + rj->core.width))
2665                  && ((event->xbutton.y > rj->core.y)
2666                      && (event->xbutton.y < rj->core.y + rj->core.height))
2667                  && rj->core.classRec->core_class.event)
2668                {
2669                  if (rj->core.classRec->core_class.event(rj, event))
2670                    break;
2671                }
2672            }
2673        }
2674
2675#ifdef DEBUGEVENTS
2676      fprintf(stderr, "ButtonPress to %d (%d) %d\n",
2677              event->xbutton.button, me->menu.buttonDown + 1,
2678              event->xbutton.state);
2679#endif
2680      if (me->menu.buttonDown == 0)
2681        {
2682          if (me->menu.rude == True &&
2683              me->menu.grabbed == False)
2684            {
2685              XGrabPointer(me->core.display,
2686                           me->core.window,
2687                           False,
2688                           ButtonPressMask |
2689                           ButtonReleaseMask |
2690                           ButtonMotionMask |
2691                           EnterWindowMask
2692                           /* | LeaveWindowMask */ ,
2693                           GrabModeAsync,
2694                           GrabModeAsync,
2695                           None,
2696                           None,
2697                           CurrentTime);
2698              me->menu.grabbed = True;
2699            }
2700          me->menu.buttonDown = 1;
2701        }
2702
2703/*      break; */
2704
2705    case MotionNotify:
2706/*
2707#ifdef DEBUGERVENTS
2708      fprintf(stderr, "MotionNotify: %d, %d; %d\n",
2709              event->xmotion.x, event->xmotion.y, event->xmotion.state);
2710#endif
2711*/
2712      if (me->menu.buttonDown == 0)
2713        break;
2714
2715      findMenu(me, &m, &newState, me->menu.deepestOpened,
2716               event->xmotion.window,
2717               event->xmotion.x, event->xmotion.y);
2718
2719      if (m == SAME)
2720        {
2721          me->menu.same = True;
2722          break;
2723        }
2724
2725      me->menu.same = False;
2726
2727      /* We're outside of all menus */
2728      if (m == NULL)
2729        {
2730          if (me->menu.inside == False)
2731            break; /* just moving around outside menus */
2732
2733          /* Here we've just moved outside of all menus */
2734          /* if (m == NULL && me->menu.inside == True) */
2735          setMenu(me, me->menu.deepestOpened, CLOSED, True);
2736          me->menu.deepestOpened =
2737            me->menu.deepestOpened->parent;
2738          me->menu.inside = False;
2739          break;
2740        }
2741
2742      /* m != NULL ... */
2743      if (me->menu.inside == False) /* just moved into something */
2744        {
2745          if (m == me->menu.deepestOpened) /* merge? */
2746            {
2747              if (m->state != newState)
2748                setMenu(me, m, newState, True);
2749
2750              me->menu.inside = True; /* this is an unlikely case */
2751              break;
2752            }
2753
2754          /* so we just moved into something that's not what's
2755             already opened */
2756          me->menu.inside = True;
2757        }
2758
2759      if (m == me->menu.deepestOpened) /* merge? */
2760        {
2761          if (m->state != newState)
2762            setMenu(me, m, newState, True);
2763         
2764          break;
2765        }
2766
2767      if (m->parent == me->menu.deepestOpened) /* opened a deeper child */
2768        {
2769          me->menu.deepestOpened = m;
2770          setMenu(me, me->menu.deepestOpened, newState, True);
2771          break;
2772        }
2773
2774      /*
2775       * Here we have moved into a sibling or parent of the deepest
2776       * child. We must close everything from the level of that
2777       * parent/sibling on down.
2778       */
2779      closeMenuAndAncestorsToLevel(me, me->menu.deepestOpened, m, True);
2780
2781      if (m->menuPane == (Window) NULL)
2782        setMenu(me, m, newState, True);
2783
2784      me->menu.deepestOpened = m;
2785
2786      break;
2787
2788    case CirculateNotify:
2789      if (DEBUG)
2790        printf("Menu - circ: x=%d, y=%d\n",
2791               event->xconfigure.x, event->xconfigure.y);
2792      break;
2793
2794    case CreateNotify:
2795      if (DEBUG)
2796        printf("Menu - create: x=%d, y=%d\n",
2797               event->xconfigure.x, event->xconfigure.y);
2798      break;
2799
2800    case ConfigureNotify:
2801      if (me->core.window == event->xany.window)
2802        {
2803          if (DEBUG)
2804            printf("Menu - config: x=%d, y=%d\n",
2805                   event->xconfigure.x, event->xconfigure.y);
2806          me->menu.rootMenu->x = event->xconfigure.x;
2807          me->menu.rootMenu->y = event->xconfigure.y;
2808        }
2809      break;
2810
2811    case DestroyNotify:
2812      XDeleteContext(me->core.display, event->xany.window, context);
2813      XjUnregisterWindow(event->xany.window, me);
2814      break;
2815
2816    case GravityNotify:
2817    case MapNotify:
2818    case ReparentNotify:
2819    case UnmapNotify:
2820      break;
2821
2822    case EnterNotify:
2823      if (me->menu.autoRaise &&
2824          me->core.window == event->xany.window &&
2825          WindowVisibility(XjParent(me)) != VisibilityUnobscured)
2826        XRaiseWindow(me->core.display, me->core.window);
2827      break;
2828
2829    case LeaveNotify:
2830      if (me->menu.buttonDown == 0)
2831        {
2832          Cursor cursor = XjCreateFontCursor(me->core.display,
2833                                             XC_ul_angle);
2834          XDefineCursor(me->core.display, me->core.window, cursor);
2835        }
2836
2837    default:
2838      return False;
2839    }
2840  return True;
2841}
2842
2843
2844
2845/************************************************************************
2846 *
2847 *  tab  --  space out...
2848 *
2849 ************************************************************************/
2850static void tab(n)
2851     int n;
2852{
2853  while (n--)
2854    fprintf(stdout, "    ");
2855}
2856
2857
2858/************************************************************************
2859 *
2860 *  printmenu  --  print out menu struct in a human-readable form.
2861 *
2862 ************************************************************************/
2863static void printmenu(first, tabbing)
2864     Menu *first;
2865     int tabbing;
2866{
2867  Menu *ptr;
2868  char *start, *end;
2869
2870  for (ptr = first; ptr != NULL; ptr = ptr->sibling)
2871    {
2872      tab(tabbing);
2873
2874      if (ptr->activateProc != NULL)
2875        fprintf(stdout, "*");
2876
2877      if (!strncmp("   ", ptr->title, 3))
2878        fprintf(stdout, "%s", ptr->title + 3);
2879      else if (!strncmp("* ", ptr->title, 2))
2880        fprintf(stdout, "%s", ptr->title + 2);
2881      else
2882        fprintf(stdout, "%s", ptr->title);
2883
2884      if (ptr->child != NULL)
2885        fprintf(stdout, ":\n");
2886      else
2887        fprintf(stdout, "\n");
2888
2889      if (ptr->paneType == HELP &&
2890          ptr->child != NULL)
2891        {
2892          start = ptr->child->title;
2893
2894          while (*start != '\0')
2895            {
2896              end = strchr(start, '\n');
2897              if (end == NULL)
2898                end = start + strlen(start);
2899              tab(tabbing + 1);
2900              fprintf(stdout, "%.*s\n", end - start, start);
2901              start = end;
2902              if (*start != '\0')
2903                start++;
2904            }
2905        }
2906      else
2907        if (ptr->child != NULL)
2908          printmenu(ptr->child, tabbing + 1);
2909    }
2910}
2911
2912
2913/************************************************************************
2914 *
2915 *  PrintMenu  --  call the printmenu proc above...  this proc is
2916 *      suitable for use in a procedure table, the other is not...
2917 *
2918 ************************************************************************/
2919void PrintMenu(me)
2920     MenuJet me;
2921{
2922  int tabbing;
2923  Menu *ptr;
2924
2925  tabbing = 0;
2926  ptr = me->menu.rootMenu;
2927
2928  printmenu(ptr, tabbing);
2929}
Note: See TracBrowser for help on using the repository browser.