earlybrowserreborn - Blame information for rev 4

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 roytam 1 /*LINTLIBRARY*/
2 /*
3  * xpa - X Pull-Aside menu
4  *
5  * David Harrison
6  * University of California, Berkeley
7  * 1989
8  *
9  * Native pull-aside menus for X11.  The inspiration for these menus
10  * come from those developed for the Xerox Dandelion under InterLisp.
11  * Pull-aside menus support arbitrarily deep menus by allowing the
12  * user to pull out new levels from the side of a menu choice.
13  * See xpa.doc for details about the interface.
14  */
15 #ifndef OCTTOOLS_COPYRIGHT_H
16 #define OCTTOOLS_COPYRIGHT_H
17 /*
18  * Oct Tools Distribution 3.0
19  *
20  * Copyright (c) 1988, 1989, Regents of the University of California.
21  * All rights reserved.
22  *
23  * Use and copying of this software and preparation of derivative works
24  * based upon this software are permitted.  However, any distribution of
25  * this software or derivative works must include the above copyright
26  * notice.
27  *
28  * This software is made available AS IS, and neither the Electronics
29  * Research Laboratory or the University of California make any
30  * warranty about the software, its performance or its conformity to
31  * any specification.
32  *
33  * Suggestions, comments, or improvements are welcome and should be
34  * addressed to:
35  *
36  *   octtools@eros.berkeley.edu
37  *   ..!ucbvax!eros!octtools
38  */
39  
40 #if !defined(lint) && !defined(SABER)
41 static char octtools_copyright[] = "Copyright (c) 1988, 1989, Regents of the University of California.  All rights reserved.";
42 #endif
43 #endif
44  
45 /*#include "defhell.h"*/
46 #include <stddef.h>
47 #include <stdio.h>
48 #include "xpa.h"
49  
50 /* there is a better way, right? */
51 #ifdef hpux
52 #include "X11/Xos.h"
53 #include "X11/Xutil.h"
54 #include "X11/cursorfont.h"
55 #else
56 #include "X11/Xos.h"
57 #include "X11/Xutil.h"
58 #include "X11/cursorfont.h"
59 #endif
60  
61 #define XPA_BORDER      1
62 #define XPA_PB_W        pb_sym_w
63 #define XPA_PB_H        pb_sym_h
64 #define XPA_PB_B        0
65 #define XPA_PB_SYM      XC_sb_right_arrow
66 #define XPA_PB_FONT     "cursor"
67 #ifdef NOTDEF
68 #define XPA_PB_SYM      '>'
69 #define XPA_PB_FONT     "6x10"
70 #endif
71 #define XPA_PB_PAD      1
72 #define XPA_HORPAD      4
73 #define XPA_VERPAD      1
74 #define XPA_ACBUFL      10
75  
76 #define XPA_MEM(type)           (type *) malloc(sizeof(type))
77 #define XPA_MEMX(type, num)     (type *) malloc((unsigned) (sizeof(type) * (num)))
78 #define XPA_SAVE(str)           strcpy(XPA_MEMX(char, strlen(str)+1), str)
79  
80 #define XPA_FREE(ptr)           free((char *) ptr)
4 roytam 81 /*
1 roytam 82 extern char *malloc();
83 extern char *realloc();
84 extern void free();
85 extern void exit();
4 roytam 86 */
1 roytam 87 static XContext xpa_xcon = (XContext) 0;
88 static void pb_draw();
89  
90 /* Size of pullbox symbol - set in set_pb_sym() */
91 static XFontStruct *pb_sym_font;
92 static int pb_sym_w;
93 static int pb_sym_h;
94 static XCharStruct pb_sym_metric;
95  
96  
97  
98 typedef enum xpa_type_defn {
99     PANE_T, ITEM_T, PB_T, TITLE_T
100 } xpa_type;
101  
102 /*
103  * All xpa_data structures share the first two fields as described below:
104  */
105 typedef struct xpa_any_defn {
106     xpa_type type;              /* Any type        */
107     Window window;              /* Primary window  */
108 } xpa_any;
109  
110 /*
111  * Panes have the following associated data
112  */
113 typedef struct xpa_pane_defn {
114     xpa_type type;              /* PANE_T             */
115     Window pane_win;            /* Window itself      */
116     int level;                  /* Level in hierarchy */
117 } xpa_pane;
118  
119 /*
120  * Items in a menu have the following associated data
121  */
122 typedef struct xpa_item_defn {
123     xpa_type type;              /* ITEM_T               */
124     Window item_win;            /* Window for item      */
125     char *name;                 /* Name to display      */
126     char key_buf[XPA_ACBUFL];   /* Accelerator key      */
127     int level;                  /* Level in hierarchy   */
128     int idx;                    /* Index in pane        */
129     int iw, ih;                 /* Size of item         */
130     int baseline;               /* Offset to baseline   */
131     int fromtop;                /* Distance from top    */
132     int key_off;                /* End of accelerator   */
133     Window sub_pane;            /* Posted subpane       */
134     struct xpa_pb_defn *pb;     /* Pull box info        */
135 } xpa_item;
136  
137 /*
138  * Pull-boxes have the following information
139  */
140 typedef struct xpa_pb_defn {
141     xpa_type type;              /* PB_T            */
142     Window pb_win;              /* Window for pb   */
143     xpa_item *item;             /* Associated item */
144     int height;                 /* Height of box   */
145 } xpa_pb;
146  
147 /*
148  * Pane titles have the following information
149  */
150 typedef struct xpa_title_defn {
151     xpa_type type;              /* TITLE_T              */
152     Window title_win;           /* Window for title     */
153     char *title_str;            /* String for title     */
154     XCharStruct t_metric;       /* Title string metric  */
155     int fullwidth;              /* Full width of window */
156     int level;                  /* Level in heirarchy   */
157 } xpa_title;
158  
159 /*
160  * Generic data stored in hash table
161  */
162 typedef union xpa_data_defn {
163     xpa_any  any;
164     xpa_pane pane;
165     xpa_item item;
166     xpa_pb   pb;
167     xpa_title title;
168 } xpa_data;
169  
170 /*
171  * Current menu state structure
172  */
173 typedef struct xpa_ms_defn {
174     int x, y;                   /* Pane location */
175     Window pane;                /* Pane itself   */
176     xpa_item *item;             /* Selected item */
177 } xpa_ms;
178  
179 /*
180  * xpa_menu is a hidden pointer to the following structure
181  */
182 typedef struct xpa_intern_defn {
183     Display *disp;              /* Connection to X server       */
184     struct xpa_context_defn *allwins;   /* Linked contexts for disposal */
185     int depth;                  /* Depth of menu                */
186     xpa_ms *state;              /* Menu state array (depth)     */
187     xpa_item **cur_items;       /* Currently selected items     */
188     xpa_appearance *attr;       /* Local menu attributes        */
189     Cursor cursor;              /* Menu cursor                  */
190     Window top;                 /* Top level window             */
191     int press_flag;             /* Whether to react on press    */
192     int width, height;          /*XXX*/
193 } xpa_intern;
194  
195 /*
196  * The following is stored in a hash table indexed by window.
197  * A general dispatch mechanism calls the function
198  * when an event happens in the window and passes the data
199  * as an argument.
200  */
201  
202 typedef struct xpa_context_defn {
203     xpa_data *data;             /* Call specific data     */
204     int (*callback)();          /* Callback function      */
205     xpa_intern *intern;         /* Internal menu info */
206     struct xpa_context_defn *next; /* Linked for disposal */
207 } xpa_context;
208  
209  
210  
211 /*
212  * Error handling
213  */
214  
215 char *xpa_error(code)
216 int code;                       /* Error code */
217 /* Returns a textual description for `code'. */
218 {
219     switch (code) {
220     case XPA_NOMEM:
221         return "out of memory";
222     case XPA_BADEVT:
223         return "received unexpected X event";
224     case XPA_NOIMPL:
225         return "function not implemented";
226     case XPA_TYPE:
227         return "type conflict";
228     case XPA_GRAB:
229         return "cannot grab mouse";
230     case XPA_NOTEVT:
231         return "encountered event not destined for xpa";
232     case XPA_BADSTAT:
233         return "bad status returned from X function";
234     case XPA_NOFONT:
235         return "unable to load necessary font";
236     case XPA_ZAPP:
237         return "null display attribute structure";
238     case XPA_BADCLR:
239         return "bad color specification";
240     default:
241         return "unknown error";
242     }
243 }
244  
245  
246 static void def_err_func(code)
247 int code;                       /* Error code */
248 /* Default error handler - prints a message and exits */
249 {
250     (void) fprintf(stderr, "Fatal error in xpa: %s\n", xpa_error(code));
251     abort();
252 }
253  
254 static void (*err_func)() = def_err_func;
255  
256 static void xpa_raise(code)
257 int code;                       /* Error code */
258 {
259     if (err_func) {
260         (*err_func)(code);
261     }
262 }
263  
264 void xpa_set_error(func)
265 void (*func)();                 /* New error function */
266 /*
267  * Sets the current xpa error function to `func'.  The function
268  * should have the following form:
269  *   void func(code)
270  *   int code;
271  * `code' will be one of the error codes given in xpa.h.  A
272  * textual representation is available by calling xpa_error().
273  * Specifying zero resets the function to the default.
274  */
275 {
276     if (func) {
277         err_func = func;
278     } else {
279         err_func = def_err_func;
280     }
281 }
282  
283  
284  
285 #define SPACE_ASCII     0040
286 #define DEL_ASCII       0177
287 #define META_ASCII      0200
288 #define AT_ASCII        0100
289  
290 static int key_string(buf, key)
291 char *buf;                      /* Destination buffer (at least XPA_ACBUFL) */
292 int key;                        /* ASCII key description                    */
293 /*
294  * Builds a readable description for the ASCII character `key'.  If
295  * it is a control character,  it is prefixed with a hat (^).  If
296  * it is above 0177,  it is prefixed with 'M-'.  The description
297  * is written into `buf' (which should be at least XPA_ACBUFL in length).
298  * The actual length is returned.  There are ASCII dependencies here.
299  */
300 {
301     int len = 0;
302  
303     if (key >= META_ASCII) {
304         while (key >= META_ASCII) key -= META_ASCII;
305         buf[len++] = 'M';
306         buf[len++] = '-';
307     }
308     if (key < SPACE_ASCII) {
309         /* Control key */
310         key += AT_ASCII;
311         buf[len++] = '^';
312     }
313     if (key == SPACE_ASCII) {
314         buf[len++] = 's';
315         buf[len++] = 'p';
316         buf[len++] = 'c';
317     } else if (key == DEL_ASCII) {
318         buf[len++] = 'd';
319         buf[len++] = 'e';
320         buf[len++] = 'l';
321     } else {
322         buf[len++] = (char) key;
323     }
324     buf[len] = '\0';
325     return len;
326 }
327  
328  
329  
330 static void set_pb_sym(disp)
331 Display *disp;
332 /*
333  * Loads the XPA_PB_FONT font.  Determines the size of the XPA_PB_SYM
334  * character.  Sets these up in globals pb_sym_font, pb_sym_w, pb_sym_h.
335  */
336 {
337     char ch[4];
338     int dir, ascent, descent;
339  
340     if (!(pb_sym_font = XLoadQueryFont(disp, XPA_PB_FONT))) {
341         /* Could try to recover in the future */
342         xpa_raise(XPA_NOFONT);
343     }
344     ch[0] = XPA_PB_SYM;
345     XTextExtents(pb_sym_font, ch, 1, &dir, &ascent, &descent, &pb_sym_metric);
346     pb_sym_w = pb_sym_metric.rbearing - pb_sym_metric.lbearing + 2*XPA_PB_PAD;
347     pb_sym_h = pb_sym_metric.ascent + pb_sym_metric.descent + 2*XPA_PB_PAD;
348 }
349  
350  
351 /*
352  * Allocator functions for each context table type
353  */
354  
355 static xpa_pane *new_pane(win, level)
356 Window win;                     /* Pane window        */
357 int level;                      /* Level in heirarchy */
358 /* Creates a new pane structure */
359 {
360     xpa_pane *result = XPA_MEM(xpa_pane);
361  
362     if (!result) xpa_raise(XPA_NOMEM);
363     result->type = PANE_T;
364     result->pane_win = win;
365     result->level = level;
366     return result;
367 }
368  
369 static xpa_item *new_item(win, name, key_idx, level, idx, iw, ih, baseline,
370                           fromtop, key_off, sub_pane, pb)
371 Window win;                     /* Item window              */
372 char *name;                     /* Name of item             */
373 int key_idx;                    /* Accelerator key          */
374 int level;                      /* Level in hierarchy       */
375 int idx;                        /* Offset in pane           */
376 int iw, ih;                     /* Physical size            */
377 int baseline;                   /* Offset to baseline       */
378 int fromtop;                    /* Distance from top        */
379 int key_off;                    /* End of accelerator       */
380 Window sub_pane;                /* Posted sub-pane (if any) */
381 xpa_pb *pb;                     /* Pullbox info             */
382 /*
383  * Creates a new item structure - automatically copies `name'.
384  * Automatically expands `key_idx' into a textual representation.
385  */
386 {
387     xpa_item *result = XPA_MEM(xpa_item);
388  
389     if (!result) xpa_raise(XPA_NOMEM);
390     result->type = ITEM_T;
391     result->item_win = win;
392     result->name = XPA_SAVE(name);
393     if (key_idx > XPA_NOCHAR) {
394         (void) key_string(result->key_buf, key_idx);
395     } else {
396         result->key_buf[0] = '\0';
397     }
398     result->level = level;
399     result->idx = idx;
400     result->iw = iw;
401     result->ih = ih;
402     result->baseline = baseline;
403     result->fromtop = fromtop;
404     result->key_off = key_off;
405     result->sub_pane = sub_pane;
406     result->pb = pb;
407     return result;
408 }
409  
410 static xpa_pb *new_pb(win, item, height)
411 Window win;                     /* Pullbox window  */
412 xpa_item *item;                 /* Associated item */
413 int height;                     /* Height of box   */
414 /* Creates a new pullbox structure */
415 {
416     xpa_pb *result = XPA_MEM(xpa_pb);
417  
418     if (!result) xpa_raise(XPA_NOMEM);
419     result->type = PB_T;
420     result->pb_win = win;
421     result->item = item;
422     result->height = height;
423     return result;
424 }
425  
426 static xpa_title *new_title(win, str, metric, fullwidth, lev)
427 Window win;                     /* Title window  */
428 char *str;                      /* Title string  */
429 XCharStruct *metric;            /* String metric */
430 int fullwidth;                  /* Full width    */
431 int lev;                        /* Hierarchy lev */
432 /* Creates a new title structure (string is automatically copied) */
433 {
434     xpa_title *result = XPA_MEM(xpa_title);
435  
436     if (!result) xpa_raise(XPA_NOMEM);
437     result->type = TITLE_T;
438     result->title_win = win;
439     result->title_str = XPA_SAVE(str);
440     result->t_metric = *metric;
441     result->fullwidth = fullwidth;
442     result->level = lev;
443     return result;
444 }
445  
446  
447 #ifdef DEBUG
448 static void out_menu(title, entrys, indent)
449 char *title;
450 xpa_entry *entrys;
451 int indent;
452 /* For debugging */
453 {
454     int i;
455  
456     if (title) {
457         for (i = 0;  i < indent-1;  i++) putchar(' ');
458         (void) printf("%s (pane name)\n", title);
459     }
460     while (entrys->item_name) {
461         for (i = 0;  i < indent;  i++) putchar(' ');
462         if (entrys->key_char > 0) {
463             (void) printf("%s (%d=%c)\n", entrys->item_name,
464                           entrys->key_char, entrys->key_char);
465         } else {
466             (void) printf("%s\n", entrys->item_name);
467         }
468         if (entrys->sub_entrys) {
469             out_menu(entrys->sub_title, (xpa_entrys) entrys->sub_entrys, indent+3);
470         }
471         entrys++;
472     }
473 }
474  
475 static void out_detail(evt, str)
476 XEvent *evt;
477 char *str;
478 /* Shows detail of leave event */
479 {
480     (void) fputs(str, stdout);
481     switch (evt->xcrossing.detail) {
482     case NotifyAncestor:
483         (void) printf("%s\n", "NotifyAncestor");
484         break;
485     case NotifyVirtual:
486         (void) printf("%s\n", "NotifyVirtual");
487         break;
488     case NotifyInferior:
489         (void) printf("%s\n", "NotifyInferior");
490         break;
491     case NotifyNonlinear:
492         (void) printf("%s\n", "NotifyNonlinear");
493         break;
494     case NotifyNonlinearVirtual:
495         (void) printf("%s\n", "NotifyNonlinearVirtual");
496         break;
497     }
498 }
499 #endif
500  
501 /*
502  * Handle graphics contexts
503  */
504  
505 static GC fg_gc(disp, win, font, fg)
506 Display *disp;                  /* X Connection       */
507 Window win;                     /* What window to use */
508 XFontStruct *font;              /* Font to use        */
509 unsigned long fg;               /* Foreground color   */
510 /*
511  * Creates or modifies a static graphics context to have the
512  * specified values.  This one is generally used for drawing
513  * text.
514  */
515 {
516     static GC gc = (GC) 0;
517     XGCValues gcvals;
518  
519     gcvals.foreground = fg;
520     gcvals.font = font->fid;
521     if (gc) {
522         XChangeGC(disp, gc, GCForeground|GCFont, &gcvals);
523     } else {
524         gc = XCreateGC(disp, win, GCForeground|GCFont, &gcvals);
525     }
526     return gc;
527 }
528  
529 static GC bg_gc(disp, win, bg)
530 Display *disp;                  /* X Connection       */
531 Window win;                     /* What window to use */
532 unsigned long bg;               /* Background color   */
533 /*
534  * Creates or modifies a static graphics context to have the
535  * specified values.  This one is generally used for filling
536  * areas.
537  */
538 {
539     static GC gc = (GC) 0;
540     XGCValues gcvals;
541  
542     gcvals.foreground = bg;
543     if (gc) {
544         XChangeGC(disp, gc, GCForeground, &gcvals);
545     } else {
546         gc = XCreateGC(disp, win, GCForeground, &gcvals);
547     }
548     return gc;
549 }
550  
551  
552 static GC bd_gc(disp, win, bd)
553 Display *disp;                  /* X Connection       */
554 Window win;                     /* What window to use */
555 unsigned long bd;               /* Border color   */
556 /*
557  * Creates or modifies a static graphics context to have the
558  * specified values.  This one is generally used for filling
559  * areas.
560  */
561 {
562     static GC gc = (GC) 0;
563     XGCValues gcvals;
564  
565     gcvals.foreground = bd;
566     if (gc) {
567         XChangeGC(disp, gc, GCForeground, &gcvals);
568     } else {
569         gc = XCreateGC(disp, win, GCForeground, &gcvals);
570     }
571     return gc;
572 }
573  
574  
575 static GC cr_gc(disp, win, cr)
576 Display *disp;                  /* X Connection       */
577 Window win;                     /* What window to use */
578 unsigned long cr;               /* Border color   */
579 /*
580  * Creates or modifies a static graphics context to have the
581  * specified values.  This one is generally used for filling
582  * areas.
583  */
584 {
585     static GC gc = (GC) 0;
586     XGCValues gcvals;
587  
588     gcvals.foreground = cr;
589     if (gc) {
590         XChangeGC(disp, gc, GCForeground, &gcvals);
591     } else {
592         gc = XCreateGC(disp, win, GCForeground, &gcvals);
593     }
594     return gc;
595 }
596  
597  
598 static int depth(entrys)
599 xpa_entry *entrys;
600 /*
601  * Examines menu specification and returns the maximum depth.
602  */
603 {
604     xpa_entry *idx;
605     int max, d;
606  
607     max = 0;
608     for (idx = entrys;  idx->item_name;  idx++) {
609         if (idx->sub_entrys) {
610             d = depth((xpa_entry *) idx->sub_entrys);
611             if (d > max) max = d;
612         }
613     }
614     return max+1;
615 }
616  
617  
618  
619 static void dispatch(win, intern, data, callback)
620 Window win;                     /* Window id             */
621 xpa_intern *intern;             /* Internal view of menu */
622 xpa_data *data;                 /* Data itself           */
623 int (*callback)();              /* Function to call      */
624 /*
625  * This routine adds a new item to the context table of
626  * menu `intern'.  The `data' and `callback' information
627  * is added under the `win' identifier.
628  */
629 {
630     xpa_context *new_context;
631  
632     if (!(new_context = XPA_MEM(xpa_context))) xpa_raise(XPA_NOMEM);
633     new_context->data = data;
634     new_context->callback = callback;
635     new_context->intern = intern;
636     new_context->next = intern->allwins;
637     intern->allwins = new_context;
638     if (XSaveContext(intern->disp, win, xpa_xcon, (caddr_t) new_context)) {
639         xpa_raise(XPA_NOMEM);
640     }
641 }
642  
643  
644 static void item_draw(intern, item)
645 xpa_intern *intern;             /* Internal view of menu */
646 xpa_item *item;                 /* Item to turn on       */
647 /*
648  * Draws the specified item.  Checks to see if it is currently
649  * selected.  If so,  the window is redrawn highlighted.  Now
650  * draws accelerator key against the left margin and the
651  * text at the key offset for the item.
652  */
653 {
654     if (intern->state[item->level].item == item) {
655         /* Selected */
656 /*
657         XFillRectangle(intern->disp, item->item_win,
658                        cr_gc(intern->disp, item->item_win,
659                              intern->attr->cur_fg),
660                        0, 0, item->iw, item->ih);
661 */
662 /*      XDrawRectangle(intern->disp, item->item_win,
663                        cr_gc(intern->disp, item->item_win,
664                              intern->attr->cur_fg),
665                        1, 1, item->iw-3, item->ih-3);
666  
667 */
668         XDrawRectangle(intern->disp, item->item_win,
669                        cr_gc(intern->disp, item->item_win,
670                              intern->attr->cur_fg),
671                        0, 0, item->iw-1, item->ih-1);
672  
673         if (item->key_buf[0]) {
674             XDrawString(intern->disp, item->item_win,
675                         fg_gc(intern->disp, item->item_win,
676                               intern->attr->key_font,
677                               intern->attr->item_fg),
678                         XPA_HORPAD, item->baseline,
679                         item->key_buf, strlen(item->key_buf));
680         }
681         XDrawString(intern->disp, item->item_win,
682                     fg_gc(intern->disp, item->item_win,
683                           intern->attr->item_font,
684                           intern->attr->item_fg),
685                     item->key_off+XPA_HORPAD, item->baseline,
686                     item->name, strlen(item->name));
687     } else {
688         /* Not selected */
689         XFillRectangle(intern->disp, item->item_win,
690                        bg_gc(intern->disp, item->item_win,
691                              intern->attr->item_bg),
692                        0, 0, item->iw, item->ih);
693         if (item->key_buf[0]) {
694             XDrawString(intern->disp, item->item_win,
695                         fg_gc(intern->disp, item->item_win,
696                               intern->attr->key_font,
697                               intern->attr->item_fg),
698                         XPA_HORPAD, item->baseline,
699                         item->key_buf, strlen(item->key_buf));
700         }
701         XDrawString(intern->disp, item->item_win,
702                     fg_gc(intern->disp, item->item_win,
703                           intern->attr->item_font,
704                           intern->attr->item_fg),
705                     item->key_off+XPA_HORPAD, item->baseline,
706                     item->name, strlen(item->name));
707     }
708 }
709  
710  
711  
712 static void item_off(intern, off_item)
713 xpa_intern *intern;             /* Internal view of menu */
714 xpa_item *off_item;             /* Item itself           */
715 /*
716  * Turns off a given item.  This includes unmapping all subpanes
717  * of the item (if any), turning off the hightlighting of the
718  * item,  and zeroing out the selection in the state field
719  * of `intern'.
720  */
721 {
722     int i;
723  
724     /* Turn off all subitems (outside to inside) */
725     for (i = intern->depth-1;  i > off_item->level;  i--) {
726         if (intern->state[i].pane) {
727             XUnmapWindow(intern->disp, intern->state[i].pane);
728         }
729         intern->state[i].pane = (Window) 0;
730         intern->state[i].item = (xpa_item *) 0;
731     }
732     /* Remove this item from state */
733     intern->state[off_item->level].item = (xpa_item *) 0;
734     /* Redraw the item */
735     item_draw(intern, off_item);
736     /* Redraw pullbox */
737     if (off_item->pb) pb_draw(intern, off_item->pb);
738 }
739  
740  
741  
742 static void item_on(intern, on_item)
743 xpa_intern *intern;             /* Internal view of menu */
744 xpa_item *on_item;              /* Item to turn on       */
745 /*
746  * Turns on the specified item.  This includes setting the
747  * appropriate pointer in the state field of `intern'
748  * and actually redrawing the item.
749  */
750 {
751     /* Turn on the selection of this item */
752     intern->state[on_item->level].item = on_item;
753     /* Redraw item */
754     item_draw(intern, on_item);
755     /* Redraw pullbox */
756     if (on_item->pb) pb_draw(intern, on_item->pb);
757 }
758  
759  
760 static void pane_on(intern, pane, lev, x, y, item)
761 xpa_intern *intern;             /* Internal view of menu */
762 Window pane;                    /* What pane             */
763 int lev;                        /* Level in heirarchy    */
764 int x;                          /* Left edge of pane     */
765 int y;                          /* Location of `item'    */
766 xpa_item *item;                 /* Item in pane          */
767 /*
768  * This routine posts the pane `pane'.  The left edge of the
769  * pane will appear at `x' (relative to the root window).
770  * The baseline of `item' will be aligned with `y'.  If
771  * `item' is zero, either the title or the first item will
772  * be aligned with `y' (depending on whether the pane has
773  * a title).  Note: should compensate for the screen as well.
774  */
775 {
776     int true_y, width, height;
777     extern int displayWidth;
778     extern int displayHeight;
779  
780     if (item) {
781         true_y = y - item->fromtop;
782     } else {
783         /* Should be a test for title */
784         true_y = y - intern->attr->item_font->ascent;
785     }
786     width = intern->width;
787     height = intern->height;
788  
789     if (x + width > displayWidth)
790       x = displayWidth - width;
791     if (true_y + height > displayHeight)
792       true_y = displayHeight - height;
793  
794     intern->state[lev].x = x;
795     intern->state[lev].y = true_y;
796     intern->state[lev].pane = pane;
797     XMoveWindow(intern->disp, pane, x, true_y);
798     XRaiseWindow(intern->disp, pane);
799     XMapWindow(intern->disp, pane);
800 }
801  
802  
803 static void pb_draw(intern, pb)
804 xpa_intern *intern;             /* Internal view of menu */
805 xpa_pb *pb;                     /* Item to turn on       */
806 /*
807  * Draws the specified pullbox.  A small arrow is drawn using
808  * the cursor font and a special character.  Eventually
809  * should recover if there isn't a cursor font.
810  */
811 {
812     char ch[4];
813     int ox, oy;
814  
815     ch[0] = XPA_PB_SYM;
816     ox = XPA_PB_PAD - pb_sym_metric.lbearing;
817     oy = pb->item->baseline - pb_sym_metric.descent - 2;
818     if (intern->state[pb->item->level].item == pb->item) {
819         /* Selected */
820 /*
821         XFillRectangle(intern->disp, pb->pb_win,
822                        cr_gc(intern->disp, pb->pb_win,
823                              intern->attr->cur_fg),
824                        0, 0, XPA_PB_W, pb->height);
825         XDrawString(intern->disp, pb->pb_win,
826                     fg_gc(intern->disp, pb->pb_win,
827                           pb_sym_font, intern->attr->item_bg),
828                     ox, oy,
829                     ch, 1);
830 */
831     } else {
832         /* Not selected */
833 /*      XFillRectangle(intern->disp, pb->pb_win,
834                        bg_gc(intern->disp, pb->pb_win,
835                              intern->attr->item_bg),
836                        1, 0, XPA_PB_W, pb->height-2);
837 */
838         XDrawString(intern->disp, pb->pb_win,
839                     fg_gc(intern->disp, pb->pb_win,
840                           pb_sym_font, intern->attr->item_fg),
841                     ox, oy,
842                     ch, 1);
843     }
844 }
845  
846  
847  
848 static void title_draw(intern, title)
849 xpa_intern *intern;             /* Internal view of menu */
850 xpa_title *title;               /* Title to draw         */
851 /*
852  * Draws the given title.  Since the background is set to
853  * the title background,  all that is required is that
854  * the string be rendered.
855  */
856 {
857     int x;
858  
859 #ifdef OLD
860     x = (title->fullwidth -
861          (title->t_metric.rbearing - title->t_metric.lbearing))/2 -
862            title->t_metric.lbearing;
863 #endif
864     x = (title->fullwidth-title->t_metric.rbearing-title->t_metric.lbearing)/2;
865  
866     XDrawString(intern->disp, title->title_win,
867                 fg_gc(intern->disp, title->title_win,
868                       intern->attr->title_font,
869                       intern->attr->title_fg),
870                 x, intern->attr->title_font->ascent,
871                 title->title_str, strlen(title->title_str));
872 }
873  
874  
875 /*
876  * Callback functions begin on this page.
877  */
878  
879 static int cb_pane(evt, intern, data)
880 XEvent *evt;                    /* X Event causing callback */
881 xpa_intern *intern;             /* Internal view of menu    */
882 xpa_data *data;                 /* Specific instance data   */
883 /*
884  * This function is called when an event occurs on a pane.  Right
885  * now,  only a leave event is recognized.  The data is (xpa_pane *).
886  * If there isn't a sub-pane posted,  it will turn off the currently
887  * selected item.
888  */
889 {
890     int rv;
891  
892     if (data->any.type != PANE_T) xpa_raise(XPA_TYPE);
893     switch (evt->type) {
894     case LeaveNotify:
895         if ((evt->xcrossing.mode == NotifyNormal) &&
896             ((evt->xcrossing.detail == NotifyNonlinear) ||
897              (evt->xcrossing.detail == NotifyNonlinearVirtual))) {
898             if (intern->state[data->pane.level].item &&
899                 ((data->pane.level >= intern->depth-1) ||
900                  (!(intern->state[data->pane.level+1].pane)))) {
901                 /* Turn it off */
902                 item_off(intern, intern->state[data->pane.level].item);
903             }
904         }
905         rv = 0;
906         break;
907     case ButtonPress:
908         /* Only if press_flag is on */
909         rv = intern->press_flag;
910         break;
911     case ButtonRelease:
912         /* Only if press_flag is off */
913         rv = intern->press_flag == 0;
914         {
915                 extern int mouseButtonPressedState;
916                 mouseButtonPressedState &=
917                         ~(1<<(((XButtonEvent*)evt)->button));
918         }
919         break;
920     default:
921         xpa_raise(XPA_BADEVT);
922     }
923     return rv;
924 }
925  
926  
927  
928 static int cb_item(evt, intern, data)
929 XEvent *evt;                    /* X Event causing callback */
930 xpa_intern *intern;             /* Internal view of menu    */
931 xpa_data *data;                 /* Specific instance data   */
932 /*
933  * This function is called when an event occurs in an item.
934  * Two events are processed: entry and exposure.  Upon entry,
935  * any other selected item is turned off on its level.  The
936  * selected item is then turned on.  Exposure simply causes
937  * the item to be redrawn.
938  */
939 {
940     if (data->any.type != ITEM_T) xpa_raise(XPA_TYPE);
941     switch (evt->type) {
942     case EnterNotify:
943         /* Turn off current selection on this level (if any) */
944         if (intern->state[data->item.level].item) {
945             item_off(intern, intern->state[data->item.level].item);
946         }
947         /* Turn on this item */
948         item_on(intern, (xpa_item *) data);
949         break;
950     case Expose:
951         if (evt->xexpose.count == 0) {
952             item_draw(intern, (xpa_item *) data);
953         }
954         break;
955     default:
956         xpa_raise(XPA_BADEVT);
957     }
958     return 0;
959 }
960  
961  
962 static int cb_pb(evt, intern, data)
963 XEvent *evt;                    /* X Event causing callback */
964 xpa_intern *intern;             /* Internal view of menu    */
965 xpa_data *data;                 /* Specific instance data   */
966 /*
967  * This function is called when events occur on the
968  * pullbox.  Entering this window posts the subpane of
969  * the associated item.  Exposure draws a small arrow
970  * in the box.
971  */
972 {
973     if (data->any.type != PB_T) xpa_raise(XPA_TYPE);
974     switch (evt->type) {
975     case EnterNotify:
976         /* Post subpane */
977         if (evt->xcrossing.detail == NotifyAncestor) {
978             pane_on(intern, data->pb.item->sub_pane,
979                     data->pb.item->level+1,
980                     intern->state[data->pb.item->level].x +
981                     data->pb.item->iw - XPA_HORPAD - XPA_PB_W,
982                     intern->state[data->pb.item->level].y +
983                     data->pb.item->fromtop, (xpa_item *) 0);
984         }
985         break;
986     case Expose:
987         if (evt->xexpose.count == 0) {
988             pb_draw(intern, (xpa_pb *) data);
989         }
990         break;
991     default:
992         xpa_raise(XPA_BADEVT);
993     }
994     return 0;
995 }
996  
997  
998  
999 static int cb_title(evt, intern, data)
1000 XEvent *evt;                    /* X Event causing callback */
1001 xpa_intern *intern;             /* Internal view of menu    */
1002 xpa_data *data;                 /* Specific instance data   */
1003 /*
1004  * This function is called when events occur in a title.  Entering
1005  * this item turns off the currently selected item.  Exposure redraws
1006  * the item.
1007  */
1008 {
1009     if (data->any.type != TITLE_T) xpa_raise(XPA_TYPE);
1010     switch (evt->type) {
1011     case EnterNotify:
1012         /* Turn off current selection on this level (if any) */
1013         if (intern->state[data->title.level].item) {
1014             item_off(intern, intern->state[data->title.level].item);
1015         }
1016         break;
1017     case Expose:
1018         if (evt->xexpose.count == 0) {
1019             title_draw(intern, (xpa_title *) data);
1020         }
1021         break;
1022     default:
1023         xpa_raise(XPA_BADEVT);
1024     }
1025     return 0;
1026 }
1027  
1028  
1029  
1030 #define SAVEUNDER       0x01
1031 #define OVERRIDE        0x02
1032  
1033 static Window make_window(disp, parent, x, y, width, height,
1034                           bwidth, bor, bg, cur, events, options)
1035 Display *disp;                  /* X Connection  */
1036 Window parent;                  /* Parent window */
1037 int x, y;                       /* Location      */
1038 int width, height;              /* Size          */
1039 int bwidth;                     /* Border width  */
1040 unsigned long bor, bg;          /* Border and background pixels */
1041 Cursor cur;                     /* Window cursor (or None)      */
1042 long events;                    /* Interesting events           */
1043 int options;                    /* SAVEUNDER, OVERRIDE          */
1044 /*
1045  * Makes a new window and returns its handle.  Basically,  this
1046  * is a convenient way to call XCreateWindow.  Saveunder will be
1047  * used if `options' contains SAVEUNDER.  Override-redirect will
1048  * be used if `options' contains OVERRIDE.
1049  */
1050 {
1051     XSetWindowAttributes attr;
1052     unsigned long wamask;
1053  
1054     attr.background_pixel = bg;
1055     attr.border_pixel = bor;
1056     attr.save_under = (options & SAVEUNDER) != 0;
1057     attr.override_redirect = (options & OVERRIDE) != 0;
1058     attr.event_mask = events;
1059     attr.cursor = cur;
1060     wamask = CWBackPixel|CWBorderPixel|CWSaveUnder|CWOverrideRedirect|
1061       CWEventMask|CWCursor;
1062  
1063     return XCreateWindow(disp, parent, x, y, width, height, bwidth,
1064                          CopyFromParent, InputOutput, CopyFromParent,
1065                          wamask, &attr);
1066 }
1067  
1068  
1069 static void item_size(font, name, sub_flag, ac_font, ac_key, rw, rh, basel, ac_off)
1070 XFontStruct *font;              /* Font for display     */
1071 char *name;                     /* Name of item         */
1072 int sub_flag;                   /* Does it have submenu */
1073 XFontStruct *ac_font;           /* Accelerator font     */
1074 int ac_key;                     /* Accelerator key      */
1075 int *rw, *rh;                   /* Returned size        */
1076 int *basel;                     /* Returned baseline    */
1077 int *ac_off;                    /* End of accelerator   */
1078 /*
1079  * Computes the minimal size for an item given its font, name,
1080  * whether it has a submenu, and the key and font of the character
1081  * accelerator.  `ac_off' gives the offset of the end of the accelerator
1082  * in pixels.  Note that the returned width DOES NOT include the
1083  * accelerator space.
1084  */
1085 {
1086     char buf[XPA_ACBUFL];
1087     int len, largest;
1088  
1089     if (ac_key > XPA_NOCHAR) {
1090         len = key_string(buf, ac_key);
1091         *ac_off = XPA_HORPAD + XTextWidth(ac_font, buf, len);
1092     } else {
1093         *ac_off = 0;
1094     }
1095     *rw = 2*XPA_HORPAD + XTextWidth(font, name, strlen(name));
1096     if (sub_flag) {
1097         *rw += XPA_HORPAD + XPA_PB_W;
1098     }
1099     largest = 0;
1100     if (font->ascent > largest) largest = font->ascent;
1101     if (ac_font->ascent > largest) largest = ac_font->ascent;
1102     if (XPA_PB_H > largest) largest = XPA_PB_H;
1103     *basel = largest + XPA_VERPAD;
1104  
1105     largest = 0;
1106     if (font->descent > largest) largest = font->descent;
1107     if (ac_font->descent > largest) largest = ac_font->descent;
1108     *rh = *basel + largest + XPA_VERPAD;
1109 }
1110  
1111 static void title_size(font, title, rw, rh)
1112 XFontStruct *font;              /* Font for display */
1113 char *title;                    /* Title itself     */
1114 int *rw, *rh;                   /* Returned size    */
1115 /*
1116  * Computes a suitable bounding box for `title'.
1117  */
1118 {
1119     *rw = 2 * XPA_HORPAD + XTextWidth(font, title, strlen(title));
1120     *rh = 2 * XPA_VERPAD + font->ascent + font->descent;
1121 }
1122  
1123  
1124  
1125 static struct xpa_pb_defn *make_pullbox(intern, parent, item, x, height)
1126 xpa_intern *intern;             /* Internal view of menu */
1127 Window parent;                  /* Parent of pullbox     */
1128 xpa_item *item;                 /* Associated item       */
1129 int x;                          /* Horizontal location   */
1130 int height;                     /* Height of pullbox     */
1131 /*
1132  * Creates a new window under `parent' for the pullbox.  This
1133  * window has no border and its background is an arrow bitmap.
1134  * It has an associated callback that posts a sub-pane for
1135  * an item.
1136  */
1137 {
1138     Window pullbox;
1139     xpa_pb *pb;
1140  
1141     pullbox = make_window(intern->disp, parent, x, 1,
1142                           XPA_PB_W, height, XPA_PB_B,
1143                           intern->attr->border_bd, intern->attr->item_bg,
1144                           (Cursor) None, EnterWindowMask | ExposureMask, 0);
1145     pb = new_pb(pullbox, item, height);
1146     dispatch(pullbox, intern, (xpa_data *) pb, cb_pb);
1147     XMapWindow(intern->disp, pullbox);
1148     return (struct xpa_pb_defn *) pb;
1149 }
1150  
1151  
1152  
1153 static int make_title(intern, parent, title_str, lev, width, yspot)
1154 xpa_intern *intern;             /* Internval view of menu */
1155 Window parent;                  /* Parent window          */
1156 char *title_str;                /* Title string           */
1157 int lev;                        /* Level in Hierarchy     */
1158 int width;                      /* Width of item          */
1159 int yspot;                      /* Y location             */
1160 /*
1161  * Creates a subwindow of `parent' that displays a the title
1162  * string `title_str'.  `width' specifies the width of the
1163  * item and `yspot' specifies the y location.  The appropriate
1164  * callback will be added to the dispatch table.  The height
1165  * of the new item will be returned.
1166  */
1167 {
1168     Window title_win;
1169     XCharStruct metric;
1170     int dir, ascent, descent;
1171     int tw, th;
1172  
1173     title_size(intern->attr->title_font, title_str, &tw, &th);
1174     title_win = make_window(intern->disp, parent, 0, yspot,
1175                             width, th, 0, intern->attr->border_bd,
1176                             intern->attr->title_bg, (Cursor) None,
1177                             EnterWindowMask | ExposureMask, 0);
1178     XTextExtents(intern->attr->title_font, title_str, strlen(title_str),
1179                  &dir, &ascent, &descent, &metric);
1180     dispatch(title_win, intern,
1181              (xpa_data *) new_title(title_win, title_str, &metric, width, lev),
1182              cb_title);
1183     XMapWindow(intern->disp, title_win);
1184     return th;
1185 }
1186  
1187  
1188  
1189 /* Forward declaration */
1190 static Window make_pane();
1191  
1192 static int make_item(intern, parent, entry, lev, idx, width, yspot, key_off)
1193 xpa_intern *intern;             /* Internal view of menu */
1194 Window parent;                  /* Parent window         */
1195 xpa_entry *entry;               /* Entry itself          */
1196 int lev;                        /* Level in heirarchy    */
1197 int idx;                        /* Numerical index       */
1198 int width;                      /* Width of item         */
1199 int yspot;                      /* Y location            */
1200 int key_off;                    /* End of accelerator    */
1201 /*
1202  * Creates a subwindow of `parent' that displays the item
1203  * `entry'.  The width of the item is given by `width'
1204  * and its y location is given by `yspot'.  If the item
1205  * has a sub-pane,  it will be created as well.  The window
1206  * will be added to the dispatch table.  The height of
1207  * the new item will be returned.  The expose routine
1208  * for the item will draw the accelerator key (if any)
1209  * at the left margin.  The text itself will be drawn
1210  * at `key_off'.
1211  */
1212 {
1213     Window item_win, sub_win;
1214     xpa_item *item;
1215     int exp_w, exp_h, bl, off;
1216  
1217     if (entry->item_type == XPA_ITEM_TYPE_ENTRY) {
1218             /* Called only to determine height of item */
1219             item_size(intern->attr->item_font, entry->item_name,
1220                       (entry->sub_entrys != (xpa_entry *) 0),
1221                       intern->attr->key_font, entry->key_char,
1222                       &exp_w, &exp_h, &bl, &off);
1223             item_win = make_window(intern->disp, parent, 0, yspot,
1224                                    width, exp_h, 0,
1225                                    intern->attr->border_bd,
1226                                    intern->attr->item_bg, (Cursor) None,
1227                                    EnterWindowMask | ExposureMask, 0);
1228     } else if (entry->item_type == XPA_ITEM_TYPE_SEPARATOR) {
1229 /*
1230             item_size(intern->attr->item_font, entry->item_name,
1231                       (entry->sub_entrys != (xpa_entry *) 0),
1232                       intern->attr->key_font, entry->key_char,
1233                       &exp_w, &exp_h, &bl, &off);
1234 */
1235             bl = 1;
1236             exp_h = 1;
1237             item_win = make_window(intern->disp, parent, 0, yspot,
1238                                    width, exp_h, 1,
1239                                    intern->attr->border_bd,
1240                                    intern->attr->item_fg, (Cursor) None,
1241                                    EnterWindowMask | ExposureMask, 0);
1242  
1243     }
1244     if (entry->sub_entrys) {
1245         sub_win = make_pane(intern, entry->sub_title,
1246                             (xpa_entry *) entry->sub_entrys, lev+1);
1247     } else {
1248         sub_win = (Window) 0;
1249     }
1250     item = new_item(item_win, entry->item_name, entry->key_char, lev, idx,
1251                     width, exp_h, bl, yspot + bl, key_off,
1252                     sub_win, (xpa_pb *) 0);
1253     if (entry->sub_entrys) {
1254         item->pb = make_pullbox(intern, item_win, item,
1255                                 width - XPA_HORPAD - XPA_PB_W,
1256                                 exp_h-2/*XXXXX*/);
1257     }
1258     dispatch(item_win, intern, (xpa_data *) item, cb_item);
1259     XMapWindow(intern->disp, item_win);
1260     return exp_h;
1261 }
1262  
1263 static Window make_pane(intern, title, entrys, lev)
1264 xpa_intern *intern;             /* Internal view of menu */
1265 char *title;                    /* Title (zero if none)  */
1266 xpa_entry *entrys;              /* Menu itself           */
1267 int lev;                        /* Level in heirarchy    */
1268 /*
1269  * Makes a pane and inserts it into the context
1270  * table.  Also makes any submenu panes.
1271  */
1272 {
1273     Window result;
1274     int w, h, tw, th, bl, off, key_off, count, i;
1275     xpa_entry *idx;
1276  
1277     /* Determine size of result window */
1278     if (title) {
1279         title_size(intern->attr->title_font, title, &w, &h);
1280     } else {
1281         w = h = 0;
1282     }
1283     count = 0;
1284     key_off = 0;
1285     for (idx = entrys;  idx->item_name;  idx++) {
1286         if (idx->item_type == XPA_ITEM_TYPE_ENTRY) {
1287             item_size(intern->attr->item_font, idx->item_name,
1288                   (idx->sub_entrys != (xpa_entrys) 0),
1289                   intern->attr->key_font, idx->key_char,
1290                   &tw, &th, &bl, &off);
1291         } else if (idx->item_type == XPA_ITEM_TYPE_SEPARATOR) {
1292                   tw = 0;
1293                   th = 1;
1294                   bl = 1;
1295                   off = 0;
1296         }
1297         h += th;
1298         if (tw > w) w = tw;
1299         if (off > key_off) key_off = off;
1300         count++;
1301     }
1302     intern->width = w+key_off;
1303     intern->height = h;
1304  
1305     /* Make window */
1306     result = make_window(intern->disp, DefaultRootWindow(intern->disp),
1307                          0, 0, w+key_off, h, XPA_BORDER,
1308                          intern->attr->border_bd,
1309                          intern->attr->title_bg, intern->cursor,
1310                          LeaveWindowMask | ButtonPressMask | ButtonReleaseMask,
1311                          SAVEUNDER | OVERRIDE);
1312     dispatch(result, intern, (xpa_data *) new_pane(result, lev), cb_pane);
1313  
1314     /* Make subwindows and subpanes */
1315     th = 0;
1316     if (title) {
1317         th += make_title(intern, result, title, lev, w+key_off, th);
1318     }
1319     for (i = 0;  i < count;  i++) {
1320         th += make_item(intern, result, &(entrys[i]), lev, i, w+key_off, th, key_off);
1321     }
1322     return result;
1323 }
1324  
1325  
1326 static XFontStruct *def_font(disp, mask, bit, new, defmask, defvalue, fontspec)
1327 Display *disp;                  /* X Connection  */
1328 int mask;                       /* Passed mask   */
1329 int bit;                        /* Bit to check  */
1330 XFontStruct *new;               /* User value    */
1331 int *defmask;                   /* Default mask  */
1332 XFontStruct **defvalue;         /* Default value */
1333 char *fontspec;                 /* Font specif.  */
1334 /*
1335  * Handles default handling for a font.  If `bit' is set in `mask',
1336  * `new' is returned, otherwise `defValue' is returned.  If `bit'
1337  * is not set in `defmask',  `defValue' is first loaded using
1338  * `fontspec' and the bit is set in `defmask'.
1339  */
1340 {
1341     char **font_list;
1342     int count;
1343  
1344     if (mask & bit) return new;
1345     if (!(*defmask & bit)) {
1346         font_list = XListFonts(disp, fontspec, 1, &count);
1347         *defvalue = XLoadQueryFont(disp, font_list[0]);
1348         XFreeFontNames(font_list);
1349         *defmask |= bit;
1350     }
1351     return *defvalue;
1352 }
1353  
1354 static unsigned long def_pixel(disp, mask, bit, new, defmask, defvalue, clrspec)
1355 Display *disp;                  /* X Connection  */
1356 int mask;                       /* Passed mask   */
1357 int bit;                        /* Bit to check  */
1358 unsigned long new;              /* User value    */
1359 int *defmask;                   /* Default mask  */
1360 unsigned long *defvalue;        /* Default value */
1361 char *clrspec;                  /* Color specif. */
1362 /*
1363  * Handles default handling for a color.  Similar to def_font().
1364  * Color specification only handles "white" and "black".  Later
1365  * it could look up the named color.
1366  */
1367 {
1368     if (mask & bit) return new;
1369     if (!(*defmask & bit)) {
1370         if (strcmp(clrspec, "white") == 0) {
1371             *defvalue = WhitePixel(disp, DefaultScreen(disp));
1372         } else if (strcmp(clrspec, "black") == 0) {
1373             *defvalue = BlackPixel(disp, DefaultScreen(disp));
1374         } else {
1375             xpa_raise(XPA_BADCLR);
1376         }
1377         *defmask |= bit;
1378     }
1379     return *defvalue;
1380 }
1381  
1382 #define PF(ptr, field)  (ptr ? ptr->field : 0)
1383  
1384 static xpa_appearance *def_appearance(disp, mask, passed)
1385 Display *disp;                  /* X Connection    */
1386 int mask;                       /* Appearance mask */
1387 xpa_appearance *passed;         /* Passed values   */
1388 /*
1389  * Returns an appearance where values are filled from default values
1390  * unless the appropriate bit in `mask' is set.  If the bit is
1391  * set,  the value is taken from `passed'.
1392  */
1393 {
1394     static xpa_appearance def_app;
1395     static xpa_appearance rtn_app;
1396     static int app_init = 0;
1397  
1398     if (mask & !passed) xpa_raise(XPA_ZAPP);
1399     rtn_app.title_font =
1400       def_font(disp, mask, XPA_T_FONT, PF(passed, title_font),
1401                &app_init, &(def_app.title_font),
1402                 "*-bold-r-*-140-*-iso8859-*");
1403     rtn_app.item_font =
1404       def_font(disp, mask, XPA_I_FONT, PF(passed, item_font),
1405        &app_init, &(def_app.item_font),
1406                 "*-medium-r-*-120-*-iso8859-*"
1407 /*              "-adobe-times-medium-r-*-*-16-*-*-*-p-*-*-*"*/);
1408     rtn_app.key_font =
1409       def_font(disp, mask, XPA_K_FONT, PF(passed, key_font),
1410                &app_init, &(def_app.key_font), "*-medium-r-*-100-*-iso8859-*");
1411  
1412     rtn_app.title_fg =
1413       def_pixel(disp, mask, XPA_T_FG, PF(passed, title_fg),
1414                 &app_init, &(def_app.title_fg), "white");
1415     rtn_app.title_bg =
1416       def_pixel(disp, mask, XPA_T_BG, PF(passed, title_bg),
1417                 &app_init, &(def_app.title_bg), "black");
1418     rtn_app.item_fg =
1419       def_pixel(disp, mask, XPA_I_FG, PF(passed, item_fg),
1420                 &app_init, &(def_app.item_fg), "black");
1421     rtn_app.item_bg =
1422       def_pixel(disp, mask, XPA_I_BG, PF(passed, item_bg),
1423                 &app_init, &(def_app.item_bg), "white");
1424     rtn_app.border_bd =
1425       def_pixel(disp, mask, XPA_I_FG, PF(passed, border_bd),
1426                 &app_init, &(def_app.border_bd), "orange");    /*XXX*/
1427     rtn_app.cur_fg =
1428       def_pixel(disp, mask, XPA_C_FG, PF(passed, cur_fg),
1429                 &app_init, &(def_app.cur_fg), "black");
1430     return &rtn_app;
1431 }
1432  
1433  
1434  
1435 xpa_menu xpa_create(disp, title, entrys, mask, appearance)
1436 Display *disp;                  /* X Connection            */
1437 char *title;                    /* Top pane title          */
1438 xpa_entry *entrys;              /* Menu contents           */
1439 int mask;                       /* Display attr mask       */
1440 xpa_appearance *appearance;     /* Menu display attributes */
1441 /*
1442  * Creates a new multi-level menu and returns a handle to it.
1443  * Those display attributes whose mask appears in `mask' will
1444  * use the corresponding value in `appearance' instead of the
1445  * default value.
1446  */
1447 {
1448     xpa_intern *intern;
1449     XColor cur_fg, cur_bg;
1450     int i;
1451  
1452     if (xpa_xcon == (XContext) 0) {
1453         xpa_xcon = XUniqueContext();
1454         set_pb_sym(disp);
1455     }
1456     if (!(intern = XPA_MEM(xpa_intern))) {
1457         return (xpa_menu) 0;
1458     }
1459     intern->disp = disp;
1460     intern->allwins = (xpa_context *) 0;
1461     intern->depth = depth(entrys);
1462     if (!(intern->state = XPA_MEMX(xpa_ms, intern->depth))) {
1463         xpa_raise(XPA_NOMEM);
1464     }
1465     for (i = 0;  i < intern->depth;  i++) {
1466         intern->state[i].x = intern->state[i].y = 0;
1467         intern->state[i].pane = (Window) 0;
1468         intern->state[i].item = (xpa_item *) 0;
1469     }
1470     if (!(intern->attr = XPA_MEM(xpa_appearance))) {
1471         xpa_raise(XPA_NOMEM);
1472     }
1473     /* Default appearance handling */
1474     appearance = def_appearance(disp, mask, appearance);
1475     *(intern->attr) = *appearance;
1476     intern->cursor = XCreateFontCursor(disp, XC_top_left_arrow);
1477     cur_fg.pixel = intern->attr->cur_fg;
1478     cur_bg.pixel = intern->attr->item_bg;
1479     XQueryColor(disp, DefaultColormap(disp, DefaultScreen(disp)), &cur_fg);
1480     XQueryColor(disp, DefaultColormap(disp, DefaultScreen(disp)), &cur_bg);
1481     XRecolorCursor(disp, intern->cursor, &cur_fg, &cur_bg);
1482     if (!(intern->top = make_pane(intern, title, entrys, 0))) {
1483         xpa_raise(XPA_NOMEM);
1484     }
1485     return (xpa_menu) intern;
1486 }
1487  
1488  
1489 #define RES_INIT        10
1490 static int *res_ary = (int *) 0;
1491 static int res_len = 0;
1492 static int res_alloc = 0;
1493  
1494 int response(intern, ary)
1495 xpa_intern *intern;             /* Internal view of menu */
1496 int **ary;                      /* Returned array        */
1497 /*
1498  * Compiles a static array of offsets for returning to
1499  * the user.  If no selections made,  returns zero.
1500  */
1501 {
1502     int i;
1503  
1504     if (res_alloc == 0) {
1505         res_alloc = RES_INIT;
1506         res_ary = XPA_MEMX(int, res_alloc);
1507     }
1508     if (res_alloc <= intern->depth) {
1509         res_alloc = intern->depth + RES_INIT;
1510         res_ary = (int *) realloc((char *) res_ary,
1511                                   (unsigned) (sizeof(int) * res_alloc));
1512     }
1513     res_len = 0;
1514     for (i = 0;  i < intern->depth;  i++) {
1515         if (intern->state[i].item) {
1516             res_ary[res_len++] = intern->state[i].item->idx;
1517         }
1518     }
1519     if ((res_len == intern->depth) ||
1520         ((res_len > 0) && (!intern->state[res_len].pane))) {
1521         *ary = res_ary;
1522     } else {
1523         *ary = (int *) 0;
1524         res_len = 0;
1525     }
1526     return res_len;
1527 }
1528  
1529  
1530  
1531 void xpa_post(menu, x, y, depth, vals, opt)
1532 xpa_menu menu;                  /* Menu to post         */
1533 int x, y;                       /* Location             */
1534 int depth;                      /* Depth                */
1535 int *vals;                      /* Array values         */
1536 int opt;                        /* XPA_PRESS            */
1537 /*
1538  * Shows menu at location (x,y).  If `depth' is greater than
1539  * zero,  the menu pops up with the selection given by
1540  * the integer offsets `vals' (where depth specifies the
1541  * number of offsets).  If `depth' is zero,  the top
1542  * level pane is shown with the mousein the title or
1543  * over the first item if there is no title.  If XPA_PRESS is
1544  * specified as an option,  the menu will react to button press
1545  * otherwise it will react to button release.
1546  * NOTE: depth and vals are not implemented.
1547  */
1548 {
1549     xpa_intern *intern = (xpa_intern *) menu;
1550     int i;
1551  
1552     intern->press_flag = (opt & XPA_PRESS) != 0;
1553     for (i = 0;  i < intern->depth;  i++) {
1554         intern->state[i].x = intern->state[i].y = 0;
1555         intern->state[i].pane = (Window) 0;
1556         intern->state[i].item = (xpa_item *) 0;
1557     }
1558     pane_on(intern, intern->top, 0, x, y, (xpa_item *) 0);
1559 }
1560  
1561  
1562  
1563 void xpa_unpost(menu)
1564 xpa_menu menu;                  /* Menu to turn off */
1565 /*
1566  * Unposts `menu'.  This includes all of its subpanes.  Does
1567  * not effect menu state.
1568  */
1569 {
1570     xpa_intern *intern = (xpa_intern *) menu;
1571     int i;
1572  
1573     for (i = intern->depth-1;  i >= 0;  i--) {
1574         if (intern->state[i].pane) {
1575             XUnmapWindow(intern->disp, intern->state[i].pane);
1576         }
1577     }
1578 }
1579  
1580  
1581  
1582 int xpa_filter(evt, menu, result)
1583 XEvent *evt;                    /* Event to handle */
1584 xpa_menu *menu;                 /* Returned menu   */
1585 int **result;                   /* Returned choice */
1586 /*
1587  * This routine handles events for the xpa menu package.
1588  * Possible return codes:
1589  *   XPA_FOREIGN        Event is not for the menu package
1590  *   XPA_HANDLED        Event was handled with no response
1591  * These return codes are less than zero.  If the return
1592  * code is zero or more,  it indicates a selection occurred
1593  * on `menu'.  Zero indicates no selection.  Greater than
1594  * zero indicates the length of `result' which gives indicies
1595  * into each pane in the heirarchy.
1596  */
1597 {
1598     xpa_context *cb;
1599     int val;
1600  
1601     if (!XFindContext(evt->xany.display, evt->xany.window, xpa_xcon,
1602                       (caddr_t *) &cb)) {
1603         /* Found it */
1604         if (cb->callback) {
1605             val = (*cb->callback)(evt, cb->intern, cb->data);
1606             if (val > 0) {
1607                 /* Something happened */
1608                 *menu = (xpa_menu) cb->intern;
1609                 return response(cb->intern, result);
1610             }
1611         }
1612         /* Nothing happened */
1613         *menu = (xpa_menu) 0;
1614         *result = (int *) 0;
1615         return XPA_HANDLED;
1616     } else {
1617         /* Not our event */
1618         *menu = (xpa_menu) 0;
1619         *result = (int *) 0;
1620         return XPA_FOREIGN;
1621     }
1622 }
1623  
1624  
1625  
1626 int xpa_moded(menu, x, y, depth, vals, opt, handler, result)
1627 xpa_menu menu;                  /* Menu to post       */
1628 int x, y;                       /* Screen coordinates */
1629 int depth;                      /* Depth of spec      */
1630 int *vals;                      /* Starting spot      */
1631 int opt;                        /* XPA_PRESS or none  */
1632 int (*handler)();               /* Event handler      */
1633 int **result;                   /* Returned result    */
1634 /*
1635  * Posts a moded menu.  This is basically a wrapper around
1636  * xpa_post, xpa_filter, and xpa_unpost.  However,  it
1637  * will guarantee there are no more menu events before
1638  * returning.  If XPA_LAST is specified,  the previous
1639  * menu state is used.  If XPA_PRESS is specified,  the menu will
1640  * react to press events otherwise it will react to
1641  * release events.  May return XPA_NOGRAB if it can't grab
1642  * the mouse.  Events not recognized by the menu package
1643  * will be passed to `handler'.  If it returns a non-zero
1644  * value,  the menu will be unposted immediately.
1645  */
1646 {
1647     XEvent evt;
1648     xpa_intern *intern = (xpa_intern *) menu;
1649     xpa_menu rtn_menu, sub_menu;
1650     int val, sub_val;
1651     int *sub_result;
1652  
1653     xpa_post(menu, x, y, depth, vals, opt);
1654     XUngrabPointer(intern->disp, CurrentTime);
1655     if (XGrabPointer(intern->disp, intern->top, True,
1656                      ButtonPressMask | ButtonReleaseMask,
1657                      GrabModeAsync, GrabModeAsync, None,
1658                      intern->cursor, CurrentTime) != GrabSuccess) {
1659         return XPA_NOGRAB;
1660     }
1661     for (;;) {
1662         XNextEvent(intern->disp, &evt);
1663         val = xpa_filter(&evt, &rtn_menu, result);
1664         if ((val >= 0) && (rtn_menu == menu)) {
1665           break;
1666         }
1667         if (val == XPA_FOREIGN) {
1668             if ((*handler)(&evt)) {
1669                 break;
1670             }
1671         }
1672     }
1673     XUngrabPointer(intern->disp, CurrentTime);
1674     xpa_unpost(menu);
1675     XSync(intern->disp, 0);
1676     while (XPending(intern->disp) > 0) {
1677         XNextEvent(intern->disp, &evt);
1678         sub_val = xpa_filter(&evt, &sub_menu, &sub_result);
1679         if (sub_val == XPA_FOREIGN) {
1680             (void) (*handler)(&evt);
1681         }
1682     }
1683  
1684     return val;
1685 }
1686  
1687  
1688  
1689 void xpa_destroy(menu)
1690 xpa_menu menu;                  /* Menu to destroy */
1691 /*
1692  * Frees all resources associated with a menu.
1693  */
1694 {
1695     xpa_intern *intern = (xpa_intern *) menu;
1696     struct xpa_context_defn *idx, *temp;
1697  
1698     idx = intern->allwins;
1699     while (idx) {
1700         if (idx->data->any.type == PANE_T) {
1701             XDestroyWindow(intern->disp, idx->data->any.window);
1702         }
1703         temp = idx;
1704         idx = idx->next;
1705         XPA_FREE(temp);
1706     }
1707     XPA_FREE(intern->state);
1708     XPA_FREE(intern->attr);
1709     XFreeCursor(intern->disp, intern->cursor);
1710     XPA_FREE(intern);
1711 }