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

Revision 13499, 69.5 KB checked in by danw, 25 years ago (diff)
fix things Irix n32 cc warns about
Line 
1/*
2 * $Id: Menu.c,v 1.3 1999-08-13 00:20:59 danw 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.3 1999-08-13 00:20:59 danw 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    }
2314}
2315
2316
2317
2318/************************************************************************
2319 *
2320 *  setMenu  --  I have no idea...
2321 *
2322 ************************************************************************/
2323static void setMenu(me, menu, newState, warp)
2324     MenuJet me;
2325     Menu *menu;
2326     int newState;
2327     Boolean warp;
2328{
2329  switch(newState)
2330    {
2331    case CLOSED:
2332      switch(menu->state)
2333        {
2334        case CLOSED:
2335          break;
2336        case OPENED:
2337          closeMenu(me, menu, warp);
2338          highlightMenu(me, menu, OPENED, me->menu.background_gc);
2339          break;
2340        case SELECTED:
2341          highlightMenu(me, menu, SELECTED, me->menu.background_gc);
2342          break;
2343        }
2344      menu->state = CLOSED;
2345      break;
2346
2347    case SELECTED:
2348      switch(menu->state)
2349        {
2350        case CLOSED:
2351          highlightMenu(me, menu, SELECTED,
2352                        (menu->activateProc && menu->supported) ?
2353                        me->menu.gc : me->menu.dot_gc);
2354          break;
2355        case OPENED:
2356          closeMenu(me, menu, warp);
2357          highlightMenu(me, menu, OPENED, me->menu.background_gc);
2358          highlightMenu(me, menu, SELECTED,
2359                        (menu->activateProc && menu->supported) ?
2360                        me->menu.gc : me->menu.dot_gc);
2361          break;
2362        case SELECTED:
2363          break;
2364        }
2365      menu->state = SELECTED;
2366      break;
2367
2368    case OPENED:
2369      switch(menu->state)
2370        {
2371        case CLOSED:
2372          highlightMenu(me, menu, OPENED, me->menu.dot_gc);
2373          openMenu(me, menu);
2374          break;
2375        case OPENED:
2376          break;
2377        case SELECTED:
2378          highlightMenu(me, menu, SELECTED, me->menu.background_gc);
2379          highlightMenu(me, menu, OPENED, me->menu.dot_gc);
2380          openMenu(me, menu);
2381          break;
2382        }
2383      menu->state = OPENED;
2384      break;
2385    }
2386}
2387
2388
2389
2390/************************************************************************
2391 *
2392 *  closeMenuAndAncestorsToLevel  --  close menu and ancestors up to
2393 *      level specified...
2394 *
2395 ************************************************************************/
2396static void closeMenuAndAncestorsToLevel(bar, start, end, warp)
2397     MenuJet bar;
2398     Menu *start, *end;
2399     Boolean warp;
2400{
2401  Menu *ptr;
2402
2403  for (ptr = start; ptr != end && ptr != end->parent; ptr = ptr->parent)
2404    setMenu(bar, ptr, CLOSED, warp);
2405  XFlush(bar->core.display);
2406}
2407
2408
2409/************************************************************************
2410 *
2411 *  drawBackground  --  draw background of menu window...
2412 *
2413 ************************************************************************/
2414static void drawBackground(me, menu)
2415     MenuJet me;
2416     Menu *menu;
2417{
2418  int width = menu->pane_width;
2419  int height = menu->pane_height;
2420
2421  if (me->core.window == menu->menuPane)
2422    width = me->core.width;
2423/*
2424  XFillRectangle(me->core.display, menu->menuPane,
2425                 me->menu.background_gc,
2426                 BORDER, BORDER,
2427                 width - 2 * BORDER - SHADOW,
2428                 height - 2 * BORDER - SHADOW);
2429*/
2430  XDrawRectangle(me->core.display, menu->menuPane,
2431                 me->menu.gc,
2432                 0, 0,
2433                 width - SHADOW - 1,
2434                 height - SHADOW - 1);
2435
2436  XDrawLine(me->core.display, menu->menuPane,
2437            me->menu.gc,
2438            BORDER, height - 1,
2439            width - 1, height - 1);
2440
2441  XDrawLine(me->core.display, menu->menuPane,
2442            me->menu.gc,
2443            width - 1, BORDER,
2444            width - 1, height - 1);
2445}
2446
2447
2448
2449/************************************************************************
2450 *
2451 *  drawPane  --  draw the items in a menu pane.
2452 *
2453 ************************************************************************/
2454static void drawPane(me, eventMenu)
2455     MenuJet me;
2456     Menu *eventMenu;
2457{
2458  Menu *m;
2459
2460  drawBackground(me, eventMenu);
2461
2462  if (eventMenu->paneType == HELP)
2463    drawHelp(me, eventMenu);
2464  else
2465    {
2466      if (eventMenu->orientation == VERTICAL)
2467        XDrawLine(me->core.display,
2468                  eventMenu->menuPane,
2469                  me->menu.dash_gc,
2470                  eventMenu->pane_open_x,
2471                  BORDER,
2472                  eventMenu->pane_open_x,
2473                  eventMenu->pane_height - BORDER);
2474
2475      for (m = eventMenu->child; m != NULL; m = m->sibling)
2476        {
2477          if (m->activateProc != NULL)
2478            XDrawString(me->core.display, eventMenu->menuPane,
2479                        m->supported ? me->menu.gc : me->menu.dim_gc,
2480                        m->label_x + me->menu.hMenuPadding / 2 - 10,
2481                        m->label_y + me->menu.vMenuPadding / 2 +
2482                        me->menu.font->ascent,
2483                        "*", 1);
2484
2485          XDrawString(me->core.display, eventMenu->menuPane,
2486                      m->supported ? me->menu.gc : me->menu.dim_gc,
2487                      m->label_x + me->menu.hMenuPadding / 2,
2488                      m->label_y + me->menu.vMenuPadding / 2 +
2489                      me->menu.font->ascent,
2490                      m->title, strlen(m->title));
2491
2492          switch(m->state)
2493            {
2494            case OPENED:
2495              highlightMenu(me, m, OPENED, me->menu.dot_gc);
2496              break;
2497
2498            case SELECTED:
2499              highlightMenu(me, m, SELECTED,
2500                            (m->activateProc && m->supported) ?
2501                            me->menu.gc : me->menu.dot_gc);
2502              break;
2503
2504            case CLOSED:
2505              break;
2506            }
2507
2508          if (eventMenu->orientation == VERTICAL)
2509            {
2510              if (m->paneType == HELP)
2511                {
2512                  if (me->menu.showHelp)
2513                    XCopyPlane(me->core.display,
2514                               me->menu.helpPixmap->pixmap,
2515                               eventMenu->menuPane,
2516                               me->menu.gc,
2517                               0, 0,
2518                               me->menu.helpPixmap->width,
2519                               me->menu.helpPixmap->height,
2520                               eventMenu->pane_open_x +
2521                               me->menu.hMenuPadding / 2,
2522                               m->label_y +
2523                               me->menu.vMenuPadding / 2,
2524                               1);
2525                }
2526              else
2527                if (m->child != NULL)
2528                  XCopyPlane(me->core.display,
2529                             me->menu.submenuPixmap->pixmap,
2530                             eventMenu->menuPane,
2531                             me->menu.gc,
2532                             0, 0,
2533                             me->menu.submenuPixmap->width,
2534                             me->menu.submenuPixmap->height,
2535                             eventMenu->pane_open_x +
2536                             me->menu.hMenuPadding / 2,
2537                             m->label_y +
2538                             me->menu.vMenuPadding / 2,
2539                             1);
2540            }
2541        }
2542    }
2543}
2544
2545
2546
2547/************************************************************************
2548 *
2549 *  event_handler  --  deal with all the x events that get thrown at us.
2550 *
2551 ************************************************************************/
2552static Boolean event_handler(me, event)
2553     MenuJet me;
2554     XEvent *event;
2555{
2556  MenuInfo info;
2557  Menu *eventMenu;
2558  Menu *m;
2559  int state, newState;
2560
2561  if (XCNOENT == XFindContext(me->core.display, event->xany.window,
2562                              context, (caddr_t *)&eventMenu))
2563    return False;
2564
2565  switch(event->type)
2566    {
2567    case GraphicsExpose:
2568    case Expose:
2569      if (DEBUG)
2570      fprintf(stderr, "Exposure\n");
2571
2572      if (event->xexpose.count != 0)
2573        break;
2574      if (eventMenu->menuPane != (Window) NULL)
2575        {
2576          drawPane(me, eventMenu);
2577        }
2578
2579      /* hack to support clock in menu bar */
2580      if (me->menu.right_jet)
2581        XjExpose(me->menu.right_jet, event);
2582#ifdef notdefined         
2583      if (me->core.sibling != NULL &&
2584          me->core.sibling->core.window == event->xany.window)
2585        XjExpose(me->core.sibling, event);
2586#endif
2587      break;
2588
2589    case ButtonRelease:
2590#ifdef DEBUGEVENTS
2591      fprintf(stderr, "ButtonRelease to %d (%d) %d\n",
2592              event->xbutton.button, me->menu.buttonDown - 1,
2593              event->xbutton.state);
2594#endif
2595
2596      findMenu(me, &m, &newState, me->menu.deepestOpened,
2597               event->xbutton.window,
2598               event->xbutton.x, event->xbutton.y);
2599
2600      if (me->menu.buttonDown == 1 &&
2601          (me->menu.deepestOpened->state == SELECTED ||
2602           m == NULL))
2603        {
2604          state = me->menu.deepestOpened->state;
2605          closeMenuAndAncestorsToLevel(me, me->menu.deepestOpened,
2606                                       me->menu.rootMenu, False);
2607
2608          if (me->menu.inside == True || me->menu.same == True)
2609            {
2610              info.menubar = me;
2611              /* there's potentially a bug here, but I don't want
2612                 to think about it right now. */
2613              if ((me->menu.deepestOpened->parent != NULL &&
2614                  me->menu.deepestOpened->parent->paneType == HELP) ||
2615                  state != SELECTED)
2616                info.menu = me->menu.deepestOpened->parent; /* NOP */
2617              else
2618                {
2619                  info.null = NULL;
2620                  info.menu = me->menu.deepestOpened;
2621                  if (me->menu.deepestOpened->supported &&
2622                      me->menu.deepestOpened->activateProc)
2623                    if (me->menu.verifyProc != NULL &&
2624                        me->menu.deepestOpened->verify)
2625                      XjCallCallbacks(&info,
2626                                      me->menu.verifyProc, NULL);
2627                    else
2628                      XjCallCallbacks(&info,
2629                                      me->menu.deepestOpened->activateProc,
2630                                      event);
2631                }
2632            }
2633
2634          me->menu.deepestOpened = me->menu.rootMenu;
2635
2636          me->menu.inside = False;
2637          me->menu.buttonDown = 0;
2638
2639          if (me->menu.rude == True)
2640            {
2641              if (me->menu.grabbed == True)
2642                XUngrabPointer(me->core.display, CurrentTime);
2643              me->menu.grabbed = False;
2644            }
2645        }
2646      else
2647        if (me->menu.buttonDown == 1)
2648          me->menu.buttonDown = 0;
2649
2650      break;
2651
2652    case ButtonPress:
2653      if (me->core.window == event->xany.window)
2654        {
2655          if (WindowVisibility(XjParent(me)) != VisibilityUnobscured)
2656            XRaiseWindow(me->core.display, me->core.window);
2657          else
2658            {
2659              Jet rj = me->menu.right_jet;
2660              if (rj
2661                  && ((event->xbutton.x > rj->core.x)
2662                      && (event->xbutton.x < rj->core.x + rj->core.width))
2663                  && ((event->xbutton.y > rj->core.y)
2664                      && (event->xbutton.y < rj->core.y + rj->core.height))
2665                  && rj->core.classRec->core_class.event)
2666                {
2667                  if (rj->core.classRec->core_class.event(rj, event))
2668                    break;
2669                }
2670            }
2671        }
2672
2673#ifdef DEBUGEVENTS
2674      fprintf(stderr, "ButtonPress to %d (%d) %d\n",
2675              event->xbutton.button, me->menu.buttonDown + 1,
2676              event->xbutton.state);
2677#endif
2678      if (me->menu.buttonDown == 0)
2679        {
2680          if (me->menu.rude == True &&
2681              me->menu.grabbed == False)
2682            {
2683              XGrabPointer(me->core.display,
2684                           me->core.window,
2685                           False,
2686                           ButtonPressMask |
2687                           ButtonReleaseMask |
2688                           ButtonMotionMask |
2689                           EnterWindowMask
2690                           /* | LeaveWindowMask */ ,
2691                           GrabModeAsync,
2692                           GrabModeAsync,
2693                           None,
2694                           None,
2695                           CurrentTime);
2696              me->menu.grabbed = True;
2697            }
2698          me->menu.buttonDown = 1;
2699        }
2700
2701/*      break; */
2702
2703    case MotionNotify:
2704/*
2705#ifdef DEBUGERVENTS
2706      fprintf(stderr, "MotionNotify: %d, %d; %d\n",
2707              event->xmotion.x, event->xmotion.y, event->xmotion.state);
2708#endif
2709*/
2710      if (me->menu.buttonDown == 0)
2711        break;
2712
2713      findMenu(me, &m, &newState, me->menu.deepestOpened,
2714               event->xmotion.window,
2715               event->xmotion.x, event->xmotion.y);
2716
2717      if (m == SAME)
2718        {
2719          me->menu.same = True;
2720          break;
2721        }
2722
2723      me->menu.same = False;
2724
2725      /* We're outside of all menus */
2726      if (m == NULL)
2727        {
2728          if (me->menu.inside == False)
2729            break; /* just moving around outside menus */
2730
2731          /* Here we've just moved outside of all menus */
2732          /* if (m == NULL && me->menu.inside == True) */
2733          setMenu(me, me->menu.deepestOpened, CLOSED, True);
2734          me->menu.deepestOpened =
2735            me->menu.deepestOpened->parent;
2736          me->menu.inside = False;
2737          break;
2738        }
2739
2740      /* m != NULL ... */
2741      if (me->menu.inside == False) /* just moved into something */
2742        {
2743          if (m == me->menu.deepestOpened) /* merge? */
2744            {
2745              if (m->state != newState)
2746                setMenu(me, m, newState, True);
2747
2748              me->menu.inside = True; /* this is an unlikely case */
2749              break;
2750            }
2751
2752          /* so we just moved into something that's not what's
2753             already opened */
2754          me->menu.inside = True;
2755        }
2756
2757      if (m == me->menu.deepestOpened) /* merge? */
2758        {
2759          if (m->state != newState)
2760            setMenu(me, m, newState, True);
2761         
2762          break;
2763        }
2764
2765      if (m->parent == me->menu.deepestOpened) /* opened a deeper child */
2766        {
2767          me->menu.deepestOpened = m;
2768          setMenu(me, me->menu.deepestOpened, newState, True);
2769          break;
2770        }
2771
2772      /*
2773       * Here we have moved into a sibling or parent of the deepest
2774       * child. We must close everything from the level of that
2775       * parent/sibling on down.
2776       */
2777      closeMenuAndAncestorsToLevel(me, me->menu.deepestOpened, m, True);
2778
2779      if (m->menuPane == (Window) NULL)
2780        setMenu(me, m, newState, True);
2781
2782      me->menu.deepestOpened = m;
2783
2784      break;
2785
2786    case CirculateNotify:
2787      if (DEBUG)
2788        printf("Menu - circ: x=%d, y=%d\n",
2789               event->xconfigure.x, event->xconfigure.y);
2790      break;
2791
2792    case CreateNotify:
2793      if (DEBUG)
2794        printf("Menu - create: x=%d, y=%d\n",
2795               event->xconfigure.x, event->xconfigure.y);
2796      break;
2797
2798    case ConfigureNotify:
2799      if (me->core.window == event->xany.window)
2800        {
2801          if (DEBUG)
2802            printf("Menu - config: x=%d, y=%d\n",
2803                   event->xconfigure.x, event->xconfigure.y);
2804          me->menu.rootMenu->x = event->xconfigure.x;
2805          me->menu.rootMenu->y = event->xconfigure.y;
2806        }
2807      break;
2808
2809    case DestroyNotify:
2810      XDeleteContext(me->core.display, event->xany.window, context);
2811      XjUnregisterWindow(event->xany.window, me);
2812      break;
2813
2814    case GravityNotify:
2815    case MapNotify:
2816    case ReparentNotify:
2817    case UnmapNotify:
2818      break;
2819
2820    case EnterNotify:
2821      if (me->menu.autoRaise &&
2822          me->core.window == event->xany.window &&
2823          WindowVisibility(XjParent(me)) != VisibilityUnobscured)
2824        XRaiseWindow(me->core.display, me->core.window);
2825      break;
2826
2827    case LeaveNotify:
2828      if (me->menu.buttonDown == 0)
2829        {
2830          Cursor cursor = XjCreateFontCursor(me->core.display,
2831                                             XC_ul_angle);
2832          XDefineCursor(me->core.display, me->core.window, cursor);
2833        }
2834
2835    default:
2836      return False;
2837    }
2838  return True;
2839}
2840
2841
2842
2843/************************************************************************
2844 *
2845 *  tab  --  space out...
2846 *
2847 ************************************************************************/
2848static void tab(n)
2849     int n;
2850{
2851  while (n--)
2852    fprintf(stdout, "    ");
2853}
2854
2855
2856/************************************************************************
2857 *
2858 *  printmenu  --  print out menu struct in a human-readable form.
2859 *
2860 ************************************************************************/
2861static void printmenu(first, tabbing)
2862     Menu *first;
2863     int tabbing;
2864{
2865  Menu *ptr;
2866  char *start, *end;
2867
2868  for (ptr = first; ptr != NULL; ptr = ptr->sibling)
2869    {
2870      tab(tabbing);
2871
2872      if (ptr->activateProc != NULL)
2873        fprintf(stdout, "*");
2874
2875      if (!strncmp("   ", ptr->title, 3))
2876        fprintf(stdout, "%s", ptr->title + 3);
2877      else if (!strncmp("* ", ptr->title, 2))
2878        fprintf(stdout, "%s", ptr->title + 2);
2879      else
2880        fprintf(stdout, "%s", ptr->title);
2881
2882      if (ptr->child != NULL)
2883        fprintf(stdout, ":\n");
2884      else
2885        fprintf(stdout, "\n");
2886
2887      if (ptr->paneType == HELP &&
2888          ptr->child != NULL)
2889        {
2890          start = ptr->child->title;
2891
2892          while (*start != '\0')
2893            {
2894              end = strchr(start, '\n');
2895              if (end == NULL)
2896                end = start + strlen(start);
2897              tab(tabbing + 1);
2898              fprintf(stdout, "%.*s\n", end - start, start);
2899              start = end;
2900              if (*start != '\0')
2901                start++;
2902            }
2903        }
2904      else
2905        if (ptr->child != NULL)
2906          printmenu(ptr->child, tabbing + 1);
2907    }
2908}
2909
2910
2911/************************************************************************
2912 *
2913 *  PrintMenu  --  call the printmenu proc above...  this proc is
2914 *      suitable for use in a procedure table, the other is not...
2915 *
2916 ************************************************************************/
2917void PrintMenu(me)
2918     MenuJet me;
2919{
2920  int tabbing;
2921  Menu *ptr;
2922
2923  tabbing = 0;
2924  ptr = me->menu.rootMenu;
2925
2926  printmenu(ptr, tabbing);
2927}
Note: See TracBrowser for help on using the repository browser.