Merge the usermenu branch. This reworks how the menus and hotkeys
[geda-pcb/leaky.git] / src / hid / lesstif / menu.c
blob0e9dcc27ebb9a7676cee2c8d924b085134f56c87
1 /* $Id$ */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <unistd.h>
13 #include "xincludes.h"
15 #include "global.h"
16 #include "data.h"
17 #include "misc.h"
19 #include "hid.h"
20 #include "../hidint.h"
21 #include "resource.h"
22 #include "lesstif.h"
23 #include "mymem.h"
25 #include "pcb-menu.h"
27 #ifdef HAVE_LIBDMALLOC
28 #include <dmalloc.h>
29 #endif
31 RCSID ("$Id$");
33 #ifndef R_OK
34 /* Common value for systems that don't define it. */
35 #define R_OK 4
36 #endif
38 Display *display;
39 static Colormap cmap;
41 static Arg args[30];
42 static int n;
43 #define stdarg(t,v) XtSetArg(args[n], t, v), n++
45 static void note_accelerator (char *acc, Resource * node);
47 static const char getxy_syntax[] =
48 "GetXY()";
50 static const char getxy_help[] =
51 "Get a coordinate.";
53 /* %start-doc actions GetXY
55 Prompts the user for a coordinate, if one is not already selected.
57 %end-doc */
59 static int
60 GetXY (int argc, char **argv, int x, int y)
62 return 0;
65 static const char debug_syntax[] =
66 "Debug(...)";
68 static const char debug_help[] =
69 "Debug action.";
71 /* %start-doc actions Debug
73 This action exists to help debug scripts; it simply prints all its
74 arguments to stdout.
76 %end-doc */
78 static const char debugxy_syntax[] =
79 "DebugXY(...)";
81 static const char debugxy_help[] =
82 "Debug action, with coordinates";
84 /* %start-doc actions DebugXY
86 Like @code{Debug}, but requires a coordinate. If the user hasn't yet
87 indicated a location on the board, the user will be prompted to click
88 on one.
90 %end-doc */
92 static int
93 Debug (int argc, char **argv, int x, int y)
95 int i;
96 printf ("Debug:");
97 for (i = 0; i < argc; i++)
98 printf (" [%d] `%s'", i, argv[i]);
99 printf (" x,y %d,%d\n", x, y);
100 return 0;
103 static const char return_syntax[] =
104 "Return(0|1)";
106 static const char return_help[] =
107 "Simulate a passing or failing action.";
109 /* %start-doc actions Return
111 This is for testing. If passed a 0, does nothing and succeeds. If
112 passed a 1, does nothing but pretends to fail.
114 %end-doc */
116 static int
117 Return (int argc, char **argv, int x, int y)
119 return atoi (argv[0]);
122 static const char dumpkeys_syntax[] =
123 "DumpKeys()";
125 static const char dumpkeys_help[] =
126 "Dump Lesstif key bindings.";
128 /* %start-doc actions DumpKeys
130 Causes the list of key bindings (from @code{pcb-menu.res}) to be
131 dumped to stdout. This is most useful when invoked from the command
132 line like this:
134 @example
135 pcb --action-string DumpKeys
136 @end example
138 %end-doc */
140 static int do_dump_keys = 0;
141 static int
142 DumpKeys (int argc, char **argv, int x, int y)
144 do_dump_keys = 1;
145 return 0;
148 /*-----------------------------------------------------------------------------*/
150 #define LB_SILK (MAX_LAYER+0)
151 #define LB_RATS (MAX_LAYER+1)
152 #define LB_NUMPICK (LB_RATS+1)
153 /* more */
154 #define LB_PINS (MAX_LAYER+2)
155 #define LB_VIAS (MAX_LAYER+3)
156 #define LB_BACK (MAX_LAYER+4)
157 #define LB_MASK (MAX_LAYER+5)
158 #define LB_NUM (MAX_LAYER+6)
160 typedef struct
162 Widget w[LB_NUM];
163 int is_pick;
164 } LayerButtons;
166 static LayerButtons *layer_button_list = 0;
167 static int num_layer_buttons = 0;
168 static int fg_colors[LB_NUM];
169 static int bg_color;
171 extern Widget lesstif_m_layer;
173 static int
174 LayersChanged (int argc, char **argv, int x, int y)
176 int l, i, set;
177 char *name;
178 int current_layer;
180 if (!layer_button_list)
181 return 0;
182 if (PCB && PCB->Data)
184 DataType *d = PCB->Data;
185 for (i = 0; i < MAX_LAYER; i++)
186 fg_colors[i] = lesstif_parse_color (d->Layer[i].Color);
187 fg_colors[LB_SILK] = lesstif_parse_color (PCB->ElementColor);
188 fg_colors[LB_RATS] = lesstif_parse_color (PCB->RatColor);
189 fg_colors[LB_PINS] = lesstif_parse_color (PCB->PinColor);
190 fg_colors[LB_VIAS] = lesstif_parse_color (PCB->ViaColor);
191 fg_colors[LB_BACK] =
192 lesstif_parse_color (PCB->InvisibleObjectsColor);
193 fg_colors[LB_MASK] = lesstif_parse_color (PCB->MaskColor);
194 bg_color = lesstif_parse_color (Settings.BackgroundColor);
196 else
198 for (i = 0; i < MAX_LAYER; i++)
199 fg_colors[i] = lesstif_parse_color (Settings.LayerColor[i]);
200 fg_colors[LB_SILK] = lesstif_parse_color (Settings.ElementColor);
201 fg_colors[LB_RATS] = lesstif_parse_color (Settings.RatColor);
202 fg_colors[LB_PINS] = lesstif_parse_color (Settings.PinColor);
203 fg_colors[LB_VIAS] = lesstif_parse_color (Settings.ViaColor);
204 fg_colors[LB_BACK] =
205 lesstif_parse_color (Settings.InvisibleObjectsColor);
206 fg_colors[LB_MASK] = lesstif_parse_color (Settings.MaskColor);
207 bg_color = lesstif_parse_color (Settings.BackgroundColor);
210 if (PCB->RatDraw)
211 current_layer = LB_RATS;
212 else if (PCB->SilkActive)
213 current_layer = LB_SILK;
214 else
215 current_layer = LayerStack[0];
217 for (l = 0; l < num_layer_buttons; l++)
219 LayerButtons *lb = layer_button_list + l;
220 for (i = 0; i < (lb->is_pick ? LB_NUMPICK : LB_NUM); i++)
222 switch (i)
224 case LB_SILK:
225 set = PCB->ElementOn;
226 break;
227 case LB_RATS:
228 set = PCB->RatOn;
229 break;
230 case LB_PINS:
231 set = PCB->PinOn;
232 break;
233 case LB_VIAS:
234 set = PCB->ViaOn;
235 break;
236 case LB_BACK:
237 set = PCB->InvisibleObjectsOn;
238 break;
239 case LB_MASK:
240 set = TEST_FLAG (SHOWMASKFLAG, PCB);
241 break;
242 default: /* layers */
243 set = PCB->Data->Layer[i].On;
244 break;
247 n = 0;
248 if (i < MAX_LAYER && PCB->Data->Layer[i].Name)
250 XmString s = XmStringCreateLocalized (PCB->Data->Layer[i].Name);
251 stdarg (XmNlabelString, s);
253 if (!lb->is_pick)
255 if (set)
257 stdarg (XmNforeground, bg_color);
258 stdarg (XmNbackground, fg_colors[i]);
260 else
262 stdarg (XmNforeground, fg_colors[i]);
263 stdarg (XmNbackground, bg_color);
265 stdarg (XmNset, set);
267 else
269 stdarg (XmNforeground, bg_color);
270 stdarg (XmNbackground, fg_colors[i]);
271 stdarg (XmNset, current_layer == i ? True : False);
273 XtSetValues (lb->w[i], args, n);
275 if (i >= max_layer && i < MAX_LAYER)
276 XtUnmanageChild(lb->w[i]);
277 else
278 XtManageChild(lb->w[i]);
281 if (lesstif_m_layer)
283 switch (current_layer)
285 case LB_RATS:
286 name = "Rats";
287 break;
288 case LB_SILK:
289 name = "Silk";
290 break;
291 default:
292 name = PCB->Data->Layer[current_layer].Name;
293 break;
295 n = 0;
296 stdarg (XmNbackground, fg_colors[current_layer]);
297 stdarg (XmNforeground, bg_color);
298 stdarg (XmNlabelString, XmStringCreateLocalized (name));
299 XtSetValues (lesstif_m_layer, args, n);
302 lesstif_update_layer_groups ();
304 return 0;
307 static void
308 show_one_layer_button (int layer, int set)
310 int l;
311 n = 0;
312 if (set)
314 stdarg (XmNforeground, bg_color);
315 stdarg (XmNbackground, fg_colors[layer]);
317 else
319 stdarg (XmNforeground, fg_colors[layer]);
320 stdarg (XmNbackground, bg_color);
322 stdarg (XmNset, set);
324 for (l = 0; l < num_layer_buttons; l++)
326 LayerButtons *lb = layer_button_list + l;
327 if (!lb->is_pick)
328 XtSetValues (lb->w[layer], args, n);
332 static void
333 layer_button_callback (Widget w, int layer, XmPushButtonCallbackStruct * pbcs)
335 int l, set;
336 switch (layer)
338 case LB_SILK:
339 set = PCB->ElementOn = !PCB->ElementOn;
340 PCB->Data->SILKLAYER.On = set;
341 PCB->Data->BACKSILKLAYER.On = set;
342 break;
343 case LB_RATS:
344 set = PCB->RatOn = !PCB->RatOn;
345 break;
346 case LB_PINS:
347 set = PCB->PinOn = !PCB->PinOn;
348 break;
349 case LB_VIAS:
350 set = PCB->ViaOn = !PCB->ViaOn;
351 break;
352 case LB_BACK:
353 set = PCB->InvisibleObjectsOn = !PCB->InvisibleObjectsOn;
354 break;
355 case LB_MASK:
356 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
357 set = TEST_FLAG (SHOWMASKFLAG, PCB);
358 break;
359 default: /* layers */
360 set = PCB->Data->Layer[layer].On = !PCB->Data->Layer[layer].On;
361 break;
364 show_one_layer_button (layer, set);
365 if (layer < max_layer)
367 int i;
368 int group = GetLayerGroupNumberByNumber (layer);
369 for (i = 0; i < PCB->LayerGroups.Number[group]; i++)
371 l = PCB->LayerGroups.Entries[group][i];
372 if (l != layer && l < max_layer)
374 show_one_layer_button (l, set);
375 PCB->Data->Layer[l].On = set;
379 lesstif_invalidate_all ();
382 static void
383 layerpick_button_callback (Widget w, int layer,
384 XmPushButtonCallbackStruct * pbcs)
386 int l, i;
387 char *name;
388 PCB->RatDraw = (layer == LB_RATS);
389 PCB->SilkActive = (layer == LB_SILK);
390 if (layer < max_layer)
391 ChangeGroupVisibility (layer, 1, 1);
392 for (l = 0; l < num_layer_buttons; l++)
394 LayerButtons *lb = layer_button_list + l;
395 if (!lb->is_pick)
396 continue;
397 for (i = 0; i < LB_NUMPICK; i++)
398 XmToggleButtonSetState (lb->w[i], layer == i, False);
400 switch (layer)
402 case LB_RATS:
403 name = "Rats";
404 break;
405 case LB_SILK:
406 name = "Silk";
407 break;
408 default:
409 name = PCB->Data->Layer[layer].Name;
410 break;
412 n = 0;
413 stdarg (XmNbackground, fg_colors[layer]);
414 stdarg (XmNforeground, bg_color);
415 stdarg (XmNlabelString, XmStringCreateLocalized (name));
416 XtSetValues (lesstif_m_layer, args, n);
417 lesstif_invalidate_all ();
420 static const char selectlayer_syntax[] =
421 "SelectLayer(1..MAXLAYER|Silk|Rats)";
423 static const char selectlayer_help[] =
424 "Select which layer is the current layer.";
426 /* %start-doc actions SelectLayer
428 The specified layer becomes the currently active layer. It is made
429 visible if it is not already visible
431 %end-doc */
433 static int
434 SelectLayer (int argc, char **argv, int x, int y)
436 int newl;
437 if (argc == 0)
438 return 1;
439 if (strcasecmp (argv[0], "silk") == 0)
440 newl = LB_SILK;
441 else if (strcasecmp (argv[0], "rats") == 0)
442 newl = LB_RATS;
443 else
444 newl = atoi (argv[0]) - 1;
445 layerpick_button_callback (0, newl, 0);
446 return 0;
449 static const char toggleview_syntax[] =
450 "ToggleView(1..MAXLAYER)\n"
451 "ToggleView(layername)\n"
452 "ToggleView(Silk|Rats|Pins|Vias|Mask|BackSide)";
454 static const char toggleview_help[] =
455 "Toggle the visibility of the specified layer or layer group.";
457 /* %start-doc actions ToggleView
459 If you pass an integer, that layer is specified by index (the first
460 layer is @code{1}, etc). If you pass a layer name, that layer is
461 specified by name. When a layer is specified, the visibility of the
462 layer group containing that layer is toggled.
464 If you pass a special layer name, the visibility of those components
465 (silk, rats, etc) is toggled. Note that if you have a layer named
466 the same as a special layer, the layer is chosen over the special layer.
468 %end-doc */
470 static int
471 ToggleView (int argc, char **argv, int x, int y)
473 int i, l;
475 if (argc == 0)
476 return 1;
477 if (isdigit ((int) argv[0][0]))
479 l = atoi (argv[0]) - 1;
480 layer_button_callback (0, l, 0);
482 else if (strcmp (argv[0], "Silk") == 0)
483 layer_button_callback (0, LB_SILK, 0);
484 else if (strcmp (argv[0], "Rats") == 0)
485 layer_button_callback (0, LB_RATS, 0);
486 else if (strcmp (argv[0], "Pins") == 0)
487 layer_button_callback (0, LB_PINS, 0);
488 else if (strcmp (argv[0], "Vias") == 0)
489 layer_button_callback (0, LB_VIAS, 0);
490 else if (strcmp (argv[0], "Mask") == 0)
491 layer_button_callback (0, LB_MASK, 0);
492 else if (strcmp (argv[0], "BackSide") == 0)
493 layer_button_callback (0, LB_BACK, 0);
494 else
496 l = -1;
497 for (i = 0; i < max_layer + 2; i++)
498 if (strcmp (argv[0], PCB->Data->Layer[i].Name) == 0)
500 l = i;
501 break;
503 if (l == -1)
504 return 1;
505 layer_button_callback (0, l, 0);
507 return 0;
510 static void
511 insert_layerview_buttons (Widget menu)
513 int i, s;
514 LayerButtons *lb;
516 num_layer_buttons++;
517 s = num_layer_buttons * sizeof (LayerButtons);
518 if (layer_button_list)
519 layer_button_list = (LayerButtons *) realloc (layer_button_list, s);
520 else
521 layer_button_list = (LayerButtons *) malloc (s);
522 lb = layer_button_list + num_layer_buttons - 1;
524 for (i = 0; i < LB_NUM; i++)
526 static char namestr[] = "Label ";
527 char *name = namestr;
528 Widget btn;
529 name[5] = 'A' + i;
530 switch (i)
532 case LB_SILK:
533 name = "Silk";
534 break;
535 case LB_RATS:
536 name = "Rat Lines";
537 break;
538 case LB_PINS:
539 name = "Pins/Pads";
540 break;
541 case LB_VIAS:
542 name = "Vias";
543 break;
544 case LB_BACK:
545 name = "Far Side";
546 break;
547 case LB_MASK:
548 name = "Solder Mask";
549 break;
551 n = 0;
552 if (i < MAX_LAYER && i < 9)
554 char buf[20], av[30];
555 Resource *ar;
556 XmString as;
557 sprintf (buf, "Ctrl-%d", i + 1);
558 as = XmStringCreateLocalized (buf);
559 stdarg (XmNacceleratorText, as);
560 ar = resource_create (0);
561 sprintf (av, "ToggleView(%d)", i + 1);
562 resource_add_val (ar, 0, strdup (av), 0);
563 resource_add_val (ar, 0, strdup (av), 0);
564 ar->flags |= FLAG_V;
565 sprintf (av, "Ctrl<Key>%d", i + 1);
566 note_accelerator (av, ar);
567 stdarg (XmNmnemonic, i + '1');
569 btn = XmCreateToggleButton (menu, name, args, n);
570 XtManageChild (btn);
571 XtAddCallback (btn, XmNvalueChangedCallback,
572 (XtCallbackProc) layer_button_callback, (XtPointer) (size_t) i);
573 lb->w[i] = btn;
575 lb->is_pick = 0;
576 LayersChanged (0, 0, 0, 0);
579 static void
580 insert_layerpick_buttons (Widget menu)
582 int i, s;
583 LayerButtons *lb;
585 num_layer_buttons++;
586 s = num_layer_buttons * sizeof (LayerButtons);
587 if (layer_button_list)
588 layer_button_list = (LayerButtons *) realloc (layer_button_list, s);
589 else
590 layer_button_list = (LayerButtons *) malloc (s);
591 lb = layer_button_list + num_layer_buttons - 1;
593 for (i = 0; i < LB_NUMPICK; i++)
595 static char namestr[] = "Label ";
596 char *name = namestr;
597 Widget btn;
598 name[5] = 'A' + i;
599 switch (i)
601 case LB_SILK:
602 name = "Silk";
603 break;
604 case LB_RATS:
605 name = "Rat Lines";
606 break;
608 n = 0;
609 if (i < MAX_LAYER && i < 9)
611 char buf[20], av[30];
612 Resource *ar;
613 XmString as;
614 sprintf (buf, "%d", i + 1);
615 as = XmStringCreateLocalized (buf);
616 stdarg (XmNacceleratorText, as);
617 ar = resource_create (0);
618 switch (i)
620 case LB_SILK:
621 strcpy (av, "SelectLayer(Silk)");
622 break;
623 case LB_RATS:
624 strcpy (av, "SelectLayer(Rats)");
625 break;
626 default:
627 sprintf (av, "SelectLayer(%d)", i + 1);
628 break;
630 resource_add_val (ar, 0, strdup (av), 0);
631 resource_add_val (ar, 0, strdup (av), 0);
632 ar->flags |= FLAG_V;
633 sprintf (av, "<Key>%d", i + 1);
634 note_accelerator (av, ar);
635 stdarg (XmNmnemonic, i + '1');
637 stdarg (XmNindicatorType, XmONE_OF_MANY);
638 btn = XmCreateToggleButton (menu, name, args, n);
639 XtManageChild (btn);
640 XtAddCallback (btn, XmNvalueChangedCallback,
641 (XtCallbackProc) layerpick_button_callback,
642 (XtPointer) (size_t) i);
643 lb->w[i] = btn;
645 lb->is_pick = 1;
646 LayersChanged (0, 0, 0, 0);
649 /*-----------------------------------------------------------------------------*/
651 typedef struct
653 Widget w;
654 const char *flagname;
655 int oldval;
656 char *xres;
657 } WidgetFlagType;
659 static WidgetFlagType *wflags = 0;
660 static int n_wflags = 0;
661 static int max_wflags = 0;
663 static void
664 note_widget_flag (Widget w, char *type, char *name)
666 if (n_wflags >= max_wflags)
668 max_wflags += 20;
669 wflags =
670 MyRealloc (wflags, max_wflags * sizeof (WidgetFlagType),
671 __FUNCTION__);
673 wflags[n_wflags].w = w;
674 wflags[n_wflags].flagname = name;
675 wflags[n_wflags].oldval = -1;
676 wflags[n_wflags].xres = type;
677 n_wflags++;
680 void
681 lesstif_update_widget_flags ()
683 int i;
685 for (i = 0; i < n_wflags; i++)
687 int v = hid_get_flag (wflags[i].flagname);
688 Arg args[1];
689 XtSetArg (args[0], wflags[i].xres, v ? 1 : 0);
690 XtSetValues (wflags[i].w, args, 1);
691 wflags[i].oldval = v;
695 /*-----------------------------------------------------------------------------*/
697 HID_Action lesstif_menu_action_list[] = {
698 {"DumpKeys", 0, DumpKeys,
699 dumpkeys_help, dumpkeys_syntax},
700 {"Debug", 0, Debug,
701 debug_help, debug_syntax},
702 {"DebugXY", "Click X,Y for Debug", Debug,
703 debugxy_help, debugxy_syntax},
704 {"GetXY", "", GetXY,
705 getxy_help, getxy_syntax},
706 {"Return", 0, Return,
707 return_help, return_syntax},
708 {"LayersChanged", 0, LayersChanged,
709 layerschanged_help, layerschanged_syntax},
710 {"ToggleView", 0, ToggleView,
711 toggleview_help, toggleview_syntax},
712 {"SelectLayer", 0, SelectLayer,
713 selectlayer_help, selectlayer_syntax}
716 REGISTER_ACTIONS (lesstif_menu_action_list)
718 #if 0
719 static void
720 do_color (char *value, char *which)
722 XColor color;
723 if (XParseColor (display, cmap, value, &color))
724 if (XAllocColor (display, cmap, &color))
726 stdarg (which, color.pixel);
729 #endif
731 typedef struct ToggleItem
733 struct ToggleItem *next;
734 Widget w;
735 char *group, *item;
736 XtCallbackProc callback;
737 Resource *node;
738 } ToggleItem;
739 static ToggleItem *toggle_items = 0;
741 static int need_xy = 0, have_xy = 0, action_x, action_y;
743 static void
744 radio_callback (Widget toggle, ToggleItem * me,
745 XmToggleButtonCallbackStruct * cbs)
747 if (!cbs->set) /* uh uh, can't turn it off */
748 XmToggleButtonSetState (toggle, 1, 0);
749 else
751 ToggleItem *ti;
752 for (ti = toggle_items; ti; ti = ti->next)
753 if (strcmp (me->group, ti->group) == 0)
755 if (me->item == ti->item || strcmp (me->item, ti->item) == 0)
756 XmToggleButtonSetState (ti->w, 1, 0);
757 else
758 XmToggleButtonSetState (ti->w, 0, 0);
760 me->callback (toggle, me->node, cbs);
765 lesstif_button_event (Widget w, XEvent * e)
767 have_xy = 1;
768 action_x = e->xbutton.x;
769 action_y = e->xbutton.y;
770 if (!need_xy)
771 return 0;
772 if (w != work_area)
773 return 1;
774 return 0;
777 void
778 lesstif_get_xy (const char *message)
780 XmString ls = XmStringCreateLocalized ((char *)message);
782 XtManageChild (m_click);
783 n = 0;
784 stdarg (XmNlabelString, ls);
785 XtSetValues (m_click, args, n);
786 //printf("need xy: msg `%s'\n", msg);
787 need_xy = 1;
788 XBell (display, 100);
789 while (!have_xy)
791 XEvent e;
792 XtAppNextEvent (app_context, &e);
793 XtDispatchEvent (&e);
795 need_xy = 0;
796 have_xy = 1;
797 XtUnmanageChild (m_click);
800 void
801 lesstif_get_coords (const char *msg, int *px, int *py)
803 if (!have_xy)
804 lesstif_get_xy (msg);
805 lesstif_coords_to_pcb (action_x, action_y, px, py);
809 lesstif_call_action (const char *aname, int argc, char **argv)
811 int px, py;
812 HID_Action *a = hid_find_action (aname);
813 if (!a)
815 int i;
816 printf ("no action %s(", aname);
817 for (i = 0; i < argc; i++)
818 printf ("%s%s", i ? ", " : "", argv[i]);
819 printf (")\n");
820 return 1;
823 if (a->need_coord_msg && !have_xy)
825 const char *msg;
826 if (strcmp (aname, "GetXY") == 0)
827 msg = argv[0];
828 else
829 msg = a->need_coord_msg;
830 lesstif_get_xy (msg);
832 lesstif_coords_to_pcb (action_x, action_y, &px, &py);
834 if (Settings.verbose)
836 int i;
837 printf ("Action: \033[34m%s(", aname);
838 for (i = 0; i < argc; i++)
839 printf ("%s%s", i ? "," : "", argv[i]);
840 printf (")\033[0m\n");
842 return a->trigger_cb (argc, argv, px, py);
845 static void
846 callback (Widget w, Resource * node, XmPushButtonCallbackStruct * pbcs)
848 int vi;
849 have_xy = 0;
850 lesstif_show_crosshair (0);
851 if (pbcs->event && pbcs->event->type == KeyPress)
853 Dimension wx, wy;
854 Widget aw = XtWindowToWidget (display, pbcs->event->xkey.window);
855 action_x = pbcs->event->xkey.x;
856 action_y = pbcs->event->xkey.y;
857 if (aw)
859 Widget p = work_area;
860 while (p && p != aw)
862 n = 0;
863 stdarg (XmNx, &wx);
864 stdarg (XmNy, &wy);
865 XtGetValues (p, args, n);
866 action_x -= wx;
867 action_y -= wy;
868 p = XtParent (p);
870 if (p == aw)
871 have_xy = 1;
873 //printf("have xy from %s: %d %d\n", XtName(aw), action_x, action_y);
876 lesstif_need_idle_proc ();
877 for (vi = 1; vi < node->c; vi++)
878 if (resource_type (node->v[vi]) == 10)
879 if (hid_parse_actions (node->v[vi].value, lesstif_call_action))
880 return;
883 typedef struct acc_table_t
885 char mods;
886 char key_char;
887 union {
888 /* If M_Multi is set in mods, these are used to chain to the next
889 attribute table for multi-key accelerators. */
890 struct {
891 int n_chain;
892 struct acc_table_t *chain;
893 } c;
894 /* If M_Multi isn't set, these are used to map a single key to an
895 event. */
896 struct {
897 KeySym key;
898 Resource *node;
899 } a;
900 } u;
901 } acc_table_t;
903 static acc_table_t *acc_table;
904 static int acc_num = 0;
906 static int
907 acc_sort (const void *va, const void *vb)
909 acc_table_t *a = (acc_table_t *) va;
910 acc_table_t *b = (acc_table_t *) vb;
911 if (a->key_char != b->key_char)
912 return a->key_char - b->key_char;
913 if (!(a->mods & M_Multi))
914 if (a->u.a.key != b->u.a.key)
915 return a->u.a.key - b->u.a.key;
916 return a->mods - b->mods;
919 static int
920 DumpKeys2 ()
922 int i;
923 char ch[2];
924 printf ("in dumpkeys! %d\n", acc_num);
925 qsort (acc_table, acc_num, sizeof (acc_table_t), acc_sort);
926 ch[1] = 0;
927 for (i = 0; i < acc_num; i++)
929 char mod[16];
930 int vi;
931 char *tabs = "";
933 sprintf (mod, "%s%s%s",
934 acc_table[i].mods & M_Alt ? "Alt-" : "",
935 acc_table[i].mods & M_Ctrl ? "Ctrl-" : "",
936 acc_table[i].mods & M_Shift ? "Shift-" : "");
937 ch[0] = toupper (acc_table[i].key_char);
938 printf ("%16s%s\t", mod,
939 acc_table[i].key_char ? ch : XKeysymToString (acc_table[i].
940 u.a.key));
942 for (vi = 1; vi < acc_table[i].u.a.node->c; vi++)
943 if (resource_type (acc_table[i].u.a.node->v[vi]) == 10)
945 printf ("%s%s", tabs, acc_table[i].u.a.node->v[vi].value);
946 tabs = "\n\t\t\t ";
949 printf ("\n");
951 exit (0);
954 static acc_table_t *
955 find_or_create_acc (char mods, char key, KeySym sym,
956 acc_table_t **table, int *n_ents)
958 int i, max;
959 acc_table_t *a;
961 if (*table)
962 for (i=(*n_ents)-1; i>=0; i--)
964 a = & (*table)[i];
965 if (a->mods == mods
966 && a->key_char == key
967 && (mods & M_Multi || a->u.a.key == sym))
968 return a;
971 (*n_ents) ++;
972 max = (*n_ents + 16) & ~15;
974 if (*table)
975 *table = (acc_table_t *) realloc (*table, max * sizeof (acc_table_t));
976 else
977 *table = (acc_table_t *) malloc (max * sizeof (acc_table_t));
979 a = & ((*table)[(*n_ents)-1]);
980 memset (a, 0, sizeof(acc_table_t));
982 a->mods = mods;
983 a->key_char = key;
984 if (!(mods & M_Multi))
985 a->u.a.key = sym;
987 return a;
990 static void
991 note_accelerator (char *acc, Resource * node)
993 char *orig_acc = acc;
994 int mods = 0;
995 acc_table_t *a;
996 char key_char = 0;
997 KeySym key = 0;
998 int multi_key = 0;
1000 while (isalpha ((int) acc[0]))
1002 if (strncmp (acc, "Shift", 5) == 0)
1004 mods |= M_Shift;
1005 acc += 5;
1007 else if (strncmp (acc, "Ctrl", 4) == 0)
1009 mods |= M_Ctrl;
1010 acc += 4;
1012 else if (strncmp (acc, "Alt", 3) == 0)
1014 mods |= M_Alt;
1015 acc += 3;
1017 else
1019 printf ("Must be Shift/Ctrl/Alt: %s\n", acc);
1020 return;
1022 while (*acc == ' ')
1023 acc++;
1025 if (strncmp (acc, "<Keys>", 6) == 0)
1027 multi_key = 1;
1028 acc ++;
1030 else if (strncmp (acc, "<Key>", 5))
1032 fprintf (stderr, "accelerator \"%s\" not <Key> or <Keys>\n", orig_acc);
1033 return;
1036 /* We have a hard time specifying the Enter key the "usual" way. */
1037 if (strcmp (acc, "<Key>Enter") == 0)
1038 acc = "<Key>\r";
1040 acc += 5;
1041 if (acc[0] && acc[1] == 0)
1043 key_char = acc[0];
1044 a = find_or_create_acc (mods, key_char, 0, &acc_table, &acc_num);
1046 else if (multi_key)
1048 acc_table_t **ap = &acc_table;
1049 int *np = &acc_num;
1051 mods |= M_Multi;
1052 while (acc[0] && acc[1])
1054 a = find_or_create_acc (mods, acc[0], 0, ap, np);
1055 ap = & (a->u.c.chain);
1056 np = & (a->u.c.n_chain);
1057 acc ++;
1059 a = find_or_create_acc (mods & ~M_Multi, acc[0], 0, ap, np);
1061 else
1063 key = XStringToKeysym (acc);
1064 if (key == NoSymbol && !key_char)
1066 printf ("no symbol for %s\n", acc);
1067 return;
1069 a = find_or_create_acc (mods, 0, key, &acc_table, &acc_num);
1072 a->u.a.node = node;
1075 #if 0
1076 static void
1077 dump_multi (int ix, int ind, acc_table_t *a, int n)
1079 int i = ix;
1080 while (n--)
1082 if (a->mods & M_Multi)
1084 printf("%*cacc[%d] mods %x char %c multi %p/%d\n",
1085 ind, ' ',
1086 i, a->mods, a->key_char,
1087 a->u.c.chain, a->u.c.n_chain);
1088 dump_multi(0, ind+4, a->u.c.chain, a->u.c.n_chain);
1090 else
1092 printf("%*cacc[%d] mods %x char %c key %d node `%s'\n",
1093 ind, ' ',
1094 i, a->mods, a->key_char,
1095 a->u.a.key, a->u.a.node->v[0].value);
1097 a++;
1098 i++;
1101 #else
1102 #define dump_multi(x,a,b,c)
1103 #endif
1105 static acc_table_t *cur_table = 0;
1106 static int cur_ntable = 0;
1108 /* We sort these such that the ones with explicit modifiers come
1109 before the ones with implicit modifiers. That way, a
1110 Shift<Key>Code gets chosen before a <Key>Code. */
1111 static int
1112 acc_sort_rev (const void *va, const void *vb)
1114 acc_table_t *a = (acc_table_t *) va;
1115 acc_table_t *b = (acc_table_t *) vb;
1116 if (a->key_char != b->key_char)
1117 return a->key_char - b->key_char;
1118 if (!(a->mods & M_Multi))
1119 if (a->u.a.key != b->u.a.key)
1120 return a->u.a.key - b->u.a.key;
1121 return b->mods - a->mods;
1125 lesstif_key_event (XKeyEvent * e)
1127 char buf[10], buf2[10];
1128 KeySym sym, sym2;
1129 int slen, slen2;
1130 int mods = 0;
1131 int i, vi;
1132 static int sorted = 0;
1134 if (!sorted)
1136 sorted = 1;
1137 qsort (acc_table, acc_num, sizeof (acc_table_t), acc_sort_rev);
1140 if (e->state & ShiftMask)
1141 mods |= M_Shift;
1142 if (e->state & ControlMask)
1143 mods |= M_Ctrl;
1144 if (e->state & Mod1Mask)
1145 mods |= M_Alt;
1147 e->state &= ~(ControlMask | Mod1Mask);
1148 slen = XLookupString (e, buf, sizeof (buf), &sym, NULL);
1150 if (e->state & ShiftMask)
1152 e->state &= ~ShiftMask;
1153 slen2 = XLookupString (e, buf2, sizeof (buf2), &sym2, NULL);
1155 else
1156 slen2 = slen;
1158 /* Ignore these. */
1159 switch (sym)
1161 case XK_Shift_L:
1162 case XK_Shift_R:
1163 case XK_Control_L:
1164 case XK_Control_R:
1165 case XK_Caps_Lock:
1166 case XK_Shift_Lock:
1167 case XK_Meta_L:
1168 case XK_Meta_R:
1169 case XK_Alt_L:
1170 case XK_Alt_R:
1171 case XK_Super_L:
1172 case XK_Super_R:
1173 case XK_Hyper_L:
1174 case XK_Hyper_R:
1175 return 1;
1178 if (cur_table == 0)
1180 cur_table = acc_table;
1181 cur_ntable = acc_num;
1184 //printf("\nmods %x key %d str `%s' in %p/%d\n", mods, (int)sym, buf, cur_table, cur_ntable);
1186 #define KM(m) ((m) & ~M_Multi)
1187 for (i = 0; i < cur_ntable; i++)
1189 dump_multi (i, 0, cur_table+i, 1);
1190 if (KM(cur_table[i].mods) == mods)
1192 if (sym == acc_table[i].u.a.key)
1193 break;
1195 if (KM(cur_table[i].mods) == (mods & ~M_Shift))
1197 if (slen == 1 && buf[0] == cur_table[i].key_char)
1198 break;
1199 if (sym == cur_table[i].u.a.key)
1200 break;
1202 if (mods & M_Shift && KM(cur_table[i].mods) == mods)
1204 if (slen2 == 1 && buf2[0] == cur_table[i].key_char)
1205 break;
1206 if (sym2 == acc_table[i].u.a.key)
1207 break;
1211 if (i == cur_ntable)
1213 if (cur_table == acc_table)
1214 lesstif_log ("Key \"%s\" not tied to an action\n", buf);
1215 else
1216 lesstif_log ("Key \"%s\" not tied to a multi-key action\n", buf);
1217 cur_table = 0;
1218 return 0;
1220 if (cur_table[i].mods & M_Multi)
1222 cur_ntable = cur_table[i].u.c.n_chain;
1223 cur_table = cur_table[i].u.c.chain;
1224 dump_multi (0, 0, cur_table, cur_ntable);
1225 return 1;
1228 if (e->window == XtWindow (work_area))
1230 have_xy = 1;
1231 action_x = e->x;
1232 action_y = e->y;
1234 else
1235 have_xy = 0;
1237 for (vi = 1; vi < cur_table[i].u.a.node->c; vi++)
1238 if (resource_type (cur_table[i].u.a.node->v[vi]) == 10)
1239 if (hid_parse_actions
1240 (cur_table[i].u.a.node->v[vi].value, lesstif_call_action))
1241 break;
1242 cur_table = 0;
1243 return 1;
1246 static void
1247 add_resource_to_menu (Widget menu, Resource * node, XtCallbackProc callback)
1249 int i, j;
1250 char *v;
1251 Widget sub, btn;
1252 Resource *r;
1254 for (i = 0; i < node->c; i++)
1255 switch (resource_type (node->v[i]))
1257 case 101: /* named subnode */
1258 n = 0;
1259 stdarg (XmNtearOffModel, XmTEAR_OFF_ENABLED);
1260 sub = XmCreatePulldownMenu (menu, node->v[i].name, args, n);
1261 XtSetValues (sub, args, n);
1262 n = 0;
1263 stdarg (XmNsubMenuId, sub);
1264 btn = XmCreateCascadeButton (menu, node->v[i].name, args, n);
1265 XtManageChild (btn);
1266 add_resource_to_menu (sub, node->v[i].subres, callback);
1267 break;
1269 case 1: /* unnamed subres */
1270 n = 0;
1271 #if 0
1272 if ((v = resource_value (node->v[i].subres, "fg")))
1274 do_color (v, XmNforeground);
1276 if ((v = resource_value (node->v[i].subres, "bg")))
1278 do_color (v, XmNbackground);
1280 if ((v = resource_value (node->v[i].subres, "font")))
1282 XFontStruct *fs = XLoadQueryFont (display, v);
1283 if (fs)
1285 XmFontList fl =
1286 XmFontListCreate (fs, XmSTRING_DEFAULT_CHARSET);
1287 stdarg (XmNfontList, fl);
1290 #endif
1291 if ((v = resource_value (node->v[i].subres, "m")))
1293 stdarg (XmNmnemonic, v);
1295 if ((r = resource_subres (node->v[i].subres, "a")))
1297 XmString as = XmStringCreateLocalized (r->v[0].value);
1298 stdarg (XmNacceleratorText, as);
1299 //stdarg(XmNaccelerator, r->v[1].value);
1300 note_accelerator (r->v[1].value, node->v[i].subres);
1302 v = "button";
1303 for (j = 0; j < node->v[i].subres->c; j++)
1304 if (resource_type (node->v[i].subres->v[j]) == 10)
1306 v = node->v[i].subres->v[j].value;
1307 break;
1309 stdarg (XmNlabelString, XmStringCreateLocalized (v));
1310 if (node->v[i].subres->flags & FLAG_S)
1312 int nn = n;
1313 stdarg (XmNtearOffModel, XmTEAR_OFF_ENABLED);
1314 sub = XmCreatePulldownMenu (menu, v, args + nn, n - nn);
1315 n = nn;
1316 stdarg (XmNsubMenuId, sub);
1317 btn = XmCreateCascadeButton (menu, "menubutton", args, n);
1318 XtManageChild (btn);
1319 add_resource_to_menu (sub, node->v[i].subres, callback);
1321 else
1323 Resource *radio = resource_subres (node->v[i].subres, "radio");
1324 char *checked = resource_value (node->v[i].subres, "checked");
1325 char *label = resource_value (node->v[i].subres, "sensitive");
1326 if (radio)
1328 ToggleItem *ti = (ToggleItem *) malloc (sizeof (ToggleItem));
1329 ti->next = toggle_items;
1330 ti->group = radio->v[0].value;
1331 ti->item = radio->v[1].value;
1332 ti->callback = callback;
1333 ti->node = node->v[i].subres;
1334 toggle_items = ti;
1336 if (resource_value (node->v[i].subres, "set"))
1338 stdarg (XmNset, True);
1340 stdarg (XmNindicatorType, XmONE_OF_MANY);
1341 btn = XmCreateToggleButton (menu, "menubutton", args, n);
1342 ti->w = btn;
1343 XtAddCallback (btn, XmNvalueChangedCallback,
1344 (XtCallbackProc) radio_callback,
1345 (XtPointer) ti);
1347 else if (checked)
1349 if (strchr (checked, ','))
1350 stdarg (XmNindicatorType, XmONE_OF_MANY);
1351 else
1352 stdarg (XmNindicatorType, XmN_OF_MANY);
1353 btn = XmCreateToggleButton (menu, "menubutton", args, n);
1354 XtAddCallback (btn, XmNvalueChangedCallback,
1355 callback, (XtPointer) node->v[i].subres);
1357 else if (label && strcmp (label, "false") == 0)
1359 stdarg (XmNalignment, XmALIGNMENT_BEGINNING);
1360 btn = XmCreateLabel (menu, "menulabel", args, n);
1362 else
1364 btn = XmCreatePushButton (menu, "menubutton", args, n);
1365 XtAddCallback (btn, XmNactivateCallback,
1366 callback, (XtPointer) node->v[i].subres);
1369 for (j = 0; j < node->v[i].subres->c; j++)
1370 switch (resource_type (node->v[i].subres->v[j]))
1372 case 110: /* named value = X resource */
1374 char *n = node->v[i].subres->v[j].name;
1375 if (strcmp (n, "fg") == 0)
1376 n = "foreground";
1377 if (strcmp (n, "bg") == 0)
1378 n = "background";
1379 if (strcmp (n, "m") == 0
1380 || strcmp (n, "a") == 0
1381 || strcmp (n, "sensitive") == 0)
1382 break;
1383 if (strcmp (n, "checked") == 0)
1385 note_widget_flag (btn, XmNset,
1386 node->v[i].subres->v[j].value);
1387 break;
1389 if (strcmp (n, "active") == 0)
1391 note_widget_flag (btn, XmNsensitive,
1392 node->v[i].subres->v[j].value);
1393 break;
1395 XtVaSetValues (btn, XtVaTypedArg,
1397 XtRString,
1398 node->v[i].subres->v[j].value,
1399 strlen (node->v[i].subres->v[j].value) + 1,
1400 NULL);
1402 break;
1405 XtManageChild (btn);
1407 break;
1409 case 10: /* unnamed value */
1410 n = 0;
1411 if (node->v[i].value[0] == '@')
1413 if (strcmp (node->v[i].value, "@layerview") == 0)
1414 insert_layerview_buttons (menu);
1415 if (strcmp (node->v[i].value, "@layerpick") == 0)
1416 insert_layerpick_buttons (menu);
1417 if (strcmp (node->v[i].value, "@routestyles") == 0)
1418 lesstif_insert_style_buttons (menu);
1420 else if (strcmp (node->v[i].value, "-") == 0)
1422 btn = XmCreateSeparator (menu, "sep", args, n);
1423 XtManageChild (btn);
1425 else if (i > 0)
1427 btn = XmCreatePushButton (menu, node->v[i].value, args, n);
1428 XtManageChild (btn);
1430 break;
1434 static char *pcbmenu_path = "pcb-menu.res";
1436 static HID_Attribute pcbmenu_attr[] = {
1437 {"pcb-menu", "Location of pcb-menu.res file",
1438 HID_String, 0, 0, {0, PCBLIBDIR "/pcb-menu.res", 0}, 0, &pcbmenu_path}
1441 REGISTER_ATTRIBUTES(pcbmenu_attr)
1443 Widget
1444 lesstif_menu (Widget parent, char *name, Arg * margs, int mn)
1446 Widget mb = XmCreateMenuBar (parent, name, margs, mn);
1447 char *filename;
1448 Resource *r = 0, *bir;
1449 char *home_pcbmenu;
1450 int screen;
1451 Resource *mr;
1453 display = XtDisplay (mb);
1454 screen = DefaultScreen (display);
1455 cmap = DefaultColormap (display, screen);
1457 home_pcbmenu = Concat (getenv ("HOME"), "/.pcb/pcb-menu.res", NULL);
1459 if (access ("pcb-menu.res", R_OK) == 0)
1460 filename = "pcb-menu.res";
1461 else if (access (home_pcbmenu, R_OK) == 0)
1462 filename = home_pcbmenu;
1463 else if (access (pcbmenu_path, R_OK) == 0)
1464 filename = pcbmenu_path;
1465 else
1466 filename = 0;
1468 bir = resource_parse (0, pcb_menu_default);
1469 if (!bir)
1471 fprintf (stderr, "Error: internal menu resource didn't parse\n");
1472 exit(1);
1475 if (filename)
1476 r = resource_parse (filename, 0);
1478 if (!r)
1479 r = bir;
1481 mr = resource_subres (r, "MainMenu");
1482 if (!mr)
1483 mr = resource_subres (bir, "MainMenu");
1484 if (mr)
1485 add_resource_to_menu (mb, mr, (XtCallbackProc) callback);
1487 mr = resource_subres (r, "Mouse");
1488 if (!mr)
1489 mr = resource_subres (bir, "Mouse");
1490 if (mr)
1491 lesstif_note_mouse_resource (mr);
1494 if (do_dump_keys)
1495 DumpKeys2 ();
1497 return mb;