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