1 #include "snd.h"
2 #include "snd-menu.h"
3 
4 
5 #if (!USE_NO_GUI)
edit_menu_update(void)6 void edit_menu_update(void)
7 {
8   /* called when the "Edit" top level menu is clicked -- make sure all items reflect current Snd state */
9   snd_info *selected_sp, *any_sp = NULL;
10   chan_info *cp = NULL;
11   bool has_selection = false, has_region = false, file_open = false, has_undoable_edit = false, has_redoable_edit = false;
12   selected_sp = selected_sound();
13   if (selected_sp)
14     {
15       file_open = true;
16       cp = any_selected_channel(selected_sp);
17       any_sp = selected_sp;
18     }
19   else
20     {
21       any_sp = any_selected_sound();
22       if (any_sp)
23 	{
24 	  cp = any_selected_channel(any_sp);
25 	  file_open = true;
26 	}
27     }
28   has_selection = selection_is_active();
29   has_region = region_ok(region_list_position_to_id(0));
30   if (cp)
31     {
32       has_undoable_edit = (cp->edit_ctr > 0);
33       has_redoable_edit = (!(((cp->edit_ctr + 1) == cp->edit_size) ||
34 			   (!(cp->edits[cp->edit_ctr + 1]))));
35     }
36 
37   /* is there an open sound? */
38   set_sensitive(edit_header_menu, file_open);
39 #if HAVE_EXTENSION_LANGUAGE
40   set_sensitive(edit_find_menu, file_open);
41 #endif
42   set_sensitive(edit_select_all_menu, file_open);
43 
44   /* is there an active selection? */
45   set_sensitive(edit_cut_menu, has_selection);
46 #if WITH_AUDIO
47   set_sensitive(edit_play_menu, has_selection);
48 #endif
49   set_sensitive(edit_mix_menu, has_selection);
50   set_sensitive(edit_save_as_menu, has_selection);
51   set_sensitive(edit_unselect_menu, has_selection);
52 
53   /* is there an undoable edit? */
54   set_sensitive(edit_undo_menu, has_undoable_edit);
55 
56   /* is there a redoable edit? */
57   set_sensitive(edit_redo_menu, has_redoable_edit);
58 
59   /* does paste make any sense? */
60   set_sensitive(edit_paste_menu, (file_open) && (has_selection || has_region));
61 
62   /* make sure edit-header menu option label correctly reflects current selected sound header type */
63   if (any_sp)
64     set_menu_label(edit_header_menu, (any_sp->hdr->type == MUS_RAW) ? "Add Header" : "Edit Header");
65 }
66 
67 
view_menu_update(void)68 void view_menu_update(void)
69 {
70   /* are there any viewable regions? are we even using them? */
71   if ((snd_regions() == 0) &&
72       (!(selection_creates_region(ss))))
73     {
74       deactivate_widget(view_region_menu);
75     }
76   else
77     {
78       set_sensitive(view_region_menu, snd_regions() > 0);
79       activate_widget(view_region_menu);
80     }
81 
82   /* graph_style */
83   set_sensitive(view_lines_menu,          graph_style(ss) != GRAPH_LINES);
84   set_sensitive(view_dots_menu,           graph_style(ss) != GRAPH_DOTS);
85   set_sensitive(view_filled_menu,         graph_style(ss) != GRAPH_FILLED);
86   set_sensitive(view_dots_and_lines_menu, graph_style(ss) != GRAPH_DOTS_AND_LINES);
87   set_sensitive(view_lollipops_menu,      graph_style(ss) != GRAPH_LOLLIPOPS);
88 
89   /* x axis style */
90   set_sensitive(view_x_axis_seconds_menu,    x_axis_style(ss) != X_AXIS_IN_SECONDS);
91   set_sensitive(view_x_axis_beats_menu,      x_axis_style(ss) != X_AXIS_IN_BEATS);
92   set_sensitive(view_x_axis_measures_menu,   x_axis_style(ss) != X_AXIS_IN_MEASURES);
93   set_sensitive(view_x_axis_samples_menu,    x_axis_style(ss) != X_AXIS_IN_SAMPLES);
94   set_sensitive(view_x_axis_percentage_menu, x_axis_style(ss) != X_AXIS_AS_PERCENTAGE);
95 
96   /* show y zero label */
97   set_menu_label(view_zero_menu, (show_y_zero(ss)) ? "Hide Y = 0" : "Show Y = 0");
98 
99   /* verbose cursor label */
100   set_menu_label(view_cursor_menu, (with_verbose_cursor(ss)) ? "Silent cursor" : "Verbose cursor");
101 
102 #if HAVE_EXTENSION_LANGUAGE
103   /* inset graph label */
104   set_menu_label(view_inset_menu, (with_inset_graph(ss)) ? "Without inset graph" : "With inset graph");
105 #endif
106 
107   /* channel style */
108   set_sensitive(view_combine_separate_menu,     channel_style(ss) != CHANNELS_SEPARATE);
109   set_sensitive(view_combine_combined_menu,     channel_style(ss) != CHANNELS_COMBINED);
110   set_sensitive(view_combine_superimposed_menu, channel_style(ss) != CHANNELS_SUPERIMPOSED);
111 
112   /* show axes */
113   set_sensitive(view_no_axes_menu,                show_axes(ss) != SHOW_NO_AXES);
114   set_sensitive(view_just_x_axis_menu,            show_axes(ss) != SHOW_X_AXIS);
115   set_sensitive(view_just_x_axis_unlabelled_menu, show_axes(ss) != SHOW_X_AXIS_UNLABELLED);
116   set_sensitive(view_all_axes_menu,               show_axes(ss) != SHOW_ALL_AXES);
117   set_sensitive(view_all_axes_unlabelled_menu,    show_axes(ss) != SHOW_ALL_AXES_UNLABELLED);
118   set_sensitive(view_bare_x_axis_menu,            show_axes(ss) != SHOW_BARE_X_AXIS);
119 
120 #if HAVE_EXTENSION_LANGUAGE && USE_MOTIF
121   /* make sure listener menu option label correctly reflects current listener state */
122   set_menu_label(view_listener_menu, (listener_is_visible()) ? "Hide listener" : "Show listener");
123 #endif
124 
125   set_menu_label(view_controls_menu, (in_show_controls(ss)) ? "Hide controls" : "Show controls");
126 
127   /* zoom focus style */
128   set_sensitive(view_focus_left_menu,   zoom_focus_style(ss) != ZOOM_FOCUS_LEFT);
129   set_sensitive(view_focus_right_menu,  zoom_focus_style(ss) != ZOOM_FOCUS_RIGHT);
130   set_sensitive(view_focus_middle_menu, zoom_focus_style(ss) != ZOOM_FOCUS_MIDDLE);
131   set_sensitive(view_focus_active_menu, zoom_focus_style(ss) != ZOOM_FOCUS_ACTIVE);
132 
133   /* grid menu label */
134   set_menu_label(view_grid_menu, (show_grid(ss) == WITH_GRID) ? "Without grid" : "With grid");
135 }
136 
137 
file_menu_update(void)138 void file_menu_update(void)
139 {
140   snd_info *any_sp;
141   bool file_open = false, has_edits = false;
142 
143   any_sp = any_selected_sound();
144   if (any_sp)
145     {
146       has_edits = has_unsaved_edits(any_sp);
147       file_open = true;
148     }
149 
150   set_sensitive(file_close_menu, file_open);
151   set_sensitive(file_print_menu, file_open);
152   set_sensitive(file_mix_menu, file_open);
153   set_sensitive(file_insert_menu, file_open);
154   set_sensitive(file_save_as_menu, file_open);
155   set_sensitive(file_update_menu, file_open);
156 
157   set_sensitive(file_save_menu, has_edits);
158   set_sensitive(file_revert_menu, has_edits);
159 
160   if (ss->active_sounds > 1)
161     activate_widget(file_close_all_menu);
162   else deactivate_widget(file_close_all_menu);
163 }
164 #endif
165 
166 
reflect_file_revert_in_label(snd_info * sp)167 void reflect_file_revert_in_label(snd_info *sp)
168 {
169 #if (!USE_NO_GUI)
170   bool editing;
171   editing = has_unsaved_edits(sp);
172   if (!editing)
173     set_sound_pane_file_label(sp, shortname_indexed(sp));
174 #endif
175 }
176 
177 
file_update(snd_info * sp)178 static void file_update(snd_info *sp)
179 {
180   /* here we should only update files that have changed on disk */
181   if ((sp) && (!sp->edited_region) &&
182       ((sp->need_update) ||
183        (file_write_date(sp->filename) != sp->write_date)))
184     {
185       redirect_everything_to(printout_to_status_area, (void *)sp);
186       snd_update(sp);
187       redirect_everything_to(NULL, NULL);
188     }
189 }
190 
191 
update_file_from_menu(void)192 void update_file_from_menu(void)
193 {
194   for_each_sound(file_update);
195 }
196 
197 
revert_file_from_menu(void)198 void revert_file_from_menu(void)
199 {
200   snd_info *sp;
201   sp = any_selected_sound();
202   if (sp)
203     {
204       uint32_t i;
205       for (i = 0; i < sp->nchans; i++)
206 	revert_edits(sp->chans[i]);
207       reflect_file_revert_in_label(sp);
208     }
209 }
210 
211 
212 static bool has_save_state_error = false;
213 
save_state_from_menu_error_handler(const char * msg,void * ignore)214 static void save_state_from_menu_error_handler(const char *msg, void *ignore)
215 {
216   snd_warning_without_format(msg);
217   has_save_state_error = true;
218 }
219 
220 
save_state_from_menu(void)221 void save_state_from_menu(void)
222 {
223   if (save_state_file(ss))
224     {
225       has_save_state_error = false;
226       redirect_everything_to(save_state_from_menu_error_handler, NULL);
227       save_state(save_state_file(ss));
228       redirect_everything_to(NULL, NULL);
229       if (!has_save_state_error)
230 	{
231 	  if (any_selected_sound())
232 	    status_report(any_selected_sound(), "saved state in %s", save_state_file(ss));
233 	}
234     }
235   else
236     {
237       snd_warning_without_format("can't save state: save-state-file is null");
238     }
239 }
240 
241 
242 
243 /* ---------------- extlang tie-ins ---------------- */
244 
snd_no_such_menu_error(const char * caller,Xen id)245 static Xen snd_no_such_menu_error(const char *caller, Xen id)
246 {
247   Xen_error(Xen_make_error_type("no-such-menu"),
248 	    Xen_list_3(C_string_to_Xen_string("~A: no such menu, ~A"),
249 		       C_string_to_Xen_string(caller),
250 		       id));
251   return(Xen_false);
252 }
253 
254 
255 static Xen *menu_functions = NULL;
256 static int *menu_functions_loc = NULL;
257 static int callbacks_size = 0;
258 static int callb = 0;
259 #define CALLBACK_INCR 16
260 
261 
make_callback_slot(void)262 static int make_callback_slot(void)
263 {
264   int old_callb, i;
265   for (i = 0; i < callb; i++)
266     if (Xen_is_false(menu_functions[i]))
267       return(i);
268   if (callbacks_size == callb)
269     {
270       callbacks_size += CALLBACK_INCR;
271       if (callb == 0)
272 	{
273 	  menu_functions = (Xen *)calloc(callbacks_size, sizeof(Xen));
274 	  for (i = 0; i < callbacks_size; i++) menu_functions[i] = Xen_undefined;
275 	  menu_functions_loc = (int *)calloc(callbacks_size, sizeof(int));
276 	  for (i = 0; i < callbacks_size; i++) menu_functions_loc[i] = NOT_A_GC_LOC;
277 	}
278       else
279 	{
280 	  menu_functions = (Xen *)realloc(menu_functions, callbacks_size * sizeof(Xen));
281 	  for (i = callbacks_size - CALLBACK_INCR; i < callbacks_size; i++) menu_functions[i] = Xen_undefined;
282 	  menu_functions_loc = (int *)realloc(menu_functions_loc, callbacks_size * sizeof(int));
283 	  for (i = callbacks_size - CALLBACK_INCR; i < callbacks_size; i++) menu_functions_loc[i] = NOT_A_GC_LOC;
284 	}
285     }
286   old_callb = callb;
287   callb++;
288   return(old_callb);
289 }
290 
291 
add_callback(int slot,Xen callback)292 static void add_callback(int slot, Xen callback)
293 {
294   if ((slot >= 0) && (slot < callbacks_size))
295     {
296       menu_functions[slot] = callback;
297       menu_functions_loc[slot] = snd_protect(callback);
298     }
299 }
300 
301 
unprotect_callback(int slot)302 void unprotect_callback(int slot)
303 {
304   /* called only if menu is being removed */
305   if ((slot >= 0) && (slot < callbacks_size))
306     {
307       if (Xen_is_procedure(menu_functions[slot]))
308 	{
309 	  snd_unprotect_at(menu_functions_loc[slot]);
310 	  menu_functions_loc[slot] = NOT_A_GC_LOC;
311 	}
312       menu_functions[slot] = Xen_false;  /* not Xen_undefined -- need a way to distinguish "no callback" from "recyclable slot" */
313     }
314 }
315 
316 
gl_add_to_main_menu(Xen label,Xen callback)317 static Xen gl_add_to_main_menu(Xen label, Xen callback)
318 {
319   #define H_add_to_main_menu "(" S_add_to_main_menu " label :optional callback): adds label to the main (top-level) menu, returning its index"
320   int slot = -1;
321   Xen_check_type(Xen_is_string(label), label, 1, S_add_to_main_menu, "a string");
322   slot = make_callback_slot();
323   if (Xen_is_bound(callback))
324     {
325       char *err;
326       err = procedure_ok(callback, 0, S_add_to_main_menu, "menu callback", 2);
327       if (!err)
328 	add_callback(slot, callback);
329       else
330 	{
331 	  Xen errm;
332 	  errm = C_string_to_Xen_string(err);
333 	  free(err);
334 	  return(snd_bad_arity_error(S_add_to_main_menu, errm, callback));
335 	}
336     }
337   else menu_functions[slot] = Xen_undefined;
338   return(C_int_to_Xen_integer(g_add_to_main_menu((char *)Xen_string_to_C_string(label), slot)));
339 }
340 
341 
gl_add_to_menu(Xen menu,Xen label,Xen callback,Xen gpos)342 static Xen gl_add_to_menu(Xen menu, Xen label, Xen callback, Xen gpos)
343 {
344   #define H_add_to_menu "(" S_add_to_menu " menu label func :optional position): adds label to menu (a main menu index), invokes \
345 func (a function of no args) when the new menu is activated. Returns the new menu label widget."
346 
347 #if (!USE_NO_GUI)
348   widget_t result;
349   char *errmsg = NULL;
350 
351   Xen_check_type(Xen_is_integer(menu), menu, 1, S_add_to_menu, "an integer");
352   Xen_check_type(Xen_is_string(label) || Xen_is_false(label), label, 2, S_add_to_menu, "a string");
353   Xen_check_type(Xen_is_procedure(callback) || Xen_is_false(callback), callback, 3, S_add_to_menu, "a procedure");
354   Xen_check_type(Xen_is_integer_or_unbound(gpos), gpos, 4, S_add_to_menu, "an integer");
355 
356   /* fprintf(stderr, "add-to-menu %s\n", Xen_object_to_C_string(Xen_car(callback))); */
357 
358   if (Xen_is_procedure(callback))
359     errmsg = procedure_ok(callback, 0, S_add_to_menu, "menu callback", 3);
360   if (!errmsg)
361     {
362       int slot = -1, m, position = -1;
363 
364       m = Xen_integer_to_C_int(menu);
365       if (m < 0)
366 	return(snd_no_such_menu_error(S_add_to_menu, menu));
367 
368       if (Xen_is_procedure(callback)) slot = make_callback_slot();
369       if (Xen_is_integer(gpos)) position = Xen_integer_to_C_int(gpos);
370 
371       result = g_add_to_menu(m,
372 			     (Xen_is_false(label)) ? NULL : Xen_string_to_C_string(label),
373 			     slot,
374 			     position);
375       if (!result)
376 	return(snd_no_such_menu_error(S_add_to_menu, menu));
377       if (Xen_is_procedure(callback)) add_callback(slot, callback);
378     }
379   else
380     {
381       Xen errm;
382       errm = C_string_to_Xen_string(errmsg);
383       free(errmsg);
384       return(snd_bad_arity_error(S_add_to_menu, errm, callback));
385     }
386   return(Xen_wrap_widget(result));
387 #else
388   return(Xen_false);
389 #endif
390 }
391 
392 
g_menu_callback(int callb)393 void g_menu_callback(int callb)
394 {
395   if ((callb >= 0) && (Xen_is_bound(menu_functions[callb])))
396     Xen_call_with_no_args(menu_functions[callb], "menu callback func");
397 }
398 
399 
gl_remove_from_menu(Xen menu,Xen label)400 static Xen gl_remove_from_menu(Xen menu, Xen label)
401 {
402   #define H_remove_from_menu "(" S_remove_from_menu " menu label): removes menu item label from menu"
403   int m;
404 
405   Xen_check_type(Xen_is_integer(menu), menu, 1, S_remove_from_menu, "an integer");
406   Xen_check_type(Xen_is_string(label), label, 2, S_remove_from_menu, "a string");
407 
408   m = Xen_integer_to_C_int(menu);
409   if (m < 0)
410     return(snd_no_such_menu_error(S_remove_from_menu, menu));
411   return(C_int_to_Xen_integer(g_remove_from_menu(m, Xen_string_to_C_string(label))));
412 }
413 
414 
g_main_menu(Xen which)415 static Xen g_main_menu(Xen which)
416 {
417   #define H_main_menu "(" S_main_menu " menu): the top-level menu widget referred to by menu"
418   int which_menu;
419 
420   Xen_check_type(Xen_is_integer(which), which, 1, S_main_menu, "an integer");
421   which_menu = Xen_integer_to_C_int(which);
422   if ((which_menu < 0) || (which_menu >= MAX_MAIN_MENUS))
423     Xen_error(Xen_make_error_type("no-such-menu"),
424 	      Xen_list_2(C_string_to_Xen_string(S_main_menu ": no such menu, ~A"),
425 			 which));
426   return(Xen_wrap_widget(menu_widget(which_menu)));
427 }
428 
429 
Xen_wrap_2_optional_args(gl_add_to_main_menu_w,gl_add_to_main_menu)430 Xen_wrap_2_optional_args(gl_add_to_main_menu_w, gl_add_to_main_menu)
431 Xen_wrap_4_optional_args(gl_add_to_menu_w, gl_add_to_menu)
432 Xen_wrap_2_args(gl_remove_from_menu_w, gl_remove_from_menu)
433 Xen_wrap_1_arg(g_main_menu_w, g_main_menu)
434 
435 void g_init_menu(void)
436 {
437 #if HAVE_SCHEME
438   s7_pointer i, p, b, fnc, s;
439   i = s7_make_symbol(s7, "integer?");
440   p = s7_make_symbol(s7, "pair?");
441   b = s7_make_symbol(s7, "boolean?");
442   fnc = s7_make_symbol(s7, "procedure?");
443   s = s7_make_symbol(s7, "string?");
444 #endif
445 
446   Xen_define_typed_procedure(S_add_to_main_menu,  gl_add_to_main_menu_w,  1, 1, 0, H_add_to_main_menu,  s7_make_signature(s7, 3, i, s, fnc));
447   Xen_define_typed_procedure(S_add_to_menu,       gl_add_to_menu_w,       3, 1, 0, H_add_to_menu,
448 			     s7_make_signature(s7, 5, p, i, s7_make_signature(s7, 2, s, b), s7_make_signature(s7, 2, fnc, b), i));
449   Xen_define_typed_procedure(S_remove_from_menu,  gl_remove_from_menu_w,  2, 0, 0, H_remove_from_menu,  s7_make_signature(s7, 3, i, i, s));
450   Xen_define_typed_procedure(S_main_menu,         g_main_menu_w,          1, 0, 0, H_main_menu,         s7_make_signature(s7, 2, p, i));
451 }
452