1 /*
2  * @(#)$Id: gr_gtk.c,v 2.16 2009/06/26 06:46:43 baccala Exp $
3  *
4  * Copyright (C) 1996 - 2001 Tim Witham <twitham@quiknet.com>
5  *
6  * (see the files README and COPYING for more details)
7  *
8  * This file implements the GTK specific pieces of the display
9  *
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <assert.h>
15 #include <sys/stat.h>
16 #include <gtk/gtk.h>
17 #include <gtkdatabox.h>
18 #include <gtkdatabox_points.h>
19 #include <gtkdatabox_lines.h>
20 #include <math.h>
21 
22 #include <string.h>
23 #include "oscope.h"		/* program defaults */
limit(int num,int lo,int hi)24 #include "display.h"
25 #include "func.h"
26 #include "file.h"
27 #include "com_gtk.h"
28 
29 #include "xoscope.rc.h"
30 
31 extern GtkWidget *glade_window;
32 extern GtkWidget *menubar;
33 
34 char my_filename[FILENAME_MAX] = "";
35 GdkFont *font;
36 char fontname[80] = DEF_FX;
37 char fonts[] = "xlsfonts";
38 
handle_opt(int opt,char * optarg)39 GtkWidget *databox;
40 
41 GtkWidget *menubar;
42 
43 GtkItemFactory *factory;
44 extern int fixing_widgets;	/* in com_gtk.c */
45 
46 void
47 yes_sel(GtkWidget *w, GtkWidget *win)
48 {
49   gtk_widget_destroy(GTK_WIDGET(win));
50   savefile(my_filename);
51 }
52 
53 
54 void
55 loadfile_ok_sel(GtkWidget *w, GtkFileSelection *fs)
56 {
57   strncpy(my_filename, gtk_file_selection_get_filename(fs), FILENAME_MAX);
58   gtk_widget_destroy(GTK_WIDGET(fs));
59   loadfile(my_filename);
60 }
61 
62 void
63 savefile_ok_sel(GtkWidget *w, GtkFileSelection *fs)
64 {
65   GtkWidget *window, *yes, *no, *label;
66   struct stat buff;
67 
68   strncpy(my_filename, gtk_file_selection_get_filename(fs), FILENAME_MAX);
69   gtk_widget_destroy(GTK_WIDGET(fs));
70 
71   if (!stat(my_filename, &buff)) {
72     window = gtk_dialog_new();
73     yes = gtk_button_new_with_label("Yes");
74     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->action_area), yes,
75 		       TRUE, TRUE, 0);
76     no = gtk_button_new_with_label("No");
77     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->action_area), no,
78 		       TRUE, TRUE, 0);
79     gtk_signal_connect_object(GTK_OBJECT(window), "delete_event",
80 			      GTK_SIGNAL_FUNC(gtk_widget_destroy),
81 			      GTK_OBJECT(window));
82     gtk_signal_connect(GTK_OBJECT(yes), "clicked",
83 		       GTK_SIGNAL_FUNC(yes_sel),
84 		       GTK_OBJECT(window));
85     gtk_signal_connect_object(GTK_OBJECT(no), "clicked",
86 			      GTK_SIGNAL_FUNC(gtk_widget_destroy),
87 			      GTK_OBJECT(window));
88     gtk_widget_show(yes);
89     gtk_widget_show(no);
90     sprintf(error, "\n  Overwrite existing file %s?  \n", my_filename);
91     label = gtk_label_new(error);
92     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), label, TRUE, TRUE, 0);
93     gtk_widget_show(label);
94     gtk_widget_show(window);
95     gtk_grab_add(window);
96   } else
97     savefile(my_filename);
98 }
99 
100 /* get a file name for load (0) or save (1) */
101 void
102 LoadSaveFile(int save)
103 {
104   GtkWidget *filew;
105 
106   filew = gtk_file_selection_new(save ? "Save File": "Load File");
107   gtk_signal_connect_object(GTK_OBJECT(filew), "delete_event",
108 			    GTK_SIGNAL_FUNC(gtk_widget_destroy),
109 			    GTK_OBJECT(filew));
110   gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filew)->ok_button),
111 		     "clicked", save ? GTK_SIGNAL_FUNC(savefile_ok_sel)
112 		     : GTK_SIGNAL_FUNC(loadfile_ok_sel),
113 		     GTK_OBJECT(filew));
114   gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(filew)
115 				       ->cancel_button), "clicked",
116 			    GTK_SIGNAL_FUNC(gtk_widget_destroy),
117 			    GTK_OBJECT(filew));
118  if (my_filename[0] != '\0')
119    gtk_file_selection_set_filename(GTK_FILE_SELECTION(filew),
120 				   (gchar *)my_filename);
121   gtk_widget_show(filew);
122 }
123 
124 void
125 run_sel(GtkWidget *w, GtkEntry *command)
126 {
127   startcommand(gtk_entry_get_text(GTK_ENTRY(command)));
128 }
129 
130 void
131 ExternCommand()
132 {
133   GtkWidget *window, *label, *command, *run, *cancel;
134   GList *glist = NULL;
135 
136   if (fixing_widgets) return;
137 
138   glist = g_list_append(glist, "xy");
139   glist = g_list_append(glist, "ofreq");
140   glist = g_list_append(glist, "operl 'abs($x)'");
141   glist = g_list_append(glist, "operl '$x - $x[0]'");
142   glist = g_list_append(glist, "operl '$x / ($y || 1)'");
143   glist = g_list_append(glist, "operl '$x > $y ? $x : $y'");
144   glist = g_list_append(glist, "operl '$t / (44100/2/250) % 2 ? 64 : -64'");
145   glist = g_list_append(glist, "operl 'sin($t * $pi / (44100/2/250)) * 64'");
146   glist = g_list_append(glist, "operl 'cos($t * $pi / (44100/2/250)) * 64'");
147 
148   window = gtk_dialog_new();
149   run = gtk_button_new_with_label("  Run  ");
150   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->action_area), run,
151 		     TRUE, TRUE, 0);
152   cancel = gtk_button_new_with_label("  Cancel  ");
153   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->action_area), cancel,
154 		     TRUE, TRUE, 0);
155   label = gtk_label_new("\n  External command and args:  \n");
156   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), label,
157 		     TRUE, TRUE, 0);
158   command = gtk_combo_new();
159   gtk_combo_set_popdown_strings(GTK_COMBO(command), glist);
160 
161   /* XXX recall previous command that was set here */
162 #if 0
163   gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(command)->entry),
164 		     ch[scope.select].command);
165 #else
166   gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(command)->entry),
167 		     "operl '$x + $y'");
168 #endif
169   gtk_combo_set_value_in_list(GTK_COMBO(command), FALSE, FALSE);
170   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), command,
171 		     TRUE, TRUE, 0);
172   gtk_signal_connect_object(GTK_OBJECT(window), "delete_event",
173 			    GTK_SIGNAL_FUNC(gtk_widget_destroy),
174 			    GTK_OBJECT(window));
175   gtk_signal_connect(GTK_OBJECT(run), "clicked",
176 		     GTK_SIGNAL_FUNC(run_sel),
177 		     GTK_ENTRY(GTK_COMBO(command)->entry));
178   gtk_signal_connect_object_after(GTK_OBJECT(run), "clicked",
179 				  GTK_SIGNAL_FUNC(gtk_widget_destroy),
180 				  GTK_OBJECT(window));
181   gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked",
182 			    GTK_SIGNAL_FUNC(gtk_widget_destroy),
183 			    GTK_OBJECT(window));
184   GTK_WIDGET_SET_FLAGS(run, GTK_CAN_DEFAULT);
185   gtk_widget_grab_default(run);
186   gtk_widget_show(run);
187   gtk_widget_show(cancel);
188   gtk_widget_show(label);
189   gtk_widget_show(command);
190   gtk_widget_show(window);
191   /*    gtk_grab_add(window); */
192 }
193 
194 /* menu option callbacks */
195 
196 void
197 datasource(GtkWidget *w, gpointer data)
198 {
199   if (fixing_widgets) return;
200   if (datasrc_byname(data)) {
201     clear();
202   }
203 }
204 
205 void
206 option_dialog(GtkWidget *w, guint data)
207 {
208   if (fixing_widgets) return;
209   if (datasrc && datasrc->gtk_options) datasrc->gtk_options();
210 }
211 
212 void
213 plotmode(GtkWidget *w, guint data)
214 {
215   if (fixing_widgets) return;
216   scope.mode = data;
217   clear();
218 }
219 
220 void
221 runmode(GtkWidget *w, guint data)
222 {
223   if (fixing_widgets) return;
224   scope.run = data;
225   clear();
226 }
227 
228 void
229 graticule(GtkWidget *w, guint data)
230 {
231   if (fixing_widgets) return;
232   if (data < 2)
233     scope.behind = data;
234   else
235     scope.grat = data - 2;
236   clear();
237 }
238 
239 void
240 change_trigger(int trigch, int trig, int trige)
241 {
writefile(char * filename)242   /* Triggering change.  Try the new trigger, and if it doesn't work,
243    * try the old one settings again, if they don't work (!) leave
244    * the trigger off.
245    */
246 
247   if (trige == 0) {
248     if (datasrc && datasrc->clear_trigger) datasrc->clear_trigger();
249     scope.trige = 0;
250   } else if (datasrc && datasrc->set_trigger
251 	     && datasrc->set_trigger(trigch, &trig, trige)) {
252     scope.trigch = trigch;
253     scope.trig = trig;
254     scope.trige = trige;
255   } else if (datasrc && datasrc->set_trigger && datasrc->clear_trigger
256 	     && !datasrc->set_trigger(scope.trigch, &scope.trig, scope.trige)){
257     datasrc->clear_trigger();
258     scope.trige = 0;
259   }
260 }
261 
262 void
263 trigger(GtkWidget *w, guint data)
264 {
265   if (fixing_widgets) return;
266 
267   if (data >= 'a' && data <= 'c') {
268     change_trigger(scope.trigch, scope.trig, data - 'a');
269   }
270 
271   if (data >= '1' && data <= '8') {
272     change_trigger(data - '1', scope.trig, scope.trige);
273   }
274 
275   clear();
276 }
277 
278 /* Radio buttons cause 2 events: the deselect and the select.
279    Selecting a built-in after external causes an extraneous command
280    dialog but I can't figure out how to get rid of it.  */
281 void
282 mathselect(GtkWidget *w, guint data)
283 {
284   if (fixing_widgets) return;
285   if (scope.select > 1) {
286     if (data == '$') {
287 /*        if (GTK_CHECK_MENU_ITEM */
288 /*  	 (gtk_item_factory_get_item */
289 /*  	  (factory, "/Channel/Math/External Command..."))->active) */
290 	handle_key('$');
291     } else {
292       function_bynum_on_channel(data - '0', &ch[scope.select]);
293       ch[scope.select].show = 1;
294     }
295     clear();
296   }
297 }
298 
299 void
300 setbits(GtkWidget *w, guint data)
301 {
302   if (fixing_widgets) return;
303   ch[scope.select].bits = data;
304   clear();
305 }
306 
307 void
308 setscale(GtkWidget *w, guint data)
309 {
310   int mul[] = {50, 20, 10, 5, 2, 1, 1, 1,  1,  1,  1};
311   int div[] = {1,   1,  1, 1, 1, 1, 2, 5, 10, 20, 50};
312 
313   ch[scope.select].target_mult = mul[data];
314   ch[scope.select].target_div = div[data];
315   clear();
316 }
317 
318 void
319 setposition(GtkWidget *w, guint data)
320 {
321   if (data >= 100) {
322     change_trigger(scope.trigch, (data - 100) * 8, scope.trige);
323   } else if (data <= -100) {
324     change_trigger(scope.trigch, (data + 100) * 8, scope.trige);
325   } else {
326     ch[scope.select].pos = data * 16;
327   }
328   clear();
329 }
330 
331 /* slurp the manual page into a window */
332 void
333 help(GtkWidget *w, void *data)
guess_input_device_pre_1_10(char * filename)334 {
335   char c;
336   char charbuffer[16];
337   int charbuffer_len = 0;
338   int running_tag = 0;
339   FILE *p;
340 
341   GtkWidget *window;
342   GtkWidget *box1;
343   GtkWidget *box2;
344   GtkWidget *button;
345   GtkWidget *text;
346   GtkWidget *scrolled_window;
347   GtkTextBuffer *textbuffer;
348   PangoFontDescription *font_desc;
349   GtkTextIter iter, start;
350   GtkTextTag *underline_tag, *bold_tag;
351 
352   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
353   gtk_widget_set_usize (window, 640, 480);
354   gtk_window_set_policy (GTK_WINDOW(window), TRUE, TRUE, FALSE);
355   gtk_signal_connect_object(GTK_OBJECT (window), "destroy",
356 			    GTK_SIGNAL_FUNC(gtk_widget_destroy),
357 			    GTK_OBJECT(window));
358   gtk_window_set_title (GTK_WINDOW (window), "xoscope(1) man page");
359   gtk_container_border_width (GTK_CONTAINER (window), 0);
360 
361   box1 = gtk_vbox_new (FALSE, 0);
362   gtk_container_add (GTK_CONTAINER (window), box1);
363   gtk_widget_show (box1);
364 
365   box2 = gtk_vbox_new (FALSE, 10);
366   gtk_container_border_width (GTK_CONTAINER (box2), 10);
367   gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
368   gtk_widget_show (box2);
369 
370   /* Create the GtkTextView widget */
371   text = gtk_text_view_new();
372   gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
373   gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_NONE);
374   gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
375 
376   /* Use a fixed width font throughout the widget */
377   font_desc = pango_font_description_from_string ("Courier 10");
378   gtk_widget_modify_font (text, font_desc);
379   pango_font_description_free (font_desc);
380 
381   /* Add scrollbars (if needed) to the GtkTextView widget */
382   scrolled_window = gtk_scrolled_window_new(NULL, NULL);
383   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
384 				 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
385   gtk_container_add(GTK_CONTAINER(scrolled_window), text);
386   gtk_box_pack_start (GTK_BOX (box2), scrolled_window, TRUE, TRUE, 0);
387   gtk_widget_show(scrolled_window);
388   gtk_widget_show(text);
389 
390 
391   /* Realizing a widget creates a window for it, ready to insert some text */
392   gtk_widget_realize (text);
393 
394   textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
395 
396   bold_tag = gtk_text_buffer_create_tag (textbuffer, NULL,
397 					 "weight", PANGO_WEIGHT_BOLD, NULL);
398   underline_tag = gtk_text_buffer_create_tag (textbuffer, NULL,
399 					      "underline", PANGO_UNDERLINE_SINGLE, NULL);
400 
401   /* Now run 'man' and copy its output into the text buffer.  We use
402    * an intermediate 'charbuffer' for two reasons: to handle
403    * backspaces (for overstrikes or underlines) which get converted
404    * into formatting tags, and to ensure that multibyte UTF-8
405    * characters get inserted as a unit; otherwise GTK+ 2 complains.
406    *
407    * XXX this can hang the program if the 'man' runs slowly
408    *
409    * XXX we're counting on GNU man to handle the '-Tutf8' flag; other
410    * versions of man might not do this.
411    *
412    * XXX the tags are attached individually to each character; it
413    * would be better to attach them to entire words, as this could
414    * conceivably affect formating, especially of underlines
415    */
416 
417   gtk_text_buffer_get_end_iter(textbuffer, &iter);
418 
419   if ((p = popen(HELPCOMMAND, "r")) != NULL) {
420 
421     while ((c = fgetc(p)) != EOF) {
422       if (c == '\b') {
423 	if (charbuffer[0] == '_') running_tag = 1;
424 	else running_tag = 2;
425 	charbuffer_len = 0;
426 	continue;
427       }
428       if (c < 0) {
429 	charbuffer[charbuffer_len ++] = c;
430       } else {
431 	if (charbuffer_len > 0) {
432 	  gtk_text_buffer_insert(textbuffer, &iter,
433 				 charbuffer, charbuffer_len);
434 	  if (running_tag) {
435 	    start = iter;
436 	    gtk_text_iter_backward_char(&start);
437 	    if (running_tag == 1) {
438 	      gtk_text_buffer_apply_tag (textbuffer, underline_tag,
439 					 &start, &iter);
440 	    } else if (running_tag == 2) {
441 	      gtk_text_buffer_apply_tag (textbuffer, bold_tag,
442 					 &start, &iter);
443 	    }
444 	    running_tag = 0;
445 	  }
446 	}
447 	charbuffer[0] = c;
448 	charbuffer_len = 1;
449       }
450     }
451     pclose(p);
452   }
453 
454   box2 = gtk_vbox_new (FALSE, 10);
455   gtk_container_border_width (GTK_CONTAINER (box2), 10);
456   gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
457   gtk_widget_show (box2);
458 
459   button = gtk_button_new_with_label ("close");
460   gtk_signal_connect_object(GTK_OBJECT (button), "clicked",
461 			    GTK_SIGNAL_FUNC(gtk_widget_destroy),
462 			    GTK_OBJECT(window));
463   gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
464   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
465   gtk_widget_grab_default (button);
466   gtk_widget_show (button);
467 
468   gtk_widget_show (window);
469 }
470 
471 void
472 cleanup_display()
473 {
474 }
475 
476 /* WARNING: if the menu system is rearranged, the finditem-based logic
477    in fix_widgets may need updated as well.  */
478 
479 static GtkItemFactoryEntry menu_items[] =
480 {
481   {"/File", NULL, NULL, 0, "<Branch>"},
482   {"/File/tear", NULL, NULL, 0, "<Tearoff>"},
483   /*     {"/File/New", "<control>N", print_hello, NULL, NULL}, */
484   {"/File/Open...", NULL, hit_key, '@', NULL},
485   {"/File/Save...", NULL, hit_key, '#', NULL},
486   /*     {"/File/Save as", NULL, NULL, 0, NULL}, */
487   {"/File/Device/tear", NULL, NULL, 0, "<Tearoff>"},
488   {"/File/Device Options...", NULL, option_dialog, 0, NULL},
489   {"/File/sep", NULL, NULL, 0, "<Separator>"},
490   {"/File/Quit", NULL, hit_key, '\e', NULL},
491 
492   {"/Channel", NULL, NULL, 0, "<Branch>"},
493   {"/Channel/tear", NULL, NULL, 0, "<Tearoff>"},
494   {"/Channel/Channel 1", NULL, hit_key, '1', "<RadioItem>"},
495   {"/Channel/Channel 2", NULL, hit_key, '2', "/Channel/Channel 1"},
496   {"/Channel/Channel 3", NULL, hit_key, '3', "/Channel/Channel 2"},
497   {"/Channel/Channel 4", NULL, hit_key, '4', "/Channel/Channel 3"},
498   {"/Channel/Channel 5", NULL, hit_key, '5', "/Channel/Channel 4"},
499   {"/Channel/Channel 6", NULL, hit_key, '6', "/Channel/Channel 5"},
500   {"/Channel/Channel 7", NULL, hit_key, '7', "/Channel/Channel 6"},
501   {"/Channel/Channel 8", NULL, hit_key, '8', "/Channel/Channel 7"},
502   {"/Channel/sep", NULL, NULL, 0, "<Separator>"},
503   {"/Channel/Show", NULL, hit_key, '\t', "<CheckItem>"},
504 
505   {"/Channel/Scale", NULL, NULL, 0, "<Branch>"},
506   {"/Channel/Scale/tear", NULL, NULL, 0, "<Tearoff>"},
507   {"/Channel/Scale/up", NULL, hit_key, '}', NULL},
508   {"/Channel/Scale/down", NULL, hit_key, '{', NULL},
509   {"/Channel/Scale/sep", NULL, NULL, 0, "<Separator>"},
510   {"/Channel/Scale/50", NULL, setscale, 0, NULL},
511   {"/Channel/Scale/20", NULL, setscale, 1, NULL},
512   {"/Channel/Scale/10", NULL, setscale, 2, NULL},
513   {"/Channel/Scale/5", NULL, setscale, 3, NULL},
514   {"/Channel/Scale/2", NULL, setscale, 4, NULL},
515   {"/Channel/Scale/1", NULL, setscale, 5, NULL},
516   /* How the ? do you put a / in a menu ? Just use \ until I figure it out. */
517   {"/Channel/Scale/1\\2", NULL, setscale, 6, NULL},
518   {"/Channel/Scale/1\\5", NULL, setscale, 7, NULL},
519   {"/Channel/Scale/1\\10", NULL, setscale, 8, NULL},
520   {"/Channel/Scale/1\\20", NULL, setscale, 9, NULL},
521   {"/Channel/Scale/1\\50", NULL, setscale, 10, NULL},
522 
523   {"/Channel/Position", NULL, NULL, 0, "<Branch>"},
524   {"/Channel/Position/tear", NULL, NULL, 0, "<Tearoff>"},
525   {"/Channel/Position/up", "]", hit_key, ']', NULL},
526   {"/Channel/Position/down", "[", hit_key, '[', NULL},
527   {"/Channel/Position/sep", NULL, NULL, 0, "<Separator>"},
528   {"/Channel/Position/160", NULL, setposition, 10, NULL},
529   {"/Channel/Position/144", NULL, setposition, 9, NULL},
530   {"/Channel/Position/128", NULL, setposition, 8, NULL},
531   {"/Channel/Position/112", NULL, setposition, 7, NULL},
532   {"/Channel/Position/96", NULL, setposition, 6, NULL},
533   {"/Channel/Position/80", NULL, setposition, 5, NULL},
534   {"/Channel/Position/64", NULL, setposition, 4, NULL},
535   {"/Channel/Position/48", NULL, setposition, 3, NULL},
536   {"/Channel/Position/32", NULL, setposition, 2, NULL},
537   {"/Channel/Position/16", NULL, setposition, 1, NULL},
538   {"/Channel/Position/0", NULL, setposition, 0, NULL},
539   {"/Channel/Position/-16", NULL, setposition, -1, NULL},
540   {"/Channel/Position/-32", NULL, setposition, -2, NULL},
541   {"/Channel/Position/-48", NULL, setposition, -3, NULL},
542   {"/Channel/Position/-64", NULL, setposition, -4, NULL},
543   {"/Channel/Position/-80", NULL, setposition, -5, NULL},
544   {"/Channel/Position/-96", NULL, setposition, -6, NULL},
545   {"/Channel/Position/-112", NULL, setposition, -7, NULL},
546   {"/Channel/Position/-128", NULL, setposition, -8, NULL},
547   {"/Channel/Position/-144", NULL, setposition, -9, NULL},
548   {"/Channel/Position/-160", NULL, setposition, -10, NULL},
549 
550   {"/Channel/Bits", NULL, NULL, 0, "<Branch>"},
551   {"/Channel/Bits/tear", NULL, NULL, 0, "<Tearoff>"},
552   {"/Channel/Bits/Analog", NULL, setbits, 0, "<RadioItem>"},
553   {"/Channel/Bits/2", NULL, setbits, 2, "/Channel/Bits/Analog"},
554   {"/Channel/Bits/4", NULL, setbits, 4, "/Channel/Bits/2"},
555   {"/Channel/Bits/6", NULL, setbits, 6, "/Channel/Bits/4"},
556   {"/Channel/Bits/8", NULL, setbits, 8, "/Channel/Bits/6"},
557   {"/Channel/Bits/10", NULL, setbits, 10, "/Channel/Bits/8"},
558   {"/Channel/Bits/12", NULL, setbits, 12, "/Channel/Bits/10"},
559   {"/Channel/Bits/14", NULL, setbits, 14, "/Channel/Bits/12"},
560   {"/Channel/Bits/16", NULL, setbits, 16, "/Channel/Bits/14"},
561 
562   {"/Channel/sep", NULL, NULL, 0, "<Separator>"},
563   {"/Channel/Math", NULL, NULL, 0, "<Branch>"},
564   {"/Channel/Math/tear", NULL, NULL, 0, "<Tearoff>"},
565   {"/Channel/Math/Prev Function", ":", hit_key, ':', NULL},
566   {"/Channel/Math/Next Function", ";", hit_key, ';', NULL},
567   {"/Channel/Math/sep", NULL, NULL, 0, "<Separator>"},
568 
569   /* this will need hacked if functions are added / changed in func.c */
570   {"/Channel/Math/Other", NULL, NULL, 0, "<RadioItem>"},
571   {"/Channel/Math/External Command...", "$", mathselect, '$', "/Channel/Math/Other"},
572   {"/Channel/Math/Inv. 1", NULL, mathselect, '0', "/Channel/Math/External Command..."},
573   {"/Channel/Math/Inv. 2", NULL, mathselect, '1', "/Channel/Math/Inv. 1"},
574   {"/Channel/Math/Sum  1+2", NULL, mathselect, '2', "/Channel/Math/Inv. 2"},
575   {"/Channel/Math/Diff 1-2", NULL, mathselect, '3', "/Channel/Math/Sum  1+2"},
576   {"/Channel/Math/Avg. 1,2", NULL, mathselect, '4', "/Channel/Math/Diff 1-2"},
577   {"/Channel/Math/FFT. 1", NULL, mathselect, '5', "/Channel/Math/Avg. 1,2"},
578   {"/Channel/Math/FFT. 2", NULL, mathselect, '6', "/Channel/Math/FFT. 1"},
579 
580   {"/Channel/Store", NULL, NULL, 0, "<Branch>"},
581   {"/Channel/Store/tear", NULL, NULL, 0, "<Tearoff>"},
582   {"/Channel/Store/Mem A", "A", hit_key, 'A', "<CheckItem>"},
583   {"/Channel/Store/Mem B", "B", hit_key, 'B', "<CheckItem>"},
584   {"/Channel/Store/Mem C", "C", hit_key, 'C', "<CheckItem>"},
585   {"/Channel/Store/Mem D", "D", hit_key, 'D', "<CheckItem>"},
586   {"/Channel/Store/Mem E", "E", hit_key, 'E', "<CheckItem>"},
587   {"/Channel/Store/Mem F", "F", hit_key, 'F', "<CheckItem>"},
588   {"/Channel/Store/Mem G", "G", hit_key, 'G', "<CheckItem>"},
589   {"/Channel/Store/Mem H", "H", hit_key, 'H', "<CheckItem>"},
590   {"/Channel/Store/Mem I", "I", hit_key, 'I', "<CheckItem>"},
591   {"/Channel/Store/Mem J", "J", hit_key, 'J', "<CheckItem>"},
592   {"/Channel/Store/Mem K", "K", hit_key, 'K', "<CheckItem>"},
593   {"/Channel/Store/Mem L", "L", hit_key, 'L', "<CheckItem>"},
594   {"/Channel/Store/Mem M", "M", hit_key, 'M', "<CheckItem>"},
595   {"/Channel/Store/Mem N", "N", hit_key, 'N', "<CheckItem>"},
596   {"/Channel/Store/Mem O", "O", hit_key, 'O', "<CheckItem>"},
597   {"/Channel/Store/Mem P", "P", hit_key, 'P', "<CheckItem>"},
598   {"/Channel/Store/Mem Q", "Q", hit_key, 'Q', "<CheckItem>"},
599   {"/Channel/Store/Mem R", "R", hit_key, 'R', "<CheckItem>"},
600   {"/Channel/Store/Mem S", "S", hit_key, 'S', "<CheckItem>"},
601   {"/Channel/Store/Mem T", "T", hit_key, 'T', "<CheckItem>"},
602   {"/Channel/Store/Mem U", "U", hit_key, 'U', "<CheckItem>"},
603   {"/Channel/Store/Mem V", "V", hit_key, 'V', "<CheckItem>"},
604   {"/Channel/Store/Mem W", "W", hit_key, 'W', "<CheckItem>"},
605   {"/Channel/Store/Mem X", "X", hit_key, 'X', "<CheckItem>"},
606   {"/Channel/Store/Mem Y", "Y", hit_key, 'Y', "<CheckItem>"},
607   {"/Channel/Store/Mem Z", "Z", hit_key, 'Z', "<CheckItem>"},
608 
609   {"/Channel/Recall", NULL, NULL, 0, "<Branch>"},
610   {"/Channel/Recall/tear", NULL, NULL, 0, "<Tearoff>"},
611   //  {"/Channel/Recall/sep", NULL, NULL, 0, "<Separator>"},
612   {"/Channel/Recall/Mem A", "a", hit_key, 'a', NULL},
613   {"/Channel/Recall/Mem B", "b", hit_key, 'b', NULL},
614   {"/Channel/Recall/Mem C", "c", hit_key, 'c', NULL},
615   {"/Channel/Recall/Mem D", "d", hit_key, 'd', NULL},
616   {"/Channel/Recall/Mem E", "e", hit_key, 'e', NULL},
617   {"/Channel/Recall/Mem F", "f", hit_key, 'f', NULL},
618   {"/Channel/Recall/Mem G", "g", hit_key, 'g', NULL},
619   {"/Channel/Recall/Mem H", "h", hit_key, 'h', NULL},
620   {"/Channel/Recall/Mem I", "i", hit_key, 'i', NULL},
621   {"/Channel/Recall/Mem J", "j", hit_key, 'j', NULL},
622   {"/Channel/Recall/Mem K", "k", hit_key, 'k', NULL},
623   {"/Channel/Recall/Mem L", "l", hit_key, 'l', NULL},
624   {"/Channel/Recall/Mem M", "m", hit_key, 'm', NULL},
625   {"/Channel/Recall/Mem N", "n", hit_key, 'n', NULL},
626   {"/Channel/Recall/Mem O", "o", hit_key, 'o', NULL},
627   {"/Channel/Recall/Mem P", "p", hit_key, 'p', NULL},
628   {"/Channel/Recall/Mem Q", "q", hit_key, 'q', NULL},
629   {"/Channel/Recall/Mem R", "r", hit_key, 'r', NULL},
630   {"/Channel/Recall/Mem S", "s", hit_key, 's', NULL},
631   {"/Channel/Recall/Mem T", "t", hit_key, 't', NULL},
632   {"/Channel/Recall/Mem U", "u", hit_key, 'u', NULL},
633   {"/Channel/Recall/Mem V", "v", hit_key, 'v', NULL},
634   {"/Channel/Recall/Mem W", "w", hit_key, 'w', NULL},
635   {"/Channel/Recall/Mem X", "x", hit_key, 'x', NULL},
636   {"/Channel/Recall/Mem Y", "y", hit_key, 'y', NULL},
637   {"/Channel/Recall/Mem Z", "z", hit_key, 'z', NULL},
638 
639   {"/Trigger", NULL, NULL, 0, "<Branch>"},
640   {"/Trigger/tear", NULL, NULL, 0, "<Tearoff>"},
641   {"/Trigger/Off", NULL, trigger, 'a', "<RadioItem>"},
642   {"/Trigger/Rising", NULL, trigger, 'b', "/Trigger/Off"},
643   {"/Trigger/Falling", NULL, trigger, 'c', "/Trigger/Rising"},
644   {"/Trigger/sep", NULL, NULL, 0, "<Separator>"},
645   {"/Trigger/Channel 1", NULL, trigger, '1', "<RadioItem>"},
646   {"/Trigger/Channel 2", NULL, trigger, '2', "/Trigger/Channel 1"},
647   {"/Trigger/Channel 3", NULL, trigger, '3', "/Trigger/Channel 1"},
648   {"/Trigger/Channel 4", NULL, trigger, '4', "/Trigger/Channel 1"},
649   {"/Trigger/Channel 5", NULL, trigger, '5', "/Trigger/Channel 1"},
650   {"/Trigger/Channel 6", NULL, trigger, '6', "/Trigger/Channel 1"},
651   {"/Trigger/Channel 7", NULL, trigger, '7', "/Trigger/Channel 1"},
652   {"/Trigger/Channel 8", NULL, trigger, '8', "/Trigger/Channel 1"},
653   {"/Trigger/sep", NULL, NULL, 0, "<Separator>"},
654   {"/Trigger/Position up", "=", hit_key, '=', NULL},
655   {"/Trigger/Position down", "-", hit_key, '-', NULL},
656   {"/Trigger/Position Positive", NULL, NULL, 0, "<Branch>"},
657   {"/Trigger/Position Positive/120", NULL, setposition, 115, NULL},
658   {"/Trigger/Position Positive/112", NULL, setposition, 114, NULL},
659   {"/Trigger/Position Positive/104", NULL, setposition, 113, NULL},
660   {"/Trigger/Position Positive/96", NULL, setposition, 112, NULL},
661   {"/Trigger/Position Positive/88", NULL, setposition, 111, NULL},
662   {"/Trigger/Position Positive/80", NULL, setposition, 110, NULL},
663   {"/Trigger/Position Positive/72", NULL, setposition, 109, NULL},
664   {"/Trigger/Position Positive/64", NULL, setposition, 108, NULL},
665   {"/Trigger/Position Positive/56", NULL, setposition, 107, NULL},
666   {"/Trigger/Position Positive/48", NULL, setposition, 106, NULL},
667   {"/Trigger/Position Positive/40", NULL, setposition, 105, NULL},
668   {"/Trigger/Position Positive/32", NULL, setposition, 104, NULL},
669   {"/Trigger/Position Positive/24", NULL, setposition, 103, NULL},
670   {"/Trigger/Position Positive/16", NULL, setposition, 102, NULL},
671   {"/Trigger/Position Positive/8", NULL, setposition, 101, NULL},
672   {"/Trigger/Position Positive/0", NULL, setposition, 100, NULL},
673   {"/Trigger/Position Negative", NULL, NULL, 0, "<Branch>"},
674   {"/Trigger/Position Negative/0", NULL, setposition, -100, NULL},
675   {"/Trigger/Position Negative/-8", NULL, setposition, -101, NULL},
676   {"/Trigger/Position Negative/-16", NULL, setposition, -102, NULL},
677   {"/Trigger/Position Negative/-24", NULL, setposition, -103, NULL},
678   {"/Trigger/Position Negative/-32", NULL, setposition, -104, NULL},
679   {"/Trigger/Position Negative/-40", NULL, setposition, -105, NULL},
680   {"/Trigger/Position Negative/-48", NULL, setposition, -106, NULL},
681   {"/Trigger/Position Negative/-56", NULL, setposition, -107, NULL},
682   {"/Trigger/Position Negative/-64", NULL, setposition, -108, NULL},
683   {"/Trigger/Position Negative/-72", NULL, setposition, -109, NULL},
684   {"/Trigger/Position Negative/-80", NULL, setposition, -110, NULL},
685   {"/Trigger/Position Negative/-88", NULL, setposition, -111, NULL},
686   {"/Trigger/Position Negative/-96", NULL, setposition, -112, NULL},
687   {"/Trigger/Position Negative/-104", NULL, setposition, -113, NULL},
688   {"/Trigger/Position Negative/-112", NULL, setposition, -114, NULL},
689   {"/Trigger/Position Negative/-120", NULL, setposition, -115, NULL},
690   {"/Trigger/Position Negative/-128", NULL, setposition, -116, NULL},
691 
692   {"/Scope", NULL, NULL, 0, "<Branch>"},
693   {"/Scope/tear", NULL, NULL, 0, "<Tearoff>"},
694   {"/Scope/Run", NULL, runmode, 1, "<RadioItem>"},
695   {"/Scope/Wait", NULL, runmode, 2, "/Scope/Run"},
696   {"/Scope/Stop", NULL, runmode, 0, "/Scope/Wait"},
697   {"/Scope/sep", NULL, NULL, 0, "<Separator>"},
698   {"/Scope/Slower Time Base", NULL, hit_key, '9', NULL},
699   {"/Scope/Faster Time Base", NULL, hit_key, '0', NULL},
700   {"/Scope/Slower Sample Rate", NULL, hit_key, '(', NULL},
701   {"/Scope/Faster Sample Rate", NULL, hit_key, ')', NULL},
702   {"/Scope/sep", NULL, NULL, 0, "<Separator>"},
703   {"/Scope/Refresh", NULL, hit_key, '\n', NULL},
704   {"/Scope/Plot Mode/tear", NULL, NULL, 0, "<Tearoff>"},
705   {"/Scope/Plot Mode/Point", NULL, plotmode, 0, "<RadioItem>"},
706   {"/Scope/Plot Mode/Point Accumulate", NULL, plotmode, 1, "/Scope/Plot Mode/Point"},
707   {"/Scope/Plot Mode/Line", NULL, plotmode, 2, "/Scope/Plot Mode/Point Accumulate"},
708   {"/Scope/Plot Mode/Line Accumulate", NULL, plotmode, 3, "/Scope/Plot Mode/Line"},
709   {"/Scope/Plot Mode/Step", NULL, plotmode, 4, "/Scope/Plot Mode/Line Accumulate"},
710   {"/Scope/Plot Mode/Step Accumulate", NULL, plotmode, 5, "/Scope/Plot Mode/Step"},
711   {"/Scope/Graticule/tear", NULL, NULL, 0, "<Tearoff>"},
712   {"/Scope/Graticule/In Front", NULL, graticule, 0, "<RadioItem>"},
713   {"/Scope/Graticule/Behind", NULL, graticule, 1, "/Scope/Graticule/In Front"},
714   {"/Scope/Graticule/sep", NULL, NULL, 0, "<Separator>"},
715   {"/Scope/Graticule/None", NULL, graticule, 2, "<RadioItem>"},
716   {"/Scope/Graticule/Minor Divisions", NULL, graticule, 3, "/Scope/Graticule/None"},
717   {"/Scope/Graticule/Minor & Major", NULL, graticule, 4, "/Scope/Graticule/Minor Divisions"},
718   {"/Scope/Cursors", NULL, hit_key, '\'', "<CheckItem>"},
719 
720   {"/Help", NULL, NULL, 0, "<LastBranch>"},
721   {"/Help/Keys&Info", NULL, hit_key, '?', "<CheckItem>"},
722   {"/Help/Manual", NULL, help, 0, NULL},
723 
724 };
725 gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
726 
727 /* return the first menu item containing str, or NULL if none */
728 GtkItemFactoryEntry *
729 finditem(char *str)
730 {
731   int i;
732 
733   for (i = 0; i < nmenu_items; i++) {
734     if (strstr(menu_items[i].path, str))
735       return &menu_items[i];
736   }
737   return NULL;
738 }
739 
740 /* set current state colors, labels, and check marks on widgets */
741 void
742 fix_widgets()
743 {
744   GtkItemFactoryEntry *p, *q, *r;
745   int i;
746 
747   fixing_widgets = 1;
748   if ((p = finditem("/Channel/Channel 1"))) {
749     p += scope.select;
750     gtk_check_menu_item_set_active
751       (GTK_CHECK_MENU_ITEM
752        (gtk_item_factory_get_item(factory, p->path)), TRUE);
753   }
754   if ((p = finditem("/Channel/Bits/Analog"))) {
755     p += ch[scope.select].bits / 2;
756     gtk_check_menu_item_set_active
757       (GTK_CHECK_MENU_ITEM
758        (gtk_item_factory_get_item(factory, p->path)), TRUE);
759   }
760   gtk_check_menu_item_set_active
761     (GTK_CHECK_MENU_ITEM
762      (gtk_item_factory_get_item(factory, "/Channel/Show")),
763      ch[scope.select].show);
764 
765   if ((p = finditem("/File/Device Options..."))) {
766     gtk_widget_set_sensitive
767       (GTK_WIDGET
768        (gtk_item_factory_get_item(factory, p->path)),
769        datasrc && datasrc->gtk_options != NULL);
770   }
771 
772   if ((p = finditem("/Trigger/Off"))) {
773     q = p + scope.trige;
774     gtk_check_menu_item_set_active
775       (GTK_CHECK_MENU_ITEM
776        (gtk_item_factory_get_item(factory, q->path)), TRUE);
777   }
778 
779   /* The trigger channels.  There are eight of them defined, but we
780    * only show as many as datasrc->nchans() indicate are available.
781    * (We assume datasrc->nchans() is <= 8).  Set their labels to the
782    * names in the Signal arrays; make them all insensitive if
783    * triggering is turned off, and select the one corresponding to the
784    * triggered channel.
785    */
786 
787   if ((p = finditem("/Trigger/Channel 1"))) {
788     for (i=0; i<8; i++) {
789       GtkWidget *widget;
790       GtkLabel *label;
791 
792       q = p + i;
793       widget = GTK_WIDGET(gtk_item_factory_get_item(factory, q->path));
794 
795       if (!datasrc || i >= datasrc->nchans()) {
796 	gtk_widget_hide(widget);
797       } else {
798 	gtk_widget_show(widget);
799 	label = GTK_LABEL (GTK_BIN (widget)->child);
800 	gtk_label_set_text(label, datasrc->chan(i)->name);
801 	gtk_widget_set_sensitive(widget, scope.trige != 0);
802       }
803     }
804     q = p + scope.trigch;
805     gtk_check_menu_item_set_active
806       (GTK_CHECK_MENU_ITEM
807        (gtk_item_factory_get_item(factory, q->path)), TRUE);
808   }
809 
810   /* The triggering options should only be sensitive if we have a
811    * data source and it defines a set_trigger function.
812    */
813 
814   if ((p = finditem("/Trigger/Rising"))) {
815     gtk_widget_set_sensitive
816       (GTK_WIDGET(gtk_item_factory_get_item(factory, p->path)),
817        datasrc && datasrc->set_trigger != NULL);
818   }
819   if ((p = finditem("/Trigger/Falling"))) {
820     gtk_widget_set_sensitive
821       (GTK_WIDGET(gtk_item_factory_get_item(factory, p->path)),
822        datasrc && datasrc->set_trigger != NULL);
823   }
824 
825   /* The remaining items on the trigger menu should only be
826    * sensitive if triggering is turned on
827    */
828 
829   if ((p = finditem("/Trigger/Position up"))) {
830     gtk_widget_set_sensitive
831       (GTK_WIDGET(gtk_item_factory_get_item(factory, p->path)),
832        scope.trige != 0);
833   }
834   if ((p = finditem("/Trigger/Position down"))) {
835     gtk_widget_set_sensitive
836       (GTK_WIDGET(gtk_item_factory_get_item(factory, p->path)),
837        scope.trige != 0);
838   }
839   if ((p = finditem("/Trigger/Position Positive"))) {
840     gtk_widget_set_sensitive
841       (GTK_WIDGET(gtk_item_factory_get_item(factory, p->path)),
842        scope.trige != 0);
843   }
844   if ((p = finditem("/Trigger/Position Negative"))) {
845     gtk_widget_set_sensitive
846       (GTK_WIDGET(gtk_item_factory_get_item(factory, p->path)),
847        scope.trige != 0);
848   }
849 
850   if ((p = finditem("/Scope/Stop")) && scope.run == 0) {
851     gtk_check_menu_item_set_active
852       (GTK_CHECK_MENU_ITEM
853        (gtk_item_factory_get_item(factory, p->path)), TRUE);
854   }
855 
856   if ((p = finditem("/Scope/Run")) && scope.run == 1) {
857     gtk_check_menu_item_set_active
858       (GTK_CHECK_MENU_ITEM
859        (gtk_item_factory_get_item(factory, p->path)), TRUE);
860   }
861 
862   if ((p = finditem("/Scope/Wait")) && scope.run == 2) {
863     gtk_check_menu_item_set_active
864       (GTK_CHECK_MENU_ITEM
865        (gtk_item_factory_get_item(factory, p->path)), TRUE);
866   }
867 
868   if ((p = finditem("/Scope/Plot Mode/Point"))) {
869     p += scope.mode;
870     gtk_check_menu_item_set_active
871       (GTK_CHECK_MENU_ITEM
872        (gtk_item_factory_get_item(factory, p->path)), TRUE);
873   }
874   if ((p = finditem("/Scope/Graticule/In Front"))) {
875     q = p + scope.behind;
876     gtk_check_menu_item_set_active
877       (GTK_CHECK_MENU_ITEM
878        (gtk_item_factory_get_item(factory, q->path)), TRUE);
879     q = p + scope.grat + 3;
880     gtk_check_menu_item_set_active
881       (GTK_CHECK_MENU_ITEM
882        (gtk_item_factory_get_item(factory, q->path)), TRUE);
883   }
884   gtk_check_menu_item_set_active
885     (GTK_CHECK_MENU_ITEM
886      (gtk_item_factory_get_item(factory, "/Scope/Cursors")), scope.curs);
887 
888   gtk_check_menu_item_set_active
889     (GTK_CHECK_MENU_ITEM
890      (gtk_item_factory_get_item(factory, "/Help/Keys&Info")), scope.verbose);
891 
892   if ((p = finditem("/Channel/Math")) &&
893       (q = finditem("/Channel/Math/FFT. 2"))) {
894     for (r = p; r <= q; r++) {
895       /* XXX add a check to the function's isvalid() test and a better
896        * way to figure out which (if any) function is active
897        */
898       gtk_widget_set_sensitive
899 	(GTK_WIDGET(gtk_item_factory_get_item(factory, r->path)),
900 	 scope.select > 1);
901 
902       /* Set 'other function' active when we hit it... */
903       if (r == p + 5)
904 	gtk_check_menu_item_set_active
905 	  (GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(factory, r->path)),
906 	   TRUE);
907 
908       /* ...then look for function that actually is active (if it exists) */
909       if (ch[scope.select].signal && (r > p + 5))
910 	gtk_check_menu_item_set_active
911 	  (GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(factory, r->path)),
912 	   ch[scope.select].signal->savestr[0] == '0' + r - p - 7);
913     }
914   }
915 
916   /* Now the store and recall channels - set the names for memory or
917    * data source, and mark the channels active/sensitive if memory
918    * is stored there.  Store menu only displays options for the memory
919    * channels that are actually 'visible', i.e, not obscured by
920    * data source channels.
921    */
922 
923   if ((p = finditem("/Channel/Store/Mem A")) &&
924       (q = finditem("/Channel/Recall/Mem A")))
925     for (i = 0; i < 26; i++) {
926       GtkWidget *widget;
927       GtkLabel *label;
928       char buf[32];
929 
930       r = p + i;
931       if (datasrc && i < datasrc->nchans()) {
932 	gtk_widget_hide(GTK_WIDGET(gtk_item_factory_get_item(factory,
933 							     r->path)));
934       } else {
935 	gtk_widget_show(GTK_WIDGET(gtk_item_factory_get_item(factory,
936 							     r->path)));
937 	gtk_check_menu_item_set_active
938 	  (GTK_CHECK_MENU_ITEM
939 	   (gtk_item_factory_get_item(factory, r->path)),
940 	   mem[i].num > 0);
941       }
942       r += (q - p);
943 
944       widget = GTK_WIDGET(gtk_item_factory_get_item(factory, r->path));
945       label = GTK_LABEL (GTK_BIN (widget)->child);
946       if (datasrc && i < datasrc->nchans()) {
947 	gtk_label_set_text(label, datasrc->chan(i)->name);
948 	gtk_widget_set_sensitive(widget, TRUE);
949       } else {
950 	sprintf(buf, "Mem %c", 'A' + i);
951 	gtk_label_set_text(label, buf);
952 	gtk_widget_set_sensitive(widget, mem[i].num > 0);
953       }
954     }
955 
956 #if 0
957   if ((p = finditem("/Channel/Recall")) &&
958       (q = finditem("/Channel/Recall/sep"))) {
959     GtkWidget *widget;
960     widget = gtk_hseparator_new();
961     gtk_menu_append(GTK_MENU(gtk_item_factory_get_widget(factory,
962 							 p->path)),
963 		    widget);
964     gtk_widget_show(widget);
965 
966   }
967 #endif
968 
969 #if 0
970   {
971     GtkWidget *widget;
972     GtkWidget *radioitem;
973     GSList *group;
974 
975     radioitem = GTK_WIDGET(gtk_item_factory_get_item(factory, "/Trigger/Channel 1"));
976     group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(radioitem));
977 
978     widget = gtk_radio_menu_item_new_with_label(group, "Hi!");
979     gtk_menu_insert(GTK_MENU(gtk_item_factory_get_widget(factory,
980 						       "/Trigger")),
981 		    widget, 3);
982     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), FALSE);
983     gtk_widget_show(widget);
984   }
985 #endif
986 
987   fixing_widgets = 0;
988 }
989 
990 void
991 get_main_menu(GtkWidget *window, GtkWidget ** menubar)
992 {
993 
994   GtkWidget *menu;
995 
996 /*    GtkAccelGroup *accel_group; */
997 
998 /*    accel_group = gtk_accel_group_new (); */
999 
1000   factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", NULL);
1001   gtk_item_factory_create_items(factory, nmenu_items, menu_items, NULL);
1002 
1003 /*        gtk_accel_group_attach (accel_group, GTK_OBJECT (window)); */
1004 
1005   /* Dynamically add the device list to the File/Device menu item */
1006 
1007   menu = gtk_item_factory_get_widget(factory, "/File/Device");
1008   if (menu) {
1009     GtkWidget *p;
1010     int i;
1011 
1012     for (i = 0; i < ndatasrcs; i++) {
1013       p = gtk_menu_item_new_with_label (datasrcs[i]->name);
1014       gtk_menu_append (GTK_MENU (menu), p);
1015       gtk_signal_connect (GTK_OBJECT (p), "activate",
1016 			  GTK_SIGNAL_FUNC (datasource),
1017 			  (gpointer) (datasrcs[i]->name));
1018       gtk_widget_show (p);
1019     }
1020   }
1021 
1022 #if 0
1023   menu = gtk_item_factory_get_widget(factory, "/Trigger");
1024   if (menu) {
1025     GtkWidget *p;
1026 
1027     p = gtk_menu_item_new_with_label("Mem Brent");
1028     gtk_menu_append (GTK_MENU (menu), p);
1029     gtk_widget_show(p);
1030   }
1031 #endif
1032 
1033   if (menubar)
1034     *menubar = gtk_item_factory_get_widget(factory, "<main>");
1035 }
1036 
1037 gboolean
1038 on_databox_button_press_event          (GtkWidget       *widget,
1039                                         GdkEventButton  *event,
1040                                         gpointer         user_data)
1041 {
1042   gfloat num, l, x;
1043   int cursor;
1044   Channel *p = &ch[scope.select];
1045 
1046   /* XXX duplicates code in draw_data() */
1047   if (p->signal->rate > 0) {
1048     num = (gfloat) 1 / p->signal->rate;
1049   } else {
1050     num = (gfloat) 1 / 1000;
1051   }
1052   l = p->signal->delay * num / 10000;
1053 
1054   if (scope.curs) {
1055 #if 1
1056     GtkDataboxCoord coord;
1057     GtkDataboxValue value;
1058     coord.x = event->x;
1059     coord.y = event->y;
1060     value = gtk_databox_value_from_coord (GTK_DATABOX(databox), coord);
1061     x = value.x;
1062 #else
1063     x = gtk_databox_pixel_to_value_x (databox, event->x);
1064 #endif
1065     cursor = rintf((x - l) / num) + 1;
1066 #if 0
1067     if (event->state & GDK_BUTTON1_MASK) {
1068       scope.cursa = cursor;
1069     } else if (event->state & GDK_BUTTON2_MASK) {
1070       scope.cursb = cursor;
1071     }
1072 #else
1073     if (event->button == 1) {
1074       scope.cursa = cursor;
1075     } else if (event->button == 3) {
1076       scope.cursb = cursor;
1077     }
1078 #endif
1079   }
1080 
1081   return FALSE;
1082 }
1083 
1084 #if 0
1085 
1086 /* draggable cursor positioning */
1087 gint
1088 motion_event (GtkWidget *widget, GdkEventMotion *event)
1089 {
1090   static int x, y;
1091   GdkModifierType state;
1092 
1093   if (event->is_hint)
1094     gdk_window_get_pointer (event->window, &x, &y, &state);
1095   else {
1096     x = event->x;
1097     y = event->y;
1098     state = event->state;
1099   }
1100   if (state & GDK_BUTTON1_MASK)
1101     return positioncursor(x, y, 1);
1102   if (state & GDK_BUTTON2_MASK)
1103     return positioncursor(x, y, 2);
1104   return TRUE;
1105 }
1106 
1107 
1108 /* context sensitive mouse click select, recall and pop-up menus */
1109 gint
1110 button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
1111 {
1112   static int x, y, b;
1113 
1114   x = event->x;
1115   y = event->y;
1116   b = event->button;
1117   if (positioncursor(x, y, b))
1118     return TRUE;
1119 
1120   x = buttoncol(event->x);	/* convert graphic to text position */
1121   y = buttonrow(event->y);
1122   /*    printf("button: %d @ %f,%f -> %d,%d\n", b, event->x, event->y, x, y); */
1123   if (!y && x > 70)
1124     handle_key('?');
1125   else if (y == 28 && x >= 27 && x <= 53) {
1126     if (b > 1)
1127       gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget
1128 			      (factory, "/Channel/Store")),
1129 		     NULL, NULL, NULL, NULL, event->button, event->time);
1130     else
1131       handle_key((x - 27) + 'a');
1132   } else if (y < 4) {
1133     if (b == 1) handle_key('-');
1134     else if (b == 2) handle_key('=');
1135     else gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget
1136 				 (factory, "/Trigger")),
1137 			NULL, NULL, NULL, NULL, event->button, event->time);
1138   } else if (y == 4) {
1139     if (b < 3) handle_key('!');
1140     else gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget
1141 				 (factory, "/Scope/Plot Mode")),
1142 			NULL, NULL, NULL, NULL, event->button, event->time);
1143   }  else if (y < 25 && (x < 11 || x > 69)) {
1144     handle_key((y - 5) / 5 + '1' + (x > 69 ? 4 : 0));
1145     if (!((y - 1) % 5)) {
1146       if (b == 1) handle_key('{');
1147       else if (b == 2) handle_key('}');
1148       else gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget
1149 				   (factory, "/Channel/Scale")),
1150 			  NULL, NULL, NULL, NULL, event->button, event->time);
1151     } else if (!((y - 2) % 5)) {
1152       if (x < 4 || (x > 69 && x < 74)) {
1153 	if (b == 1) handle_key('`');
1154 	else if (b == 2) handle_key('~');
1155 	else gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget
1156 				     (factory, "/Channel/Bits")),
1157 			    NULL, NULL, NULL, NULL, event->button, event->time);
1158       } else {
1159 	if (b == 1) handle_key('[');
1160 	else if (b == 2) handle_key(']');
1161 	else gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget
1162 				     (factory, "/Channel/Position")),
1163 			    NULL, NULL, NULL, NULL, event->button, event->time);
1164       }
1165     } else if (b > 1)
1166       gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget
1167 			      (factory, "/Channel")),
1168 		     NULL, NULL, NULL, NULL, event->button, event->time);
1169   } else if (b > 1)
1170     gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget
1171 			    (factory, "/Scope")),
1172 		   NULL, NULL, NULL, NULL, event->button, event->time);
1173   return TRUE;
1174 }
1175 
1176 #endif
1177 
1178 GtkWidget *
1179 create_databox (void)
1180 {
1181    /* This is a global var - our one, unique, databox */
1182    databox = gtk_databox_new();
1183 
1184    gtk_databox_set_enable_zoom(GTK_DATABOX(databox), FALSE);
1185    gtk_databox_set_enable_selection(GTK_DATABOX(databox), FALSE);
1186 
1187    return databox;
1188 }
1189 
1190 GtkWidget * create_main_window();
1191 
1192 /* initialize all the widgets, called by init_screen in display.c */
1193 void
1194 init_widgets()
1195 {
1196   char ** xoscope_rc_ptr = xoscope_rc;
1197 
1198   /* I don't like the added complexity of having to install rc files
1199    * (and the related problems if they can't be found), so instead of
1200    * loading 'xoscope.rc', it gets compiled into the program and
1201    * loaded as a series of strings.
1202    */
1203 
1204   /* gtk_rc_parse("xoscope.rc"); */
1205   for (xoscope_rc_ptr=xoscope_rc; *xoscope_rc_ptr != NULL; xoscope_rc_ptr++) {
1206     gtk_rc_parse_string(*xoscope_rc_ptr);
1207   }
1208 
1209   glade_window = create_main_window();
1210 
1211   setup_help_text(glade_window);
1212 
1213 #if 0
1214   gtk_signal_connect(GTK_OBJECT(window), "delete_event",
1215 		     GTK_SIGNAL_FUNC(delete_event), NULL);
1216 #endif
1217 
1218   get_main_menu(glade_window, &menubar);
1219   gtk_box_pack_start(GTK_BOX(LU("vbox1")), menubar, FALSE, TRUE, 0);
1220   gtk_box_reorder_child(GTK_BOX(LU("vbox1")), menubar, 0);
1221   gtk_widget_show(menubar);
1222 
1223   gtk_databox_set_hadjustment (GTK_DATABOX (databox),
1224 			       gtk_range_get_adjustment (GTK_RANGE (LU("databox_hscrollbar"))));
1225 
1226   gtk_widget_show(glade_window);
1227 
1228 #if 0
1229   gtk_signal_connect(GTK_OBJECT(drawing_area), "motion_notify_event",
1230 		     GTK_SIGNAL_FUNC(motion_event), NULL);
1231   gtk_signal_connect(GTK_OBJECT(drawing_area), "button_press_event",
1232 		     GTK_SIGNAL_FUNC(button_event), NULL);
1233   gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
1234 			 | GDK_LEAVE_NOTIFY_MASK
1235 			 | GDK_BUTTON_PRESS_MASK
1236 			 | GDK_POINTER_MOTION_MASK
1237 			 | GDK_POINTER_MOTION_HINT_MASK);
1238 #endif
1239 
1240 }
1241 
1242 void inputCallback(gpointer data, gint source, GdkInputCondition condition)
1243 {
1244   animate(NULL);
1245 }
1246 
1247 static int input_fd = -1;
1248 static int input_tag_valid = 0;
1249 static gint input_tag;
1250 
1251 static int timeout_tag_valid = 0;
1252 static gint timeout_tag;
1253 
1254 /* GTK documentation says to return 0 if you don't want your timeout
1255  * function to be called again, or 1 if you do.  In our case,
1256  * animate() will call show_data(), which will call settimeout(),
1257  * which will remove the old timeout and possibly set a new one, and
1258  * all before the callback returns.  It's a little unclear to me what
1259  * the return value should be in this case (or if it matters)...
1260  */
1261 
1262 gint timeout_callback(gpointer data)
1263 {
1264   animate(NULL);
1265   return 0;
1266 }
1267 
1268 void settimeout(int ms)
1269 {
1270   if (timeout_tag_valid) {
1271     gtk_timeout_remove(timeout_tag);
1272     timeout_tag_valid = 0;
1273   }
1274 
1275   if (ms == 0) return;
1276 
1277   timeout_tag = gtk_timeout_add(ms, timeout_callback, 0);
1278   timeout_tag_valid = 1;
1279 }
1280 
1281 void setinputfd(int fd)
1282 {
1283   if (input_fd != fd) {
1284 
1285     if (input_tag_valid) {
1286       gdk_input_remove(input_tag);
1287       input_tag_valid = 0;
1288     }
1289 
1290     if (fd != -1) {
1291       input_tag = gdk_input_add(fd, GDK_INPUT_READ, inputCallback, NULL);
1292       input_tag_valid = 1;
1293     }
1294 
1295     input_fd = fd;
1296   }
1297 }
1298