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