1 
2 // callbacks.c
3 // LiVES
4 // (c) G. Finch 2003 - 2020 <salsaman+lives@gmail.com>
5 // released under the GNU GPL 3 or later
6 // see file ../COPYING for licensing details
7 
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <sys/stat.h>
14 #include <dlfcn.h>
15 
16 #include "main.h"
17 #include "callbacks.h"
18 #include "interface.h"
19 #include "effects.h"
20 #include "resample.h"
21 #include "rte_window.h"
22 #include "events.h"
23 #include "audio.h"
24 #include "cvirtual.h"
25 #include "paramwindow.h"
26 #include "ce_thumbs.h"
27 #include "startup.h"
28 #include "diagnostics.h"
29 
30 #ifdef LIBAV_TRANSCODE
31 #include "transcode.h"
32 #endif
33 
34 #ifdef HAVE_YUV4MPEG
35 #include "lives-yuv4mpeg.h"
36 #endif
37 
38 #ifdef HAVE_UNICAP
39 #include "videodev.h"
40 #endif
41 
42 #ifdef ENABLE_OSC
43 #include "osc.h"
44 #endif
45 
46 static char file_name[PATH_MAX];
47 
48 
lives_notify(int msgnumber,const char * msgstring)49 void lives_notify(int msgnumber, const char *msgstring) {
50 #ifdef IS_LIBLIVES
51   binding_cb(msgnumber, msgstring, mainw->id);
52 #endif
53 #ifdef ENABLE_OSC
54   lives_osc_notify(msgnumber, msgstring);
55 #endif
56 
57 #ifdef TEST_NOTIFY
58   if (msgnumber == LIVES_OSC_NOTIFY_CLIPSET_OPENED) {
59     char *details = lives_strdup_printf(_("'LiVES opened the clip set' '%s'"), msgstring);
60     char *tmp = lives_strdup_printf("notify-send %s", details);
61     lives_system(tmp, TRUE);
62     lives_free(tmp);
63     lives_free(details);
64   }
65 
66   if (msgnumber == LIVES_OSC_NOTIFY_CLIPSET_SAVED) {
67     char *details = lives_strdup_printf(_("'LiVES saved the clip set' '%s'"), msgstring);
68     char *tmp = lives_strdup_printf("notify-send %s", details);
69     lives_system(tmp, TRUE);
70     lives_free(tmp);
71     lives_free(details);
72   }
73 #endif
74 }
75 
76 
lives_notify_int(int msgnumber,int msgint)77 LIVES_GLOBAL_INLINE void lives_notify_int(int msgnumber, int msgint) {
78   char *tmp = lives_strdup_printf("%d", msgint);
79   lives_notify(msgnumber, tmp);
80   lives_free(tmp);
81 }
82 
83 
on_LiVES_delete_event(LiVESWidget * widget,LiVESXEventDelete * event,livespointer user_data)84 boolean on_LiVES_delete_event(LiVESWidget *widget, LiVESXEventDelete *event, livespointer user_data) {
85   if (!LIVES_IS_INTERACTIVE) return TRUE;
86   on_quit_activate(NULL, NULL);
87   return TRUE;
88 }
89 
90 
cleanup_set_dir(const char * set_name)91 static void cleanup_set_dir(const char *set_name) {
92   // this function is called:
93   // - when a set is saved and merged with an existing one
94   // - when a set is deleted
95   // - when the last clip in a set is closed
96 
97   char *lfiles, *ofile, *sdir;
98 
99   sdir = lives_build_path(prefs->workdir, set_name, LAYOUTS_DIRNAME, NULL);
100   if (lives_file_test(sdir, LIVES_FILE_TEST_IS_DIR))
101     lives_rmdir(sdir, FALSE);
102   lives_free(sdir);
103 
104   sdir = lives_build_filename(prefs->workdir, set_name, CLIPS_DIRNAME, NULL);
105   if (lives_file_test(sdir, LIVES_FILE_TEST_IS_DIR))
106     lives_rmdir(sdir, FALSE);
107   lives_free(sdir);
108 
109   // remove any stale lockfiles
110   lfiles = SET_LOCK_FILES(set_name);
111   lives_rmglob(lfiles);
112   lives_free(lfiles);
113 
114   ofile = lives_build_filename(prefs->workdir, set_name, CLIP_ORDER_FILENAME, NULL);
115   lives_rm(ofile);
116   lives_free(ofile);
117 
118   ofile = lives_build_filename(prefs->workdir, set_name,
119                                CLIP_ORDER_FILENAME "." LIVES_FILE_EXT_NEW, NULL);
120   lives_rm(ofile);
121   lives_free(ofile);
122 
123   lives_sync(1);
124 
125   sdir = lives_build_path(prefs->workdir, set_name, NULL);
126   lives_rmdir(sdir, FALSE); // set to FALSE in case the user placed extra files there
127   lives_free(sdir);
128 
129   if (prefs->ar_clipset && !strcmp(prefs->ar_clipset_name, set_name)) {
130     prefs->ar_clipset = FALSE;
131     lives_memset(prefs->ar_clipset_name, 0, 1);
132     set_string_pref(PREF_AR_CLIPSET, "");
133   }
134   mainw->set_list = lives_list_delete_string(mainw->set_list, set_name);
135 }
136 
137 
138 #ifndef VALGRIND_ON
139 #ifdef _lives_free
140 #undef  lives_free
141 #define lives_free(a) (mainw->is_exiting ? a : _lives_free(a))
142 #endif
143 #endif
144 
lives_exit(int signum)145 void lives_exit(int signum) {
146   char *tmp, *com;
147   int i;
148 
149   if (!mainw) _exit(0);
150 
151   if (!mainw->only_close) {
152     mainw->is_exiting = TRUE;
153 
154     // unlock all mutexes to prevent deadlocks
155 #ifdef HAVE_PULSE_AUDIO
156     /* if (mainw->pulsed || mainw->pulsed_read) */
157     /*   pa_mloop_unlock(); */
158 #endif
159 
160     // recursive
161     while (!pthread_mutex_unlock(&mainw->instance_ref_mutex));
162     while (!pthread_mutex_unlock(&mainw->abuf_mutex));
163 
164     // non-recursive
165     pthread_mutex_trylock(&mainw->abuf_frame_mutex);
166     pthread_mutex_unlock(&mainw->abuf_frame_mutex);
167     pthread_mutex_trylock(&mainw->fxd_active_mutex);
168     pthread_mutex_unlock(&mainw->fxd_active_mutex);
169     pthread_mutex_trylock(&mainw->event_list_mutex);
170     pthread_mutex_unlock(&mainw->event_list_mutex);
171     pthread_mutex_trylock(&mainw->clip_list_mutex);
172     pthread_mutex_unlock(&mainw->clip_list_mutex);
173     pthread_mutex_trylock(&mainw->vpp_stream_mutex);
174     pthread_mutex_unlock(&mainw->vpp_stream_mutex);
175     pthread_mutex_trylock(&mainw->cache_buffer_mutex);
176     pthread_mutex_unlock(&mainw->cache_buffer_mutex);
177     pthread_mutex_trylock(&mainw->audio_filewriteend_mutex);
178     pthread_mutex_unlock(&mainw->audio_filewriteend_mutex);
179     pthread_mutex_trylock(&mainw->fbuffer_mutex);
180     pthread_mutex_unlock(&mainw->fbuffer_mutex);
181     pthread_mutex_trylock(&mainw->alarmlist_mutex);
182     pthread_mutex_unlock(&mainw->alarmlist_mutex);
183     // filter mutexes are unlocked in weed_unload_all
184 
185     if (pthread_mutex_trylock(&mainw->exit_mutex)) pthread_exit(NULL);
186 
187     if (mainw->memok && prefs->crash_recovery && mainw->record) {
188       backup_recording(NULL, NULL);
189     }
190 
191     //lives_threadpool_finish();
192     //show_weed_stats();
193   }
194 
195   if (mainw->is_ready) {
196     if (mainw->multitrack && mainw->multitrack->idlefunc > 0) {
197       //lives_source_remove(mainw->multitrack->idlefunc);
198       mainw->multitrack->idlefunc = 0;
199     }
200 
201     threaded_dialog_spin(0.);
202 
203     if (mainw->toy_type != LIVES_TOY_NONE) {
204       on_toy_activate(NULL, LIVES_INT_TO_POINTER(LIVES_TOY_NONE));
205     }
206 
207     if (mainw->alives_pgid > 0) {
208       autolives_toggle(NULL, NULL);
209     }
210 
211 #ifdef VALGRIND_ON
212     if (mainw->stored_event_list || mainw->sl_undo_mem) {
213       stored_event_list_free_all(FALSE);
214     }
215 
216     if (mainw->multitrack && !mainw->only_close) {
217       lives_freep((void **)&mainw->multitrack->undo_mem);
218     }
219 
220     if (mainw->multi_opts.set && !mainw->only_close && mainw->multi_opts.aparam_view_list) {
221       lives_list_free(mainw->multi_opts.aparam_view_list);
222     }
223 #endif
224 
225 #ifdef VALGRIND_ON
226     if (LIVES_IS_PLAYING) {
227       lives_grab_remove(LIVES_MAIN_WINDOW_WIDGET);
228       if (mainw->ext_playback) {
229         pthread_mutex_lock(&mainw->vpp_stream_mutex);
230         mainw->ext_audio = FALSE;
231         pthread_mutex_unlock(&mainw->vpp_stream_mutex);
232         if (mainw->vpp->exit_screen)(*mainw->vpp->exit_screen)(mainw->ptr_x, mainw->ptr_y);
233         stop_audio_stream();
234         mainw->stream_ticks = -1;
235       }
236 
237       // tell non-realtime audio players (sox or mplayer) to stop
238       if (!is_realtime_aplayer(prefs->audio_player) && mainw->aud_file_to_kill > -1 &&
239           mainw->files[mainw->aud_file_to_kill]) {
240         char *lsname = lives_build_filename(prefs->workdir, mainw->files[mainw->aud_file_to_kill]->handle, NULL);
241         lives_touch(lsname);
242         lives_free(lsname);
243         com = lives_strdup_printf("%s stop_audio \"%s\"", prefs->backend, mainw->files[mainw->aud_file_to_kill]->handle);
244         lives_system(com, TRUE);
245         lives_free(com);
246       }
247     }
248 #endif
249     // stop any background processing for the current clip
250     if (CURRENT_CLIP_IS_VALID) {
251       if (cfile->handle && CURRENT_CLIP_IS_NORMAL) {
252         lives_kill_subprocesses(cfile->handle, TRUE);
253       }
254     }
255 
256     // prevent crash in "threaded" dialog
257     mainw->current_file = -1;
258 
259     if (!mainw->only_close) {
260       // shut down audio players
261 #ifdef HAVE_PULSE_AUDIO
262       if (mainw->pulsed) pulse_close_client(mainw->pulsed);
263       if (mainw->pulsed_read) pulse_close_client(mainw->pulsed_read);
264       pulse_shutdown();
265 #endif
266 #ifdef ENABLE_JACK
267       lives_jack_end();
268       if (mainw->jackd) {
269         jack_close_device(mainw->jackd);
270       }
271       if (mainw->jackd_read) {
272         jack_close_device(mainw->jackd_read);
273       }
274 #endif
275     }
276 
277     if (mainw->vpp && !mainw->only_close) {
278       if (mainw->memok) {
279         if (mainw->write_vpp_file) {
280           // save video playback plugin parameters
281           char *vpp_file = lives_build_filename(prefs->config_datadir, VPP_DEFS_FILE, NULL);
282           save_vpp_defaults(mainw->vpp, vpp_file);
283         }
284       }
285       close_vid_playback_plugin(mainw->vpp);
286     }
287 
288     if (mainw->memok) {
289       if (!mainw->leave_recovery) {
290         lives_rm(mainw->recovery_file);
291         // hide the main window
292         threaded_dialog_spin(0.);
293         lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
294         threaded_dialog_spin(0.);
295       }
296 
297       if (*(future_prefs->workdir) && lives_strcmp(future_prefs->workdir, prefs->workdir)) {
298         // if we changed the workdir, remove everything but sets from the old dir
299         // create the new directory, and then move any sets over
300         end_threaded_dialog();
301         if (do_move_workdir_dialog()) {
302           do_do_not_close_d();
303           lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
304 
305           // TODO *** - check for namespace collisions between sets in old dir and sets in new dir
306 
307           // use backend to move the sets
308           com = lives_strdup_printf("%s move_workdir \"%s\"", prefs->backend_sync,
309                                     future_prefs->workdir);
310           lives_system(com, FALSE);
311           lives_free(com);
312         }
313         lives_snprintf(prefs->workdir, PATH_MAX, "%s", future_prefs->workdir);
314       }
315 
316       if (mainw->leave_files && !mainw->fatal) {
317         d_print(_("Saving as set %s..."), mainw->set_name);
318         mainw->suppress_dprint = TRUE;
319       }
320 
321       for (i = 1; i <= MAX_FILES; i++) {
322         if (mainw->files[i]) {
323           mainw->current_file = i;
324           threaded_dialog_spin(0.);
325           if (cfile->event_list_back) event_list_free(cfile->event_list_back);
326           if (cfile->event_list) event_list_free(cfile->event_list);
327 
328           cfile->event_list = cfile->event_list_back = NULL;
329 
330           lives_list_free_all(&cfile->layout_map);
331 
332           if (cfile->laudio_drawable) {
333             if (mainw->laudio_drawable == cfile->laudio_drawable) mainw->laudio_drawable = NULL;
334             lives_painter_surface_destroy(cfile->laudio_drawable);
335             cfile->laudio_drawable = NULL;
336           }
337 
338           if (cfile->raudio_drawable) {
339             if (mainw->raudio_drawable == cfile->raudio_drawable) mainw->raudio_drawable = NULL;
340             lives_painter_surface_destroy(cfile->raudio_drawable);
341             cfile->raudio_drawable = NULL;
342           }
343           if (mainw->drawsrc == mainw->current_file) mainw->drawsrc = -1;
344 
345           if (IS_NORMAL_CLIP(i) && mainw->files[i]->ext_src && mainw->files[i]->ext_src_type == LIVES_EXT_SRC_DECODER) {
346             // must do this before we move it
347             close_clip_decoder(i);
348             threaded_dialog_spin(0.);
349           }
350           lives_freep((void **)&mainw->files[i]->frame_index);
351           cfile->layout_map = NULL;
352         }
353 
354         if (mainw->files[i]) {
355           /// physically remove:
356           /// - "device" clips, generators, etc.
357           /// - scrap files, if we are not closing or crash recovery is disabled
358           /// - "normal" clips, unless crash recovery is in effect, or
359           /// - we are no exiting and it's the clipboard, or the mt render file
360           if (!IS_NORMAL_CLIP(i) || (i == 0 && !mainw->only_close)
361               || (!mainw->leave_files && !mainw->leave_recovery)
362               || (i == mainw->scrap_file && (mainw->only_close ||
363                                              !mainw->leave_recovery || !prefs->rr_crash))
364               || (i == mainw->ascrap_file && (mainw->only_close ||
365                                               !mainw->leave_recovery || !prefs->rr_crash))
366               || (i > 0 && !mainw->only_close && mainw->multitrack
367                   && i == mainw->multitrack->render_file && !CLIP_HAS_VIDEO(i)
368                   && !CLIP_HAS_AUDIO(i))) {
369             if (mainw->only_close || !IS_NORMAL_CLIP(i)) {
370               int current_file = mainw->current_file;
371               mainw->current_file = i;
372               close_current_file(current_file);
373             } else {
374               char *permitname;
375               threaded_dialog_spin(0.);
376               lives_kill_subprocesses(mainw->files[i]->handle, TRUE);
377               permitname = lives_build_filename(prefs->workdir, mainw->files[i]->handle,
378                                                 TEMPFILE_MARKER "." LIVES_FILE_EXT_TMP, NULL);
379               lives_touch(permitname);
380               lives_free(permitname);
381               com = lives_strdup_printf("%s close \"%s\"", prefs->backend, mainw->files[i]->handle);
382               lives_system(com, FALSE);
383               lives_free(com);
384               threaded_dialog_spin(0.);
385             }
386           } else {
387             threaded_dialog_spin(0.);
388             // or just clean them up -
389             // remove the following: "*.mgk *.bak *.pre *.tmp pause audio.* audiodump* audioclip";
390             if (!prefs->vj_mode) {
391               if (prefs->autoclean) {
392                 com = lives_strdup_printf("%s clear_tmp_files \"%s\"",
393                                           prefs->backend_sync, mainw->files[i]->handle);
394                 lives_system(com, FALSE);
395                 threaded_dialog_spin(0.);
396                 lives_free(com);
397               }
398               if (IS_NORMAL_CLIP(i)) {
399                 char *fname = lives_build_filename(prefs->workdir, mainw->files[i]->handle,
400                                                    TOTALSAVE_NAME, NULL);
401                 int fd = lives_create_buffered(fname, DEF_FILE_PERMS);
402                 lives_write_buffered(fd, (const char *)mainw->files[i], sizeof(lives_clip_t), TRUE);
403                 lives_close_buffered(fd);
404               }
405             }
406             if (mainw->files[i]->frameno != mainw->files[i]->saved_frameno) {
407               save_clip_value(i, CLIP_DETAILS_PB_FRAMENO, &mainw->files[i]->frameno);
408 	      // *INDENT-OFF*
409 	    }}}}
410       // *INDENT-ON*
411       if (prefs->autoclean) {
412         com = lives_strdup_printf("%s empty_trash . general", prefs->backend);
413         lives_system(com, FALSE);
414         lives_free(com);
415       }
416 
417       if (*mainw->set_name) {
418         /// if a set was loaded:
419         /// - if we are only closing them in the app, leave the frames on disk
420         if (mainw->only_close) {
421           mainw->suppress_dprint = TRUE;
422           mainw->close_keep_frames = TRUE;
423           mainw->is_processing = TRUE; ///< stop multitrack from sensitizing too soon
424           for (i = 1; i <= MAX_FILES; i++) {
425             if (IS_NORMAL_CLIP(i) && (!mainw->multitrack || i != mainw->multitrack->render_file)) {
426               mainw->current_file = i;
427               close_current_file(0);
428               threaded_dialog_spin(0.);
429             }
430           }
431           mainw->close_keep_frames = FALSE;
432         }
433 
434         if (*mainw->set_name) {
435           if (!mainw->leave_files && !mainw->leave_recovery) {
436             // delete the current set (this is for DELETE_SET)
437             cleanup_set_dir(mainw->set_name);
438             lives_memset(mainw->set_name, 0, 1);
439             mainw->was_set = FALSE;
440             lives_widget_set_sensitive(mainw->vj_load_set, TRUE);
441           } else {
442             unlock_set_file(mainw->set_name);
443           }
444         }
445       }
446 
447       if (mainw->only_close) {
448         mainw->suppress_dprint = FALSE;
449         if (!mainw->multitrack) resize(1);
450         mainw->was_set = FALSE;
451         lives_memset(mainw->set_name, 0, 1);
452         mainw->only_close = FALSE;
453         prefs->crash_recovery = TRUE;
454 
455         threaded_dialog_spin(0.);
456         if (mainw->current_file > -1) sensitize();
457         lives_widget_queue_draw(LIVES_MAIN_WINDOW_WIDGET);
458         d_print_done();
459         end_threaded_dialog();
460 
461         if (mainw->multitrack) {
462           mainw->current_file = mainw->multitrack->render_file;
463           mainw->multitrack->file_selected = -1;
464           polymorph(mainw->multitrack, POLY_NONE);
465           polymorph(mainw->multitrack, POLY_CLIPS);
466           mt_sensitise(mainw->multitrack);
467         } else {
468           if (prefs->show_msg_area) {
469             reset_message_area();
470             lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
471             if (mainw->idlemax == 0) {
472               lives_idle_add_simple(resize_message_area, NULL);
473             }
474             mainw->idlemax = DEF_IDLE_MAX;
475           }
476         }
477         mainw->is_processing = FALSE; ///< mt may now sensitize...
478         return;
479       }
480       save_future_prefs();
481     }
482 
483     // stop valgrind from complaining
484 #ifdef VALGRIND_ON
485     if (mainw->frame_layer) {
486       check_layer_ready(mainw->frame_layer);
487       weed_layer_free(mainw->frame_layer);
488       mainw->frame_layer = NULL;
489     }
490 #endif
491     if (mainw->sep_win && (LIVES_IS_PLAYING || prefs->sepwin_type == SEPWIN_TYPE_STICKY)) {
492       threaded_dialog_spin(0.);
493       kill_play_window();
494       threaded_dialog_spin(0.);
495     }
496 
497     weed_unload_all();
498 
499 #ifdef VALGRIND_ON
500     lives_list_free_all(&mainw->current_layouts_map);
501 
502     if (capable->has_encoder_plugins) {
503       LiVESList *dummy_list = plugin_request("encoders", prefs->encoder.name, "finalise");
504       lives_list_free_all(&dummy_list);
505     }
506     threaded_dialog_spin(0.);
507     rfx_free_all();
508     threaded_dialog_spin(0.);
509 #endif
510 
511 #ifdef ENABLE_OSC
512     if (prefs->osc_udp_started) lives_osc_end();
513 #endif
514     mainw->is_ready = FALSE;
515 
516 #ifdef VALGRIND_ON
517     pconx_delete_all();
518     cconx_delete_all();
519 
520     mainw->msg_adj = NULL;
521     free_n_msgs(mainw->n_messages);
522 
523     if (mainw->multitrack) {
524       event_list_free_undos(mainw->multitrack);
525 
526       if (mainw->multitrack->event_list) {
527         event_list_free(mainw->multitrack->event_list);
528         mainw->multitrack->event_list = NULL;
529       }
530     }
531 
532     lives_freep((void **)&prefs->fxdefsfile);
533     lives_freep((void **)&prefs->fxsizesfile);
534     lives_freep((void **)&capable->wm_name);
535     lives_freep((void **)&mainw->recovery_file);
536 
537     for (i = 0; i < NUM_LIVES_STRING_CONSTANTS; i++) lives_freep((void **)&mainw->string_constants[i]);
538     for (i = 0; i < mainw->n_screen_areas; i++) lives_freep((void **)&mainw->screen_areas[i].name);
539 
540     lives_freep((void **)&mainw->foreign_visual);
541     lives_freep((void **)&THREADVAR(read_failed_file));
542     lives_freep((void **)&THREADVAR(write_failed_file));
543     lives_freep((void **)&THREADVAR(bad_aud_file));
544 
545     lives_freep((void **)&mainw->old_vhash);
546     lives_freep((void **)&mainw->version_hash);
547     lives_freep((void **)&mainw->multitrack);
548     lives_freep((void **)&mainw->mgeom);
549     lives_list_free_all(&prefs->disabled_decoders);
550     if (mainw->fonts_array) lives_strfreev(mainw->fonts_array);
551 #ifdef ENABLE_NLS
552     //lives_freep((void **)&trString);
553 #endif
554 #endif
555     unload_decoder_plugins();
556   }
557   tmp = lives_strdup_printf("signal: %d", signum);
558   lives_notify(LIVES_OSC_NOTIFY_QUIT, tmp);
559   lives_free(tmp);
560 
561   exit(0);
562 }
563 
564 
565 #ifndef VALGRIND_ON
566 #ifdef _lives_free
567 #undef  lives_free
568 #define lives_free _lives_free
569 #endif
570 #endif
571 
572 
open_sel_range_activate(int frames,double fps)573 static void open_sel_range_activate(int frames, double fps) {
574   // open selection range dialog
575   LiVESWidget *opensel_dialog;
576   mainw->fc_buttonresponse = LIVES_RESPONSE_NONE; // reset button state
577   mainw->fx1_val = 0.;
578   mainw->fx2_val = frames > 1000. ? 1000. : (double)frames;
579   opensel_dialog = create_opensel_dialog(frames, fps);
580   lives_widget_show_all(opensel_dialog);
581 }
582 
583 
read_file_details_generic(const char * fname)584 static boolean read_file_details_generic(const char *fname) {
585   /// make a tmpdir in case we need to open images for example
586   char *tmpdir, *dirname, *com;
587   const char *prefix = "_fsp";
588   dirname = get_worktmp(prefix);
589   if (dirname) tmpdir = lives_build_path(prefs->workdir, dirname, NULL);
590   else {
591     dirname = lives_strdup_printf("%s%lu", prefix, gen_unique_id());
592     tmpdir = lives_build_path(prefs->workdir, dirname, NULL);
593     if (!lives_make_writeable_dir(tmpdir)) {
594       workdir_warning();
595       lives_free(tmpdir); lives_free(dirname);
596       end_fs_preview();
597       return FALSE;
598     }
599   }
600 
601   // check details
602   com = lives_strdup_printf("%s get_details %s \"%s\" \"%s\" %d", prefs->backend_sync,
603                             dirname, fname, prefs->image_ext, 0);
604   lives_popen(com, FALSE, mainw->msg, MAINW_MSG_SIZE);
605   lives_free(com); lives_free(dirname);
606 
607   lives_rmdir(tmpdir, TRUE);
608   lives_free(tmpdir);
609 
610   if (THREADVAR(com_failed)) {
611     THREADVAR(com_failed) = FALSE;
612     end_fs_preview();
613     return FALSE;
614   }
615   return TRUE;
616 }
617 
618 
on_open_sel_activate(LiVESMenuItem * menuitem,livespointer user_data)619 void on_open_sel_activate(LiVESMenuItem * menuitem, livespointer user_data) {
620   // OPEN A FILE
621   LiVESWidget *chooser;
622   char **array;
623   char *fname, *tmp;
624   double fps;
625   int resp, npieces, frames;
626   mainw->mt_needs_idlefunc = FALSE;
627 
628   if (mainw->multitrack) {
629     if (mainw->multitrack->idlefunc > 0) {
630       lives_source_remove(mainw->multitrack->idlefunc);
631       mainw->multitrack->idlefunc = 0;
632       mainw->mt_needs_idlefunc = TRUE;
633     }
634     mt_desensitise(mainw->multitrack);
635     lives_widget_set_sensitive(mainw->multitrack->playall, TRUE);
636     lives_widget_set_sensitive(mainw->m_playbutton, TRUE);
637   }
638 
639   while (1) {
640     chooser = choose_file_with_preview((*mainw->vid_load_dir) ? mainw->vid_load_dir : NULL, NULL, NULL,
641                                        LIVES_FILE_SELECTION_VIDEO_AUDIO);
642     resp = lives_dialog_run(LIVES_DIALOG(chooser));
643 
644     end_fs_preview();
645 
646     if (resp != LIVES_RESPONSE_ACCEPT) {
647       on_filechooser_cancel_clicked(chooser);
648       if (mainw->multitrack) {
649         mt_sensitise(mainw->multitrack);
650         maybe_add_mt_idlefunc();
651       }
652       return;
653     }
654 
655     fname = lives_file_chooser_get_filename(LIVES_FILE_CHOOSER(chooser));
656 
657     if (!fname) {
658       if (mainw->multitrack) {
659         mt_sensitise(mainw->multitrack);
660         maybe_add_mt_idlefunc();
661       }
662       return;
663     }
664 
665     lives_snprintf(file_name, PATH_MAX, "%s", (tmp = lives_filename_to_utf8(fname, -1, NULL, NULL, NULL)));
666     lives_free(tmp);
667 
668     lives_widget_destroy(LIVES_WIDGET(chooser));
669 
670     lives_snprintf(mainw->vid_load_dir, PATH_MAX, "%s", file_name);
671     get_dirname(mainw->vid_load_dir);
672 
673     lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
674 
675     if (prefs->save_directories) {
676       set_utf8_pref(PREF_VID_LOAD_DIR, mainw->vid_load_dir);
677     }
678 
679     if (!read_file_details_generic(fname)) {
680       lives_free(fname);
681       if (mainw->multitrack) {
682         mt_sensitise(mainw->multitrack);
683         maybe_add_mt_idlefunc();
684       }
685       return;
686     }
687     lives_free(fname);
688 
689     npieces = get_token_count(mainw->msg, '|');
690     if (npieces < 8) {
691       end_fs_preview();
692       if (mainw->multitrack) {
693         mt_sensitise(mainw->multitrack);
694         maybe_add_mt_idlefunc();
695       }
696       return;
697     }
698 
699     array = lives_strsplit(mainw->msg, "|", npieces);
700     frames = atoi(array[2]);
701     fps = lives_strtod(array[7], NULL);
702     lives_strfreev(array);
703 
704     if (frames == 0) {
705       do_error_dialog("LiVES could not extract any video frames from this file.\nSorry.\n");
706       end_fs_preview();
707       if (mainw->multitrack) {
708         mt_sensitise(mainw->multitrack);
709         maybe_add_mt_idlefunc();
710       }
711       continue;
712     }
713     break;
714   }
715 
716   open_sel_range_activate(frames, fps);
717 }
718 
719 
on_open_vcd_activate(LiVESMenuItem * menuitem,livespointer device_type)720 void on_open_vcd_activate(LiVESMenuItem * menuitem, livespointer device_type) {
721   LiVESWidget *vcdtrack_dialog;
722   int type = LIVES_POINTER_TO_INT(device_type);
723   mainw->mt_needs_idlefunc = FALSE;
724 
725   if (mainw->multitrack) {
726     if (mainw->multitrack->idlefunc > 0) {
727       lives_source_remove(mainw->multitrack->idlefunc);
728       mainw->multitrack->idlefunc = 0;
729       mainw->mt_needs_idlefunc = TRUE;
730     }
731     mt_desensitise(mainw->multitrack);
732     lives_widget_set_sensitive(mainw->multitrack->playall, TRUE);
733     lives_widget_set_sensitive(mainw->m_playbutton, TRUE);
734   }
735 
736   mainw->fx1_val = 1;
737   mainw->fx2_val = 1;
738   mainw->fx3_val = DVD_AUDIO_CHAN_DEFAULT;
739 
740   vcdtrack_dialog = create_cdtrack_dialog(type, NULL);
741   lives_widget_show_all(vcdtrack_dialog);
742 }
743 
744 
on_open_loc_activate(LiVESMenuItem * menuitem,livespointer user_data)745 void on_open_loc_activate(LiVESMenuItem * menuitem, livespointer user_data) {
746   // need non-instant opening (for now)
747   mainw->mt_needs_idlefunc = FALSE;
748 
749   if (!HAS_EXTERNAL_PLAYER) {
750     do_need_mplayer_mpv_dialog();
751     return;
752   }
753 
754   if (mainw->multitrack) {
755     if (mainw->multitrack->idlefunc > 0) {
756       lives_source_remove(mainw->multitrack->idlefunc);
757       mainw->multitrack->idlefunc = 0;
758       mainw->mt_needs_idlefunc = TRUE;
759     }
760     mt_desensitise(mainw->multitrack);
761     lives_widget_set_sensitive(mainw->multitrack->playall, TRUE);
762     lives_widget_set_sensitive(mainw->m_playbutton, TRUE);
763   }
764 
765   locw = create_location_dialog();
766   lives_widget_show_all(locw->dialog);
767 }
768 
769 
on_open_utube_activate(LiVESMenuItem * menuitem,livespointer user_data)770 void on_open_utube_activate(LiVESMenuItem * menuitem, livespointer user_data) {
771   /// get a system tmpdir
772 
773   lives_remote_clip_request_t *req = NULL, *req2;
774   char *tmpdir;
775   if (!check_for_executable(&capable->has_mktemp, EXEC_MKTEMP)) {
776     do_program_not_found_error(EXEC_MKTEMP);
777     return;
778   }
779   tmpdir = get_systmp("ytdl", TRUE);
780   if (!tmpdir) return;
781 
782   mainw->mt_needs_idlefunc = FALSE;
783 
784   if (mainw->multitrack) {
785     if (mainw->multitrack->idlefunc > 0) {
786       lives_source_remove(mainw->multitrack->idlefunc);
787       mainw->multitrack->idlefunc = 0;
788       mainw->mt_needs_idlefunc = TRUE;
789     }
790     mt_desensitise(mainw->multitrack);
791     lives_widget_set_sensitive(mainw->multitrack->playall, TRUE);
792     lives_widget_set_sensitive(mainw->m_playbutton, TRUE);
793   }
794 
795   do {
796     mainw->cancelled = CANCEL_NONE;
797     req = run_youtube_dialog(req);
798     if (!req) {
799       if (mainw->multitrack) {
800         mt_sensitise(mainw->multitrack);
801         maybe_add_mt_idlefunc();
802       }
803       goto final123;
804     }
805     req2 = on_utube_select(req, tmpdir);
806 #ifndef ALLOW_NONFREE_CODECS
807     req2 = NULL;
808 #endif
809     if (req2 && mainw->cancelled == CANCEL_RETRY) req = req2;
810     else {
811       lives_free(req);
812       req = NULL;
813     }
814   } while (mainw->cancelled == CANCEL_RETRY);
815 
816 final123:
817   if (mainw->permmgr) {
818     lives_freep((void **)&mainw->permmgr->key);
819     lives_free(mainw->permmgr);
820     mainw->permmgr = NULL;
821   }
822   if (tmpdir) {
823     if (lives_file_test(tmpdir, LIVES_FILE_TEST_EXISTS)) {
824       lives_rmdir(tmpdir, TRUE);
825     }
826     lives_free(tmpdir);
827   }
828 
829   if (!mainw->multitrack) sensitize();
830 }
831 
832 
on_recent_activate(LiVESMenuItem * menuitem,livespointer user_data)833 void on_recent_activate(LiVESMenuItem * menuitem, livespointer user_data) {
834   char file[PATH_MAX];
835   double start = 0.;
836   int end = 0, pno;
837   char *pref;
838   mainw->mt_needs_idlefunc = FALSE;
839 
840   pno = LIVES_POINTER_TO_INT(user_data);
841 
842   if (mainw->multitrack) {
843     if (mainw->multitrack->idlefunc > 0) {
844       lives_source_remove(mainw->multitrack->idlefunc);
845       mainw->multitrack->idlefunc = 0;
846       mainw->mt_needs_idlefunc = TRUE;
847     }
848     mt_desensitise(mainw->multitrack);
849   }
850 
851   //lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
852 
853   pref = lives_strdup_printf("%s%d", PREF_RECENT, pno);
854 
855   get_utf8_pref(pref, file, PATH_MAX);
856 
857   lives_free(pref);
858 
859   if (get_token_count(file, '\n') > 1) {
860     char **array = lives_strsplit(file, "\n", 2);
861     lives_snprintf(file, PATH_MAX, "%s", array[0]);
862     lives_freep((void **)&mainw->file_open_params);
863     mainw->file_open_params = lives_strdup(array[1]);
864     lives_strfreev(array);
865   }
866 
867   if (get_token_count(file, '|') > 2) {
868     char **array = lives_strsplit(file, "|", 3);
869     lives_snprintf(file, PATH_MAX, "%s", array[0]);
870     start = lives_strtod(array[1], NULL);
871     end = atoi(array[2]);
872     lives_strfreev(array);
873   }
874   deduce_file(file, start, end);
875 
876   if (mainw->multitrack) {
877     polymorph(mainw->multitrack, POLY_NONE);
878     polymorph(mainw->multitrack, POLY_CLIPS);
879     mt_sensitise(mainw->multitrack);
880     maybe_add_mt_idlefunc();
881   }
882 }
883 
884 
on_location_select(LiVESButton * button,livespointer user_data)885 void on_location_select(LiVESButton * button, livespointer user_data) {
886   lives_snprintf(file_name, PATH_MAX, "%s", lives_entry_get_text(LIVES_ENTRY(locw->entry)));
887   lives_widget_destroy(locw->dialog);
888   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
889   lives_free(locw);
890 
891   mainw->opening_loc = TRUE;
892   lives_freep((void **)&mainw->file_open_params);
893   if (prefs->no_bandwidth) {
894     mainw->file_open_params = lives_strdup("nobandwidth");
895   } else mainw->file_open_params = lives_strdup("sendbandwidth");
896   mainw->img_concat_clip = -1;
897   open_file(file_name);
898 
899   if (mainw->multitrack) {
900     polymorph(mainw->multitrack, POLY_NONE);
901     polymorph(mainw->multitrack, POLY_CLIPS);
902     mt_sensitise(mainw->multitrack);
903     maybe_add_mt_idlefunc();
904   }
905 }
906 
907 #define USE_YTDL
908 
909 //ret updated req if fmt sel. needs change
on_utube_select(lives_remote_clip_request_t * req,const char * tmpdir)910 lives_remote_clip_request_t *on_utube_select(lives_remote_clip_request_t *req, const char *tmpdir) {
911   char *com, *full_dfile = NULL, *tmp, *ddir, *dest = NULL;
912   char *overrdkey = NULL;
913   char *mpf = NULL, *mpt = NULL;
914   lives_remote_clip_request_t *reqout = NULL;
915   boolean hasnone = FALSE, hasalts = FALSE;
916   boolean keep_old_dir = FALSE;
917   boolean forcecheck = FALSE;
918   boolean bres;
919   boolean badfile = FALSE;
920   int keep_frags = 0;
921   int manage_ds = 0;
922   int current_file = mainw->current_file;
923   int audchoice = 0;
924 
925   mainw->no_switch_dprint = TRUE;
926 
927   // if possible, download to a temp dir first, that way we can cleanly delete leftover fragments, etc.
928   if (tmpdir) {
929     ddir = lives_build_path(tmpdir, req->fname, NULL);
930     keep_frags = 1;
931     if (lives_file_test(ddir, LIVES_FILE_TEST_IS_DIR)) {
932       if (check_dir_access(ddir, FALSE)) {
933         keep_old_dir = TRUE;
934       }
935     }
936     if (!keep_old_dir) {
937       LiVESResponseType resp;
938       char *xdir = lives_build_path(tmpdir, "*", NULL);
939       lives_rmdir(xdir, TRUE);
940       lives_free(xdir);
941       do {
942         resp = LIVES_RESPONSE_NONE;
943         if (!lives_make_writeable_dir(ddir)) {
944           resp = do_dir_perm_error(ddir, TRUE);
945           if (resp == LIVES_RESPONSE_CANCEL) goto cleanup_ut;
946         }
947       } while (resp == LIVES_RESPONSE_RETRY);
948     }
949   } else ddir = req->save_dir;
950 
951   if (capable->mountpoint && *capable->mountpoint) {
952     /// if tempdir is on same volume as workdir, or if not using tmpdir and final is on same
953     // then we monitor continuously
954     mpf = get_mountpoint_for(ddir);
955     if (mpf && *mpf && !lives_strcmp(mpf, capable->mountpoint)) {
956       manage_ds = 1;
957     }
958   }
959   if (!manage_ds && ddir != req->save_dir) {
960     mpt = get_mountpoint_for(req->save_dir);
961     if (mpf && *mpf && mpt && *mpt) {
962       if (lives_strcmp(mpf, mpt)) {
963         /// in this case tmpdir is on another volume, but final dest. is on our volume
964         /// or a different one
965         /// we will only check before moving the file
966         if (capable->mountpoint && *capable->mountpoint && !lives_strcmp(mpt, capable->mountpoint)) {
967           // final dest is ours, we will check warn, crit and overflow
968           manage_ds = 2;
969         } else {
970           // final dest is not ours, we will only check overflow
971           manage_ds = 3;
972 	  // *INDENT-OFF*
973         }}}}
974   // *INDENT-ON*
975 
976   lives_freep((void **)&mpf);
977 
978   if (prefs->ds_warn_level && !prefs->ds_crit_level) {
979     /// if the user disabled disk_quota, disk_warn, AND disk_crit, then we just let the disk fill up
980     /// the only thing we will care about is if there is insufficient space to move the file
981     if (manage_ds == 2) manage_ds = 3;
982     else manage_ds = 0;
983   }
984 
985   mainw->error = FALSE;
986 
987   // do minimal ds checking until we hit full download
988   forcecheck = FALSE;
989 
990   if (!get_temp_handle(-1)) {
991     // we failed because we ran out of file handles; this hsould almost never happen
992     d_print_failed();
993     goto cleanup_ut;
994   }
995 
996   while (1) {
997 retry:
998     lives_rm(cfile->info_file);
999     if (req->do_update) {
1000       if (!check_for_executable(&capable->has_pip, EXEC_PIP)) {
1001         /// check we can update locally
1002         do_please_install(EXEC_PIP, 0);
1003         capable->has_pip = UNCHECKED;
1004         d_print_failed();
1005         goto cleanup_ut;
1006       } else {
1007 #ifdef YTDL_URL
1008         /// if youtube-dl has a fixed download location, we can let the backend install it for us
1009         /// TODO: pass URL as argv[15]
1010         if (mainw->permmgr && mainw->permmgr->key && req->do_update) {
1011           /// force fresh install of local youtube-dl
1012           overrdkey = mainw->permmgr->key;
1013         }
1014 #else
1015         /// else copy system binary to $HOME/.local/bin
1016         if (capable->has_youtube_dl != LOCAL) {
1017           char *todir = lives_build_path(capable->home_dir, LOCAL_HOME_DIR, "bin", NULL);
1018           char *to = lives_build_filename(todir, EXEC_YOUTUBE_DL, NULL);
1019           if (!lives_file_test(to, LIVES_FILE_TEST_IS_EXECUTABLE)) {
1020             char from[PATH_MAX];
1021             if (check_for_executable(&capable->has_youtube_dlc, EXEC_YOUTUBE_DLC))
1022               get_location(EXEC_YOUTUBE_DLC, from, PATH_MAX);
1023             else
1024               get_location(EXEC_YOUTUBE_DL, from, PATH_MAX);
1025             if (lives_strlen(from) > 10) {
1026               if (check_dir_access(todir, TRUE)) {
1027                 lives_cp(from, to);
1028               }
1029             }
1030           }
1031           lives_free(todir); lives_free(to);
1032           check_for_executable(&capable->has_youtube_dl, EXEC_YOUTUBE_DL);
1033         }
1034       }
1035     }
1036 #endif
1037 
1038         // get format list
1039 
1040         // for now, we dont't pass req->desired_fps or req->audchoice
1041         // also we could send req->sub_lang...
1042 
1043         if (!mainw->save_with_sound) audchoice = -1;
1044 
1045         com = lives_strdup_printf("%s download_clip \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" %d %d %d \"%s\" %d "
1046                                   "%d %d %d %s", prefs->backend, cfile->handle, req->URI, ddir,
1047                                   req->fname, req->format, req->desired_width, req->desired_height,
1048                                   req->matchsize, req->vidchoice, audchoice, keep_frags,
1049                                   req->do_update, req->debug,
1050                                   overrdkey ? (tmp = lives_strdup_printf(" %s", overrdkey))
1051                                   : (tmp = lives_strdup("")));
1052         lives_free(tmp);
1053 
1054         if (mainw->permmgr) {
1055           lives_freep((void **)&mainw->permmgr->key);
1056           lives_free(mainw->permmgr);
1057           overrdkey = NULL;
1058           mainw->permmgr = NULL;
1059         }
1060 
1061         mainw->error = FALSE;
1062         lives_system(com, FALSE);
1063         lives_free(com);
1064 
1065         if (THREADVAR(com_failed)) {
1066           d_print_failed();
1067           mainw->error = FALSE;
1068           req->allownf = TRUE;
1069           reqout = req;
1070           reqout->do_update = TRUE;
1071           mainw->cancelled = CANCEL_RETRY;
1072           goto cleanup_ut;
1073         }
1074 
1075 #ifndef USE_YTDL
1076         break;
1077 #endif
1078 
1079         // we expect to get back a list of available formats
1080         // or the selected format
1081         if (*(req->vidchoice)) break;
1082 
1083         if (!do_auto_dialog(_("Getting format list"), 2)) {
1084           /// we can get CANCEL_ERROR: - error was detected but user gave permission to fix it
1085           /// or CANCEL_USER: - error was detected and user declined auto fix
1086           if (mainw->cancelled == CANCEL_ERROR) {
1087             mainw->cancelled = CANCEL_NONE;
1088             mainw->error = TRUE;
1089           }
1090           if (mainw->cancelled) d_print_cancelled();
1091           else if (mainw->error) {
1092             d_print_failed();
1093             if (mainw->permmgr)  {
1094               if (mainw->cancelled == CANCEL_NONE &&
1095                   mainw->permmgr->key && *mainw->permmgr->key) {
1096                 req->do_update = TRUE;
1097                 mainw->error = FALSE;
1098                 goto retry;
1099               } else {
1100                 req->do_update = FALSE;
1101                 lives_free(mainw->permmgr);
1102                 mainw->permmgr = NULL;
1103               }
1104             } else {
1105               do_error_dialogf(_("Unable to download media from the requested URL:\n%s\n\n"
1106                                  "NB: Obtaining the address by right clicking on the target itself "
1107                                  "can sometimes work better\n\n\nAlso, please note that downloading of 'Private' videos "
1108                                  "from Youtube is not possible,\nthey need to be changed to 'Unlisted' "
1109                                  "in order for the download to succeed.\n"), req->URI);
1110             }
1111             if (reqout) reqout->do_update = TRUE;
1112           }
1113           mainw->error = FALSE;
1114           mainw->cancelled = CANCEL_RETRY;
1115           reqout = req;
1116           goto cleanup_ut;
1117         }
1118 
1119         if (!(*mainw->msg)) {
1120           hasnone = TRUE;
1121         }
1122 #ifdef ALLOW_NONFREE_CODECS
1123         else if (!lives_strncmp(mainw->msg, "completed|altfmts", 17)) {
1124           hasalts = TRUE;
1125         }
1126 #endif
1127 
1128         if (hasnone || hasalts) {
1129           d_print_failed();
1130           if (hasnone) {
1131             do_error_dialog(
1132               _("\nLiVES was unable to download the clip.\nPlease check the clip URL and make sure you have \n"
1133                 "the latest youtube-dl installed.\n"));
1134           } else {
1135 #ifdef ALLOW_NONFREE_CODECS
1136             if (do_yesno_dialog(
1137                   _("\nLiVES was unable to download the clip in the desired format\nWould you like to try using an alternate "
1138                     "format selection ?"))) {
1139               mainw->error = FALSE;
1140               mainw->cancelled = CANCEL_RETRY;
1141               req->allownf = TRUE;
1142               reqout = req;
1143 #endif
1144             }
1145           }
1146           goto cleanup_ut;
1147         }
1148 
1149         if (req->matchsize == LIVES_MATCH_CHOICE && !*req->vidchoice)  {
1150           // show a list of the video formats and let the user pick one
1151           if (!youtube_select_format(req)) {
1152             goto cleanup_ut;
1153           }
1154           // we try again, this time with req->vidchoice set
1155           req->matchsize = LIVES_MATCH_SPECIFIED;
1156         } else {
1157           // returned completed|vidchoice|audchoice
1158           char **array = lives_strsplit(mainw->msg, "|", 3);
1159           lives_snprintf(req->vidchoice, 512, "%s", array[1]);
1160           lives_snprintf(req->audchoice, 512, "%s", array[2]);
1161           lives_strfreev(array);
1162         }
1163       }
1164 
1165       // backend should now be downloading the clip
1166 
1167       // do more intensive diskspace checking
1168       forcecheck = TRUE;
1169 
1170       if (manage_ds == 1) {
1171         /// type 1, we do continuous monitoring
1172         // we will only monitor for CRIT, other statuses will be checked at the end
1173         mainw->ds_mon = CHECK_CRIT;
1174       }
1175 
1176       cfile->nopreview = TRUE;
1177       cfile->no_proc_sys_errors = TRUE; ///< do not show processing error dialogs, we will show our own msg
1178 
1179       //// TODO - allow downloading in bg while user does something else
1180       /// **ADD TASK CALLBACK
1181 
1182       lives_snprintf(req->ext, 16, "%s", req->format);
1183 
1184       full_dfile = lives_strdup_printf("%s.%s", req->fname, req->ext);
1185 
1186       bres = do_progress_dialog(TRUE, TRUE, _("Downloading clip"));
1187       cfile->no_proc_sys_errors = FALSE;
1188       if (!bres || mainw->error) badfile = TRUE;
1189       else {
1190         dest = lives_build_filename(req->save_dir, full_dfile, NULL);
1191         if (ddir != req->save_dir) {
1192           lives_storage_status_t dstate;
1193           char *from = lives_build_filename(ddir, full_dfile, NULL);
1194           off_t clipsize;
1195           if (!lives_file_test(from, LIVES_FILE_TEST_EXISTS) || (clipsize = sget_file_size(from)) <= 0) {
1196             badfile = TRUE;
1197           } else {
1198             if (manage_ds == 2 || manage_ds == 3) {
1199               LiVESResponseType resp;
1200               do {
1201                 char *msg = NULL;
1202                 int64_t dsu = -1;
1203                 resp = LIVES_RESPONSE_OK;
1204                 dstate = get_storage_status(req->save_dir, mainw->next_ds_warn_level, &dsu, clipsize);
1205                 if (dstate == LIVES_STORAGE_STATUS_OVERFLOW) {
1206                   msg =
1207                     lives_strdup_printf(_("There is insufficient disk space in %s to move the downloaded clip.\n"), mpt);
1208                 } else if (manage_ds != 3) {
1209                   if (dstate == LIVES_STORAGE_STATUS_CRITICAL || dstate == LIVES_STORAGE_STATUS_WARNING) {
1210                     msg = lives_strdup_printf(_("Moving the downloaded clip to\n%s\nwill bring free disk space in %s\n"
1211                                                 "below the %s level of %s\n"), mpt);
1212                   }
1213                 }
1214 
1215                 if (msg) {
1216                   char *cs = lives_format_storage_space_string(clipsize);
1217                   char *vs = lives_format_storage_space_string(dsu);
1218                   char *xmsg = lives_strdup_printf("%s\n(Free space in volume = %s, clip size = %s)\n"
1219                                                    "You can either try deleting some files from %s and clicking on  Retry\n"
1220                                                    "or else click Cancel to cancel the download.\n", msg, vs, cs, mpt);
1221                   resp = do_retry_cancel_dialog(xmsg);
1222                   lives_free(xmsg); lives_free(msg);
1223                   lives_free(cs); lives_free(vs);
1224                 }
1225               } while (resp == LIVES_RESPONSE_RETRY);
1226               if (resp == LIVES_RESPONSE_CANCEL) {
1227                 badfile = TRUE;
1228               }
1229             }
1230           }
1231           if (!badfile) {
1232             /// move file to its final destination
1233             LiVESResponseType resp;
1234             req->do_update = FALSE;
1235             if (tmpdir) {
1236               if (lives_mv(from, dest)) {
1237                 lives_free(from);
1238                 lives_free(dest);
1239                 badfile = TRUE;
1240                 goto cleanup_ut;
1241               }
1242             }
1243             do {
1244               resp = LIVES_RESPONSE_NONE;
1245               if (!lives_file_test(dest, LIVES_FILE_TEST_EXISTS)) {
1246                 char *errtxt = lives_strdup_printf(_("Failed to move %s to %s\n"), from, dest);
1247                 resp = do_write_failed_error_s_with_retry(dest, errtxt);
1248                 lives_free(errtxt);
1249               }
1250             } while (resp == LIVES_RESPONSE_RETRY);
1251             if (resp == LIVES_RESPONSE_CANCEL) {
1252               badfile = TRUE;
1253             }
1254           }
1255           lives_free(from);
1256         }
1257       }
1258 
1259       if (badfile) {
1260         lives_kill_subprocesses(cfile->handle, TRUE);
1261         if (mainw->error) {
1262           d_print_failed();
1263           do_error_dialog(
1264             _("\nLiVES was unable to download the clip.\nPlease check the clip URL and make sure you have \n"
1265               "the latest youtube-dl installed.\n(Note: try right-clicking on the clip itself to copy its address)\n"));
1266           mainw->error = FALSE;
1267         }
1268       }
1269 
1270 cleanup_ut:
1271       close_temp_handle(current_file);
1272       lives_freep((void **)&mpt);
1273 
1274       if (manage_ds) {
1275         lives_storage_status_t dstat;
1276         boolean recheck = FALSE;
1277         boolean tempdir_removed = FALSE;
1278         int64_t dsu = -1;
1279         if (prefs->disk_quota) {
1280           size_t wdlen = lives_strlen(prefs->workdir);
1281           size_t tlen = lives_strlen(req->save_dir);
1282           if (tlen >= wdlen && lives_strncmp(req->save_dir, prefs->workdir, wdlen)) {
1283             dsu = capable->ds_used = get_dir_size(prefs->workdir);
1284           }
1285         }
1286         dstat = mainw->ds_status = get_storage_status(prefs->workdir, mainw->next_ds_warn_level, &dsu, 0);
1287         capable->ds_free = dsu;
1288         THREADVAR(com_failed) = FALSE;
1289         if (dstat != LIVES_STORAGE_STATUS_NORMAL) {
1290           // Houston, we hava problem !
1291           if (!forcecheck) recheck = TRUE;
1292           if (tmpdir) {
1293             /// remove tmpdir and re-check
1294             lives_freep((void **)&mpt);
1295             mpt = get_mountpoint_for(tmpdir);
1296             if (!lives_strcmp(mpt, capable->mountpoint)) {
1297               tempdir_removed = TRUE;
1298               lives_rmdir(tmpdir, TRUE);
1299               if (!THREADVAR(com_failed)) {
1300                 recheck = TRUE;
1301 	    // *INDENT-OFF*
1302           }}}}
1303     // *INDENT-ON*
1304 
1305         if (recheck) {
1306           if (prefs->disk_quota) {
1307             mainw->dsu_valid = TRUE;
1308             dsu = capable->ds_used = get_dir_size(prefs->workdir);
1309           }
1310           dstat = mainw->ds_status = get_storage_status(prefs->workdir, mainw->next_ds_warn_level, &dsu, 0);
1311           capable->ds_free = dsu;
1312         }
1313         if (dstat != LIVES_STORAGE_STATUS_NORMAL) {
1314           if (!tempdir_removed) {
1315             if (tmpdir) {
1316               /// remove tmpdir and re-check
1317               lives_freep((void **)&mpt);
1318               mpt = get_mountpoint_for(tmpdir);
1319               if (!lives_strcmp(mpt, capable->mountpoint)) {
1320                 lives_rmdir(tmpdir, TRUE);
1321                 if (!THREADVAR(com_failed)) {
1322                   if (prefs->disk_quota) {
1323                     dsu = capable->ds_used = get_dir_size(prefs->workdir);
1324                   }
1325                   dstat = mainw->ds_status = get_storage_status(prefs->workdir, mainw->next_ds_warn_level, &dsu, 0);
1326                   capable->ds_free = dsu;
1327 	      // *INDENT-OFF*
1328             }}}}}
1329     // *INDENT-ON*
1330         if (dstat != LIVES_STORAGE_STATUS_NORMAL) {
1331           /// iff critical, delete file
1332           // we should probably offer if warn or quota too
1333           if (mainw->ds_status == LIVES_STORAGE_STATUS_CRITICAL && dest) {
1334             lives_rm(dest);
1335             badfile = TRUE;
1336           }
1337           if (!check_storage_space(-1, FALSE)) {
1338             badfile = TRUE;
1339           }
1340         }
1341       }
1342 
1343       mainw->img_concat_clip = -1;
1344       mainw->no_switch_dprint = FALSE;
1345 
1346       if (dest) {
1347         if (!badfile) {
1348           open_file(dest);
1349           if (mainw->multitrack) {
1350             polymorph(mainw->multitrack, POLY_NONE);
1351             polymorph(mainw->multitrack, POLY_CLIPS);
1352             mt_sensitise(mainw->multitrack);
1353           }
1354         }
1355         lives_free(dest);
1356       }
1357       if (mainw->multitrack) {
1358         maybe_add_mt_idlefunc();
1359       }
1360       return reqout;
1361     }
1362 
1363 
1364     void on_stop_clicked(LiVESMenuItem * menuitem, livespointer user_data) {
1365       // 'enough' button for open, open location, and record audio
1366 
1367 #ifdef ENABLE_JACK
1368       if (mainw->jackd && mainw->jackd_read && mainw->jackd_read->in_use) {
1369         mainw->cancelled = CANCEL_KEEP;
1370         return;
1371       }
1372 #endif
1373 #ifdef HAVE_PULSE_AUDIO
1374       if (mainw->pulsed && mainw->pulsed_read && mainw->pulsed_read->in_use) {
1375         mainw->cancelled = CANCEL_KEEP;
1376         return;
1377       }
1378 #endif
1379 
1380       if (CURRENT_CLIP_IS_VALID) {
1381         lives_kill_subprocesses(cfile->handle, FALSE);
1382         if (mainw->proc_ptr) {
1383           if (mainw->proc_ptr->stop_button)
1384             lives_widget_set_sensitive(mainw->proc_ptr->stop_button, FALSE);
1385           lives_widget_set_sensitive(mainw->proc_ptr->pause_button, FALSE);
1386           lives_widget_set_sensitive(mainw->proc_ptr->preview_button, FALSE);
1387           lives_widget_set_sensitive(mainw->proc_ptr->cancel_button, FALSE);
1388         }
1389       }
1390 
1391       // resume to allow return
1392       if (mainw->effects_paused && CURRENT_CLIP_IS_VALID) {
1393         lives_suspend_resume_process(cfile->handle, FALSE);
1394       }
1395     }
1396 
1397 
1398     void on_save_as_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1399       if (cfile->frames == 0) {
1400         on_export_audio_activate(NULL, NULL);
1401         return;
1402       }
1403       save_file(mainw->current_file, cfile->start, cfile->end, NULL);
1404     }
1405 
1406 
1407 #ifdef LIBAV_TRANSCODE
1408     void on_transcode_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1409       if (!CURRENT_CLIP_IS_VALID) return;
1410       transcode_clip(cfile->start, cfile->end, FALSE, NULL);
1411     }
1412 #endif
1413 
1414 
1415     void on_save_selection_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1416       if (!CURRENT_CLIP_IS_VALID) return;
1417       save_file(mainw->current_file, cfile->start, cfile->end, NULL);
1418     }
1419 
1420 
1421     static void check_remove_layout_files(void) {
1422       if (prompt_remove_layout_files()) {
1423         // delete layout directory
1424         char *laydir = lives_build_filename(prefs->workdir, mainw->set_name, LAYOUTS_DIRNAME, NULL);
1425         lives_rmdir(laydir, TRUE);
1426         lives_free(laydir);
1427         d_print(_("Layouts were removed for set %s.\n"), mainw->set_name);
1428       }
1429     }
1430 
1431 
1432     void on_close_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1433       char *warn, *extra;
1434       boolean lmap_errors = FALSE, acurrent = FALSE, only_current = FALSE;
1435       mainw->mt_needs_idlefunc = FALSE;
1436 
1437       if (mainw->multitrack) {
1438         if (mainw->multitrack->idlefunc > 0) {
1439           lives_source_remove(mainw->multitrack->idlefunc);
1440           mainw->multitrack->idlefunc = 0;
1441           mainw->mt_needs_idlefunc = TRUE;
1442         }
1443         mt_desensitise(mainw->multitrack);
1444         mainw->current_file = mainw->multitrack->file_selected;
1445       } else desensitize();
1446 
1447       if (!(prefs->warning_mask & WARN_MASK_LAYOUT_CLOSE_FILE)) {
1448         mainw->xlays = layout_frame_is_affected(mainw->current_file, 1, 0, mainw->xlays);
1449         mainw->xlays = layout_audio_is_affected(mainw->current_file, 0., 0., mainw->xlays);
1450 
1451         acurrent = used_in_current_layout(mainw->multitrack, mainw->current_file);
1452         if (acurrent) {
1453           if (!mainw->xlays) only_current = TRUE;
1454           mainw->xlays = lives_list_append_unique(mainw->xlays, mainw->string_constants[LIVES_STRING_CONSTANT_CL]);
1455         }
1456 
1457         if (mainw->xlays) {
1458           char *title = get_menu_name(cfile, FALSE);
1459           if (strlen(title) > 128) {
1460             lives_free(title);
1461             title = (_("This file"));
1462           }
1463           if (acurrent) extra = (_(",\n - including the current layout - "));
1464           else extra = lives_strdup("");
1465           if (!only_current) warn = lives_strdup_printf(_("\n%s\nis used in some multitrack layouts%s.\n\nReally close it ?"),
1466                                       title, extra);
1467           else warn = lives_strdup_printf(_("\n%s\nis used in the current layout.\n\nReally close it ?"), title);
1468           lives_free(title);
1469           lives_free(extra);
1470           if (!do_warning_dialog(warn)) {
1471             lives_free(warn);
1472             lives_list_free_all(&mainw->xlays);
1473             goto close_done;
1474           }
1475           lives_free(warn);
1476           add_lmap_error(LMAP_ERROR_CLOSE_FILE, cfile->name, cfile->layout_map, 0, 1, 0., acurrent);
1477           lmap_errors = TRUE;
1478           lives_list_free_all(&mainw->xlays);
1479         }
1480       }
1481       if (!lmap_errors) {
1482         if (cfile->changed) {
1483           if (!do_close_changed_warn()) {
1484             goto close_done;
1485           }
1486         }
1487       }
1488 
1489       if (mainw->sl_undo_mem && (cfile->stored_layout_frame != 0 || cfile->stored_layout_audio != 0.)) {
1490         // need to invalidate undo/redo stack, in case file was used in some layout undo
1491         stored_event_list_free_undos();
1492       }
1493 
1494       if (mainw->multitrack) {
1495         event_list_free_undos(mainw->multitrack);
1496       }
1497 
1498       close_current_file(0);
1499 
1500       if (mainw->multitrack) {
1501         mainw->current_file = mainw->multitrack->render_file;
1502         polymorph(mainw->multitrack, POLY_NONE);
1503         polymorph(mainw->multitrack, POLY_CLIPS);
1504         if (mainw->multitrack->event_list) only_current = FALSE;
1505       }
1506 
1507       if (lmap_errors && !only_current && mainw->cliplist) popup_lmap_errors(NULL, NULL);
1508 
1509       if (!mainw->cliplist && *mainw->set_name) {
1510         boolean has_layout_map = FALSE;
1511 
1512         // check for layout maps
1513         if (mainw->current_layouts_map) {
1514           has_layout_map = TRUE;
1515         }
1516 
1517         if (has_layout_map) {
1518           check_remove_layout_files();
1519           recover_layout_cancelled(FALSE);
1520         }
1521         // the user closed the last clip in the set, we should remove the set
1522         d_print(_("Removing set %s since it is now empty..."));
1523         cleanup_set_dir(mainw->set_name);
1524         lives_memset(mainw->set_name, 0, 1);
1525         mainw->was_set = FALSE;
1526         lives_widget_set_sensitive(mainw->vj_load_set, TRUE);
1527         d_print_done();
1528       }
1529 
1530 close_done:
1531       if (mainw->multitrack) {
1532         mt_sensitise(mainw->multitrack);
1533         maybe_add_mt_idlefunc();
1534       } else sensitize();
1535     }
1536 
1537 
1538     void on_import_proj_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1539       char *com;
1540       char *filt[] = {"*."LIVES_FILE_EXT_PROJECT, NULL};
1541       char *proj_file = choose_file(NULL, NULL, filt, LIVES_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
1542       char *new_set;
1543       char *set_dir;
1544       char *msg;
1545 
1546       int current_file = mainw->current_file;
1547 
1548       if (!proj_file) return;
1549       com = lives_strdup_printf("%s get_proj_set \"%s\"", prefs->backend_sync, proj_file);
1550       lives_popen(com, FALSE, mainw->msg, MAINW_MSG_SIZE);
1551       lives_free(com);
1552 
1553       if (THREADVAR(com_failed)) {
1554         THREADVAR(com_failed) = FALSE;
1555         lives_free(proj_file);
1556         return;
1557       }
1558 
1559       if (!(*mainw->msg)) {
1560         lives_free(proj_file);
1561         widget_opts.non_modal = TRUE;
1562         do_error_dialog(_("\nInvalid project file.\n"));
1563         widget_opts.non_modal = FALSE;
1564         return;
1565       }
1566 
1567       if (!is_legal_set_name(mainw->msg, TRUE, FALSE)) return;
1568 
1569       new_set = lives_strdup(mainw->msg);
1570       set_dir = lives_build_filename(prefs->workdir, new_set, NULL);
1571 
1572       if (lives_file_test(set_dir, LIVES_FILE_TEST_IS_DIR)) {
1573         msg = lives_strdup_printf(
1574                 _("\nA set called %s already exists.\nIn order to import this project, you must rename or delete the existing set.\n"
1575                   "You can do this by File|Reload Set, and giving the set name\n%s\n"
1576                   "then File|Close/Save all Clips and provide a new set name or discard it.\n"
1577                   "Once you have done this, you will be able to import the new project.\n"),
1578                 new_set, new_set);
1579         do_error_dialog(msg);
1580         lives_free(msg);
1581         lives_free(proj_file);
1582         lives_free(set_dir);
1583         lives_free(new_set);
1584         return;
1585       }
1586 
1587       lives_free(set_dir);
1588 
1589       d_print(_("Importing the project %s as set %s..."), proj_file, new_set);
1590 
1591       if (!get_temp_handle(-1)) {
1592         lives_free(proj_file);
1593         lives_free(new_set);
1594         d_print_failed();
1595         return;
1596       }
1597 
1598       com = lives_strdup_printf("%s import_project \"%s\" \"%s\"", prefs->backend, cfile->handle, proj_file);
1599       lives_system(com, FALSE);
1600       lives_free(com);
1601       lives_free(proj_file);
1602 
1603       if (THREADVAR(com_failed)) {
1604         mainw->current_file = close_temp_handle(current_file);
1605         lives_free(new_set);
1606         d_print_failed();
1607         return;
1608       }
1609 
1610       do_progress_dialog(TRUE, FALSE, _("Importing project"));
1611 
1612       mainw->current_file = close_temp_handle(current_file);
1613       sensitize();
1614 
1615       if (mainw->error) {
1616         lives_free(new_set);
1617         d_print_failed();
1618         return;
1619       }
1620 
1621       d_print_done();
1622 
1623       reload_set(new_set);
1624       lives_free(new_set);
1625     }
1626 
1627 
1628     void on_export_proj_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1629       char *filt[] = {"*."LIVES_FILE_EXT_PROJECT, NULL};
1630       char *def_file;
1631       char *proj_file;
1632       char *com, *tmp;
1633 
1634       if (!*mainw->set_name) {
1635         int response;
1636         char new_set_name[MAX_SET_NAME_LEN];
1637         do {
1638           // prompt for a set name, advise user to save set
1639           renamew = create_rename_dialog(5);
1640           lives_widget_show_all(renamew->dialog);
1641           response = lives_dialog_run(LIVES_DIALOG(renamew->dialog));
1642           if (response == LIVES_RESPONSE_CANCEL) {
1643             mainw->cancelled = CANCEL_USER;
1644             return;
1645           }
1646           lives_snprintf(new_set_name, MAX_SET_NAME_LEN, "%s", (tmp = U82F(lives_entry_get_text(LIVES_ENTRY(renamew->entry)))));
1647           lives_widget_destroy(renamew->dialog);
1648           lives_freep((void **)&renamew);
1649           lives_free(tmp);
1650           lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
1651 
1652         } while (!is_legal_set_name(new_set_name, FALSE, FALSE));
1653         lives_snprintf(mainw->set_name, MAX_SET_NAME_LEN, "%s", new_set_name);
1654       }
1655 
1656       if (mainw->stored_event_list && mainw->stored_event_list_changed) {
1657         if (!check_for_layout_del(NULL, FALSE)) return;
1658       }
1659 
1660       if (mainw->sl_undo_mem) stored_event_list_free_undos();
1661 
1662       if (!mainw->was_set) {
1663         mainw->no_exit = TRUE;
1664         if (!on_save_set_activate(NULL, NULL)) return;
1665         mainw->no_exit = FALSE;
1666         mainw->was_set = TRUE;
1667         if (mainw->multitrack && !mainw->multitrack->changed) recover_layout_cancelled(FALSE);
1668       }
1669 
1670       def_file = lives_strdup_printf("%s.%s", mainw->set_name, LIVES_FILE_EXT_PROJECT);
1671       proj_file = choose_file(NULL, def_file, filt, LIVES_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
1672       lives_free(def_file);
1673 
1674       if (!proj_file) return;
1675 
1676       lives_rm((tmp = lives_filename_from_utf8(proj_file, -1, NULL, NULL, NULL)));
1677       lives_free(tmp);
1678 
1679       d_print(_("Exporting project %s..."), proj_file);
1680 
1681       com = lives_strdup_printf("%s export_project \"%s\" \"%s\" \"%s\"", prefs->backend, cfile->handle, mainw->set_name, proj_file);
1682       lives_system(com, FALSE);
1683       lives_free(com);
1684 
1685       if (THREADVAR(com_failed)) {
1686         lives_free(proj_file);
1687         d_print_failed();
1688         return;
1689       }
1690 
1691       do_progress_dialog(TRUE, FALSE, _("Exporting project"));
1692 
1693       if (mainw->error) d_print_failed();
1694       else d_print_done();
1695 
1696       lives_free(proj_file);
1697     }
1698 
1699 
1700     void on_export_theme_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1701       lives_colRGBA64_t lcol;
1702 
1703       char *filt[] = {"*."LIVES_FILE_EXT_TAR_GZ, NULL};
1704 
1705       char theme_name[128];
1706 
1707       char *file_name, *tmp, *tmp2, *com, *fname;
1708       char *sepimg_ext, *frameimg_ext, *sepimg, *frameimg;
1709       char *themedir, *thfile, *themefile;
1710       char *pstyle;
1711 
1712       int response;
1713 
1714       desensitize();
1715 
1716       do {
1717         // prompt for a set name, advise user to save set
1718         renamew = create_rename_dialog(8);
1719         lives_widget_show_all(renamew->dialog);
1720         response = lives_dialog_run(LIVES_DIALOG(renamew->dialog));
1721         if (response == LIVES_RESPONSE_CANCEL) return;
1722         lives_snprintf(theme_name, 128, "%s", (tmp = U82F(lives_entry_get_text(LIVES_ENTRY(renamew->entry)))));
1723         lives_widget_destroy(renamew->dialog);
1724         lives_freep((void **)&renamew);
1725         lives_free(tmp);
1726         lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
1727 
1728       } while (!do_std_checks(U82F(theme_name), _("Theme"), 64, NULL));
1729 
1730       fname = lives_strdup_printf("%s.%s", theme_name, LIVES_FILE_EXT_TAR_GZ);
1731 
1732       file_name = choose_file(capable->home_dir, fname, filt,
1733                               LIVES_FILE_CHOOSER_ACTION_SAVE, _("Choose a directory to export to"), NULL);
1734 
1735       lives_free(fname);
1736 
1737       if (!file_name) {
1738         return;
1739       }
1740 
1741       lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
1742       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
1743 
1744       // create a header.theme file in tmp, then zip it up with the images
1745 
1746       sepimg_ext = get_extension(mainw->sepimg_path);
1747       frameimg_ext = get_extension(mainw->frameblank_path);
1748 
1749       thfile = lives_strdup_printf("%s%d", THEME_LITERAL, capable->mainpid);
1750       themedir = lives_build_filename(prefs->workdir, thfile, NULL);
1751       themefile = lives_build_filename(themedir, THEME_HEADER, NULL);
1752 #ifdef GUI_GTK
1753 #if !GTK_CHECK_VERSION(3, 0, 0)
1754       lives_free(themefile);
1755       themefile = lives_build_filename(themedir, THEME_HEADER_2, NULL);
1756 #endif
1757 #endif
1758       lives_free(thfile);
1759 
1760       thfile = lives_strdup_printf("%s.%s", THEME_SEP_IMG_LITERAL, sepimg_ext);
1761       sepimg = lives_build_filename(themedir, thfile, NULL);
1762       lives_free(thfile);
1763 
1764       thfile = lives_strdup_printf("%s.%s", THEME_FRAME_IMG_LITERAL, frameimg_ext);
1765       frameimg = lives_build_filename(themedir, thfile, NULL);
1766 
1767       lives_free(sepimg_ext);
1768       lives_free(frameimg_ext);
1769 
1770       lives_mkdir_with_parents(themedir, capable->umask);
1771 
1772       set_theme_pref(themefile, THEME_DETAIL_NAME, theme_name);
1773 
1774       pstyle = lives_strdup_printf("%d", palette->style);
1775       set_theme_pref(themefile, THEME_DETAIL_STYLE, pstyle);
1776       lives_free(pstyle);
1777 
1778       widget_color_to_lives_rgba(&lcol, &palette->normal_fore);
1779       set_theme_colour_pref(themefile, THEME_DETAIL_NORMAL_FORE, &lcol);
1780 
1781       widget_color_to_lives_rgba(&lcol, &palette->normal_back);
1782       set_theme_colour_pref(themefile, THEME_DETAIL_NORMAL_BACK, &lcol);
1783 
1784       widget_color_to_lives_rgba(&lcol, &palette->menu_and_bars_fore);
1785       set_theme_colour_pref(themefile, THEME_DETAIL_ALT_FORE, &lcol);
1786 
1787       widget_color_to_lives_rgba(&lcol, &palette->menu_and_bars);
1788       set_theme_colour_pref(themefile, THEME_DETAIL_ALT_BACK, &lcol);
1789 
1790       widget_color_to_lives_rgba(&lcol, &palette->info_text);
1791       set_theme_colour_pref(themefile, THEME_DETAIL_INFO_TEXT, &lcol);
1792 
1793       widget_color_to_lives_rgba(&lcol, &palette->info_base);
1794       set_theme_colour_pref(themefile, THEME_DETAIL_INFO_BASE, &lcol);
1795 
1796       if (mainw->fx1_bool) {
1797         widget_color_to_lives_rgba(&lcol, &palette->mt_timecode_fg);
1798         set_theme_colour_pref(themefile, THEME_DETAIL_MT_TCFG, &lcol);
1799 
1800         widget_color_to_lives_rgba(&lcol, &palette->mt_timecode_bg);
1801         set_theme_colour_pref(themefile, THEME_DETAIL_MT_TCBG, &lcol);
1802 
1803         set_theme_colour_pref(themefile, THEME_DETAIL_AUDCOL, &palette->audcol);
1804         set_theme_colour_pref(themefile, THEME_DETAIL_VIDCOL, &palette->vidcol);
1805         set_theme_colour_pref(themefile, THEME_DETAIL_FXCOL, &palette->fxcol);
1806 
1807         set_theme_colour_pref(themefile, THEME_DETAIL_MT_TLREG, &palette->mt_timeline_reg);
1808         set_theme_colour_pref(themefile, THEME_DETAIL_MT_MARK, &palette->mt_mark);
1809         set_theme_colour_pref(themefile, THEME_DETAIL_MT_EVBOX, &palette->mt_evbox);
1810 
1811         set_theme_colour_pref(themefile, THEME_DETAIL_FRAME_SURROUND, &palette->frame_surround);
1812 
1813         set_theme_colour_pref(themefile, THEME_DETAIL_CE_SEL, &palette->ce_sel);
1814         set_theme_colour_pref(themefile, THEME_DETAIL_CE_UNSEL, &palette->ce_unsel);
1815       }
1816 
1817       lives_free(themefile);
1818 
1819       d_print(_("Exporting theme as %s..."), file_name);
1820 
1821       // copy images for packaging
1822       lives_cp(mainw->sepimg_path, sepimg);
1823       lives_free(sepimg);
1824 
1825       if (THREADVAR(com_failed)) {
1826         lives_rmdir(themedir, TRUE);
1827         lives_free(frameimg);
1828         lives_free(file_name);
1829         lives_free(themedir);
1830         d_print_failed();
1831         sensitize();
1832         return;
1833       }
1834 
1835       lives_cp(mainw->frameblank_path, frameimg);
1836       lives_free(frameimg);
1837 
1838       if (THREADVAR(com_failed)) {
1839         lives_rmdir(themedir, TRUE);
1840         lives_free(file_name);
1841         lives_free(themedir);
1842         d_print_failed();
1843         sensitize();
1844         return;
1845       }
1846 
1847       com = lives_strdup_printf("%s create_package \"%s\" \"%s\"", prefs->backend_sync,
1848                                 (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)),
1849                                 (tmp2 = lives_filename_from_utf8(themedir, -1, NULL, NULL, NULL)));
1850 
1851       lives_free(tmp);
1852       lives_free(tmp2);
1853       lives_free(file_name);
1854 
1855       lives_system(com, TRUE);
1856       lives_free(com);
1857 
1858       lives_rmdir(themedir, TRUE);
1859       lives_free(themedir);
1860 
1861       if (THREADVAR(com_failed)) {
1862         d_print_failed();
1863         sensitize();
1864         return;
1865       }
1866 
1867       d_print_done();
1868       sensitize();
1869       lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1870     }
1871 
1872 
1873     void on_import_theme_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1874       char *filt[] = {"*."LIVES_FILE_EXT_TAR_GZ, NULL};
1875       char tname[128];
1876 
1877       char *importcheckdir, *themeheader, *themedir;
1878       char *com;
1879       char *theme_file;
1880 
1881       desensitize();
1882 
1883       theme_file = choose_file(NULL, NULL, filt, LIVES_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
1884 
1885       if (!theme_file) {
1886         sensitize();
1887         return;
1888       }
1889       lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
1890       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
1891 
1892 
1893       importcheckdir = lives_build_filename(prefs->workdir, IMPORTS_DIRNAME, NULL);
1894       lives_rmdir(importcheckdir, TRUE);
1895 
1896       // unpackage file to get the theme name
1897       com = lives_strdup_printf("%s import_package \"%s\" \"%s\"", prefs->backend_sync, U82F(theme_file), importcheckdir);
1898       lives_system(com, FALSE);
1899       lives_free(com);
1900 
1901       if (THREADVAR(com_failed)) {
1902         lives_rmdir(importcheckdir, TRUE);
1903         lives_free(importcheckdir);
1904         lives_free(theme_file);
1905         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1906         sensitize();
1907         return;
1908       }
1909 
1910       themeheader = lives_build_filename(prefs->workdir, IMPORTS_DIRNAME, THEME_HEADER, NULL);
1911 
1912       if (get_pref_from_file(themeheader, THEME_DETAIL_NAME, tname, 128) == LIVES_RESPONSE_NO) {
1913         // failed to get name
1914         lives_rmdir(importcheckdir, TRUE);
1915         lives_free(importcheckdir);
1916         lives_free(themeheader);
1917         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1918         do_bad_theme_import_error(theme_file);
1919         lives_free(theme_file);
1920         sensitize();
1921         return;
1922       }
1923 
1924       lives_rmdir(importcheckdir, TRUE);
1925       lives_free(importcheckdir);
1926       lives_free(themeheader);
1927 
1928       d_print(_("Importing theme \"%s\" from %s..."), tname, theme_file);
1929 
1930       if (!do_std_checks(U82F(tname), _("Theme"), 64, NULL)) {
1931         lives_free(theme_file);
1932         d_print_failed();
1933         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1934         return;
1935       }
1936 
1937       // check for existing dupes
1938 
1939       themedir = lives_build_filename(prefs->config_datadir, PLUGIN_THEMES, tname, NULL);
1940 
1941       if (lives_file_test(themedir, LIVES_FILE_TEST_IS_DIR)) {
1942         if (!do_theme_exists_warn(tname)) {
1943           lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1944           lives_free(themedir);
1945           lives_free(theme_file);
1946           d_print_failed();
1947           sensitize();
1948           return;
1949         }
1950         lives_rmdir(themedir, TRUE);
1951       }
1952 
1953       // name was OK, unpack into custom dir
1954       com = lives_strdup_printf("%s import_package \"%s\" \"%s\"", prefs->backend_sync, U82F(theme_file), themedir);
1955       lives_system(com, FALSE);
1956       lives_free(com);
1957 
1958       lives_free(theme_file);
1959 
1960       if (THREADVAR(com_failed)) {
1961         lives_rmdir(themedir, TRUE);
1962         lives_free(themedir);
1963         d_print_failed();
1964         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1965         sensitize();
1966         return;
1967       }
1968 
1969       lives_free(themedir);
1970 
1971       lives_snprintf(prefs->theme, 64, "%s", tname);
1972 
1973       // try to set theme colours
1974       if (!set_palette_colours(TRUE)) {
1975         lives_snprintf(prefs->theme, 64, "%s", future_prefs->theme);
1976         d_print_failed();
1977         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1978         sensitize();
1979         return;
1980       }
1981 
1982       lives_snprintf(future_prefs->theme, 64, "%s", prefs->theme);
1983       set_string_pref(PREF_GUI_THEME, prefs->theme);
1984 
1985       load_theme_images();
1986       pref_change_images();
1987       pref_change_colours();
1988       pref_change_xcolours();
1989 
1990       d_print_done();
1991       sensitize();
1992       lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1993     }
1994 
1995 
1996     void on_backup_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1997       char *filt[] = {"*."LIVES_FILE_EXT_BACKUP, NULL};
1998       char *file_name;
1999       char *defname, *text;
2000 
2001       defname = lives_strdup_printf("%s.%s", cfile->name, LIVES_FILE_EXT_BACKUP);
2002 
2003       text = lives_strdup_printf(_("Backup as %s File"), LIVES_FILE_EXT_BACKUP);
2004 
2005       file_name = choose_file((*mainw->proj_save_dir) ? mainw->proj_save_dir : NULL, defname, filt,
2006                               LIVES_FILE_CHOOSER_ACTION_SAVE, text, NULL);
2007 
2008       lives_free(text);
2009       lives_free(defname);
2010 
2011       if (!file_name) return;
2012 
2013       backup_file(mainw->current_file, 1, cfile->frames, file_name);
2014 
2015       lives_snprintf(mainw->proj_save_dir, PATH_MAX, "%s", file_name);
2016       get_dirname(mainw->proj_save_dir);
2017       lives_free(file_name);
2018     }
2019 
2020 
2021     void on_restore_activate(LiVESMenuItem * menuitem, livespointer user_data) {
2022       char *filt[] = {"*."LIVES_FILE_EXT_BACKUP, NULL};
2023       char *file_name, *text;
2024 
2025       text = lives_strdup_printf(_("Restore %s File"), LIVES_FILE_EXT_BACKUP);
2026 
2027       file_name = choose_file((*mainw->proj_load_dir) ? mainw->proj_load_dir : NULL, text, filt,
2028                               LIVES_FILE_CHOOSER_ACTION_OPEN, text, NULL);
2029 
2030       lives_free(text);
2031 
2032       if (!file_name) return;
2033 
2034       restore_file(file_name);
2035 
2036       lives_snprintf(mainw->proj_load_dir, PATH_MAX, "%s", file_name);
2037       get_dirname(mainw->proj_load_dir);
2038       lives_free(file_name);
2039       check_storage_space(-1, FALSE);
2040     }
2041 
2042 
2043     void mt_memory_free(void) {
2044       int i;
2045 
2046       threaded_dialog_spin(0.);
2047 
2048       mainw->multitrack->no_expose = TRUE;
2049 
2050       if (CURRENT_CLIP_HAS_AUDIO) {
2051         delete_audio_tracks(mainw->multitrack, mainw->multitrack->audio_draws, FALSE);
2052         if (mainw->multitrack->audio_vols) lives_list_free(mainw->multitrack->audio_vols);
2053       }
2054 
2055       if (mainw->multitrack->video_draws) {
2056         for (i = 0; i < mainw->multitrack->num_video_tracks; i++) {
2057           delete_video_track(mainw->multitrack, i, FALSE);
2058         }
2059         lives_list_free(mainw->multitrack->video_draws);
2060       }
2061 
2062       lives_widget_object_unref(mainw->multitrack->clip_scroll);
2063       lives_widget_object_unref(mainw->multitrack->in_out_box);
2064 
2065       lives_list_free(mainw->multitrack->tl_marks);
2066 
2067       if (mainw->multitrack->event_list) event_list_free(mainw->multitrack->event_list);
2068       mainw->multitrack->event_list = NULL;
2069 
2070       if (mainw->multitrack->undo_mem) event_list_free_undos(mainw->multitrack);
2071 
2072       recover_layout_cancelled(FALSE);
2073 
2074       threaded_dialog_spin(0.);
2075     }
2076 
2077 
2078     void del_current_set(boolean exit_after) {
2079       char *msg;
2080       boolean moc = mainw->only_close, crec;
2081       mainw->only_close = !exit_after;
2082       set_string_pref(PREF_AR_CLIPSET, "");
2083       prefs->ar_clipset = FALSE;
2084 
2085       if (mainw->multitrack) {
2086         event_list_free_undos(mainw->multitrack);
2087 
2088         if (mainw->multitrack->event_list) {
2089           event_list_free(mainw->multitrack->event_list);
2090           mainw->multitrack->event_list = NULL;
2091         }
2092       }
2093 
2094       // check for layout maps
2095       if (mainw->current_layouts_map) {
2096         check_remove_layout_files();
2097       }
2098 
2099       if (mainw->multitrack && !mainw->only_close) mt_memory_free();
2100       else if (mainw->multitrack) wipe_layout(mainw->multitrack);
2101 
2102       mainw->was_set = mainw->leave_files = mainw->leave_recovery = FALSE;
2103 
2104       recover_layout_cancelled(FALSE);
2105 
2106       if (mainw->clips_available) {
2107         if (*mainw->set_name)
2108           msg = lives_strdup_printf(_("Deleting set %s..."), mainw->set_name);
2109         else
2110           msg = lives_strdup(_("Deleting set..."));
2111         d_print(msg);
2112         lives_free(msg);
2113 
2114         do_threaded_dialog(_("Deleting set"), FALSE);
2115       }
2116 
2117       crec = prefs->crash_recovery;
2118       prefs->crash_recovery = FALSE;
2119 
2120       // do a lot of cleanup, delete files
2121       lives_exit(0);
2122       prefs->crash_recovery = crec;
2123 
2124       if (*mainw->set_name) {
2125         d_print(_("Set %s was permanently deleted from the disk.\n"), mainw->set_name);
2126         lives_memset(mainw->set_name, 0, 1);
2127       }
2128       mainw->only_close = moc;
2129       end_threaded_dialog();
2130     }
2131 
2132 
2133     void on_quit_activate(LiVESMenuItem * menuitem, livespointer user_data) {
2134       char *tmp;
2135       boolean legal_set_name;
2136 
2137       mainw->mt_needs_idlefunc = FALSE;
2138 
2139       if (user_data && LIVES_POINTER_TO_INT(user_data) == 1) {
2140         mainw->no_exit = TRUE;
2141         mainw->only_close = TRUE;
2142       } else {
2143         mainw->no_exit = FALSE;
2144         mainw->only_close = FALSE;
2145       }
2146 
2147       // stop if playing
2148       if (LIVES_IS_PLAYING) {
2149         mainw->cancelled = CANCEL_APP_QUIT;
2150         mainw->only_close = mainw->is_exiting = FALSE;
2151         return;
2152       }
2153 
2154       if (mainw->multitrack) {
2155         if (mainw->multitrack->idlefunc > 0) {
2156           lives_source_remove(mainw->multitrack->idlefunc);
2157           mainw->multitrack->idlefunc = 0;
2158           mainw->mt_needs_idlefunc = TRUE;
2159         }
2160         mt_desensitise(mainw->multitrack);
2161       }
2162 
2163       if (mainw->multitrack && mainw->multitrack->event_list) {
2164         if (mainw->only_close) {
2165           if (!check_for_layout_del(mainw->multitrack, FALSE)) {
2166             if (mainw->multitrack) {
2167               mt_sensitise(mainw->multitrack);
2168               maybe_add_mt_idlefunc();
2169             }
2170             return;
2171 	// *INDENT-OFF*
2172       }}}
2173   // *INDENT-ON*
2174 
2175       if (mainw->stored_event_list && mainw->stored_event_list_changed) {
2176         if (!check_for_layout_del(NULL, FALSE)) {
2177           mainw->only_close = mainw->is_exiting = FALSE;
2178           return;
2179         }
2180       } else if (mainw->stored_layout_undos) {
2181         stored_event_list_free_undos();
2182       }
2183 
2184       /// do not popup warning if set name is changed
2185       mainw->suppress_layout_warnings = TRUE;
2186 
2187       if (!mainw->clips_available || (future_prefs->vj_mode && !mainw->no_exit)) {
2188         lives_exit(0);
2189       } else {
2190         char *set_name;
2191         _entryw *cdsw = create_cds_dialog(1);
2192         LiVESResponseType resp;
2193         do {
2194           legal_set_name = TRUE;
2195           lives_widget_show_all(cdsw->dialog);
2196           resp = lives_dialog_run(LIVES_DIALOG(cdsw->dialog));
2197 
2198           if (resp == LIVES_RESPONSE_RETRY) continue;
2199 
2200           if (resp == LIVES_RESPONSE_CANCEL) {
2201             lives_widget_destroy(cdsw->dialog);
2202             lives_free(cdsw);
2203             mainw->only_close = mainw->is_exiting = FALSE;
2204             if (mainw->multitrack) {
2205               mt_sensitise(mainw->multitrack);
2206               maybe_add_mt_idlefunc();
2207             }
2208             mainw->only_close = mainw->is_exiting = FALSE;
2209             return;
2210           }
2211           if (resp == LIVES_RESPONSE_ACCEPT) {
2212             // save set
2213             if ((legal_set_name = is_legal_set_name((set_name = U82F(lives_entry_get_text(LIVES_ENTRY(cdsw->entry)))),
2214                                                     TRUE, FALSE))) {
2215               lives_widget_destroy(cdsw->dialog);
2216               lives_free(cdsw);
2217 
2218               if (prefs->ar_clipset) set_string_pref(PREF_AR_CLIPSET, set_name);
2219               else set_string_pref(PREF_AR_CLIPSET, "");
2220 
2221               mainw->leave_recovery = FALSE;
2222 
2223               on_save_set_activate(NULL, (tmp = U82F(set_name)));
2224               lives_free(tmp);
2225               lives_free(set_name);
2226 
2227               if (!mainw->no_exit) lives_exit(0);
2228 
2229               if (mainw->multitrack) {
2230                 mt_sensitise(mainw->multitrack);
2231                 maybe_add_mt_idlefunc();
2232               }
2233               mainw->only_close = mainw->is_exiting = FALSE;
2234               if (mainw->cs_manage) lives_idle_add_simple(run_diskspace_dialog_idle, NULL);
2235               end_threaded_dialog();
2236               return;
2237             }
2238             resp = LIVES_RESPONSE_RETRY;
2239             lives_widget_hide(cdsw->dialog);
2240             lives_free(set_name);
2241           }
2242           if (resp == LIVES_RESPONSE_RESET) {
2243             char *what, *expl;
2244             // TODO
2245             //if (check_for_executable(&capable->has_gio, EXEC_GIO)) mainw->add_trash_rb = TRUE;
2246             if (mainw->was_set) {
2247               what = lives_strdup_printf(_("Set '%s'"), mainw->set_name);
2248               expl = lives_strdup("");
2249             } else {
2250               what = (_("All currently open clips"));
2251               expl = (_("<b>(Note: original source material will NOT be affected !)</b>"));
2252               widget_opts.use_markup = TRUE;
2253             }
2254             if (!do_warning_dialogf(_("\n\n%s will be permanently deleted from the disk.\nAre you sure ?\n\n%s"), what, expl)) {
2255               resp = LIVES_RESPONSE_ABORT;
2256             }
2257             widget_opts.use_markup = FALSE;
2258             lives_free(what); lives_free(expl);
2259             //mainw->add_trash_rb = FALSE;
2260           }
2261         } while (resp == LIVES_RESPONSE_ABORT || resp == LIVES_RESPONSE_RETRY);
2262 
2263         lives_widget_destroy(cdsw->dialog);
2264         lives_free(cdsw);
2265 
2266         // discard clipset
2267         del_current_set(!mainw->only_close);
2268       }
2269 
2270       mainw->leave_recovery = TRUE;
2271     }
2272 
2273 
2274     // TODO - split into undo.c
2275     void on_undo_activate(LiVESWidget * menuitem, livespointer user_data) {
2276       char *com, *tmp;
2277 
2278       boolean bad_header = FALSE;
2279       boolean retvalb;
2280 
2281       int ostart = cfile->start;
2282       int oend = cfile->end;
2283       int current_file = mainw->current_file;
2284       int switch_file = current_file;
2285       int asigned, aendian;
2286       int i;
2287 
2288       if (mainw->multitrack) return;
2289 
2290       lives_widget_set_sensitive(mainw->undo, FALSE);
2291       lives_widget_set_sensitive(mainw->redo, TRUE);
2292       cfile->undoable = FALSE;
2293       cfile->redoable = TRUE;
2294       lives_widget_hide(mainw->undo);
2295       lives_widget_show(mainw->redo);
2296 
2297       mainw->osc_block = TRUE;
2298 
2299       d_print("");
2300 
2301       if (menuitem) {
2302         mainw->no_switch_dprint = TRUE;
2303         d_print("%s...", lives_menu_item_get_text(LIVES_WIDGET(menuitem)));
2304         mainw->no_switch_dprint = FALSE;
2305       }
2306 
2307       if (cfile->undo_action == UNDO_INSERT_SILENCE) {
2308         double start = cfile->undo1_dbl;
2309         // if old audio end < start then we want to delete from oae to end
2310         if (cfile->old_laudio_time < start) cfile->undo1_dbl = cfile->old_laudio_time;
2311         on_del_audio_activate(NULL, NULL);
2312         cfile->undo_action = UNDO_INSERT_SILENCE;
2313         set_redoable(_("Insert Silence"), TRUE);
2314         cfile->undo1_dbl = start;
2315       }
2316 
2317       if (cfile->undo_action == UNDO_CUT || cfile->undo_action == UNDO_DELETE || cfile->undo_action == UNDO_DELETE_AUDIO) {
2318         int reset_achans = 0;
2319         lives_rm(cfile->info_file);
2320         if (cfile->achans != cfile->undo_achans) {
2321           if (cfile->audio_waveform) {
2322             for (i = 0; i < cfile->achans; lives_freep((void **)&cfile->audio_waveform[i++]));
2323             lives_freep((void **)&cfile->audio_waveform);
2324             lives_freep((void **)&cfile->aw_sizes);
2325           }
2326         }
2327 
2328         cfile->arate = cfile->undo_arate;
2329         cfile->signed_endian = cfile->undo_signed_endian;
2330         cfile->achans = cfile->undo_achans;
2331         cfile->asampsize = cfile->undo_asampsize;
2332         cfile->arps = cfile->undo_arps;
2333 
2334         if (cfile->frames == 0) {
2335           cfile->hsize = cfile->ohsize;
2336           cfile->vsize = cfile->ovsize;
2337         }
2338 
2339         if (cfile->undo_action == UNDO_DELETE_AUDIO) {
2340           if (cfile->undo1_dbl == cfile->undo2_dbl && cfile->undo1_dbl == 0.) {
2341             // undo delete_all_audio
2342             reset_achans = cfile->undo_achans;
2343             com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, cfile->handle);
2344           }
2345           // undo delete selected audio
2346           // (set with with_audio==2 [audio only],therfore start,end,where are in secs.; times==-1)
2347           else com = lives_strdup_printf("%s insert \"%s\" \"%s\" %.8f 0. %.8f \"%s\" 2 0 0 0 0 %d %d %d %d %d -1",
2348                                            prefs->backend,
2349                                            cfile->handle, get_image_ext_for_type(cfile->img_type), cfile->undo1_dbl,
2350                                            cfile->undo2_dbl - cfile->undo1_dbl, cfile->handle, cfile->arps, cfile->achans,
2351                                            cfile->asampsize, !(cfile->signed_endian & AFORM_UNSIGNED),
2352                                            !(cfile->signed_endian & AFORM_BIG_ENDIAN));
2353         } else {
2354           // undo cut or delete (times to insert is -1)
2355           // start,end, where are in frames
2356           cfile->undo1_boolean &= mainw->ccpd_with_sound;
2357           com = lives_strdup_printf("%s insert \"%s\" \"%s\" %d %d %d \"%s\" %d %d 0 0 %.3f %d %d %d %d %d -1",
2358                                     prefs->backend, cfile->handle,
2359                                     get_image_ext_for_type(cfile->img_type), cfile->undo_start - 1, cfile->undo_start,
2360                                     cfile->undo_end, cfile->handle, cfile->undo1_boolean, cfile->frames, cfile->fps,
2361                                     cfile->arps, cfile->achans, cfile->asampsize, !(cfile->signed_endian & AFORM_UNSIGNED),
2362                                     !(cfile->signed_endian & AFORM_BIG_ENDIAN));
2363 
2364         }
2365 
2366         lives_system(com, FALSE);
2367         lives_free(com);
2368 
2369         if (THREADVAR(com_failed)) return;
2370 
2371         // show a progress dialog, not cancellable
2372         do_progress_dialog(TRUE, FALSE, _("Undoing"));
2373 
2374         if (mainw->error) {
2375           d_print_failed();
2376           //cfile->may_be_damaged=TRUE;
2377           return;
2378         }
2379 
2380         if (cfile->undo_action != UNDO_DELETE_AUDIO) {
2381           cfile->insert_start = cfile->undo_start;
2382           cfile->insert_end = cfile->undo_end;
2383 
2384           if (cfile->start >= cfile->undo_start) {
2385             cfile->start += cfile->undo_end - cfile->undo_start + 1;
2386           }
2387           if (cfile->end >= cfile->undo_start) {
2388             cfile->end += cfile->undo_end - cfile->undo_start + 1;
2389           }
2390 
2391           cfile->frames += cfile->undo_end - cfile->undo_start + 1;
2392           if (cfile->frames > 0) {
2393             if (cfile->start == 0) {
2394               cfile->start = 1;
2395             }
2396             if (cfile->end == 0) {
2397               cfile->end = cfile->frames;
2398             }
2399           }
2400           if (cfile->frame_index_back) {
2401             restore_frame_index_back(mainw->current_file);
2402           }
2403           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FRAMES, &cfile->frames)) bad_header = TRUE;
2404           showclipimgs();
2405         }
2406         if (reset_achans > 0) {
2407           if (cfile->audio_waveform) {
2408             for (i = 0; i < cfile->achans; lives_freep((void **)&cfile->audio_waveform[i++]));
2409             lives_freep((void **)&cfile->audio_waveform);
2410             lives_freep((void **)&cfile->aw_sizes);
2411           }
2412           asigned = !(cfile->signed_endian & AFORM_UNSIGNED);
2413           aendian = cfile->signed_endian & AFORM_BIG_ENDIAN;
2414           cfile->achans = reset_achans;
2415           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ACHANS, &cfile->achans)) bad_header = TRUE;
2416           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ARATE, &cfile->arps)) bad_header = TRUE;
2417           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_ARATE, &cfile->arate)) bad_header = TRUE;
2418           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASAMPS, &cfile->asampsize)) bad_header = TRUE;
2419           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_AENDIAN, &aendian)) bad_header = TRUE;
2420           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASIGNED, &asigned)) bad_header = TRUE;
2421         }
2422 
2423         showclipimgs();
2424 
2425         if (bad_header) do_header_write_error(mainw->current_file);
2426       }
2427 
2428       if (cfile->undo_action == UNDO_RESIZABLE || cfile->undo_action == UNDO_RENDER || cfile->undo_action == UNDO_EFFECT ||
2429           cfile->undo_action == UNDO_MERGE || (cfile->undo_action == UNDO_ATOMIC_RESAMPLE_RESIZE &&
2430               (cfile->frames != cfile->old_frames || cfile->hsize != cfile->ohsize ||
2431                cfile->vsize != cfile->ovsize || cfile->fps != cfile->undo1_dbl))) {
2432         char *audfile;
2433 
2434         com = lives_strdup_printf("%s undo \"%s\" %d %d \"%s\"", prefs->backend, cfile->handle, cfile->undo_start, cfile->undo_end,
2435                                   get_image_ext_for_type(cfile->img_type));
2436         lives_rm(cfile->info_file);
2437         lives_system(com, FALSE);
2438         lives_free(com);
2439 
2440         if (THREADVAR(com_failed)) return;
2441 
2442         mainw->cancelled = CANCEL_NONE;
2443         mainw->error = FALSE;
2444 
2445         // show a progress dialog, not cancellable
2446         cfile->progress_start = cfile->undo_start;
2447         cfile->progress_end = cfile->undo_end;
2448         do_progress_dialog(TRUE, FALSE, _("Undoing"));
2449 
2450         if (cfile->undo_action == UNDO_RENDER || cfile->undo_action == UNDO_MERGE) {
2451           audfile = lives_get_audio_file_name(mainw->current_file);
2452           tmp = lives_strdup_printf("%s.%s", audfile, LIVES_FILE_EXT_BAK);
2453           lives_free(audfile);
2454           audfile = tmp;
2455           if (lives_file_test(audfile, LIVES_FILE_TEST_EXISTS)) {
2456             // restore overwritten audio
2457             com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, cfile->handle);
2458             lives_rm(cfile->info_file);
2459             lives_system(com, FALSE);
2460             lives_free(com);
2461             if (THREADVAR(com_failed)) {
2462               lives_free(audfile);
2463               return;
2464             }
2465             retvalb = do_auto_dialog(_("Restoring audio"), 0);
2466             if (!retvalb) {
2467               d_print_failed();
2468               //cfile->may_be_damaged=TRUE;
2469               return;
2470             }
2471           }
2472           lives_free(audfile);
2473         }
2474 
2475         if (cfile->frame_index_back) {
2476           int *tmpindex = cfile->frame_index;
2477           cfile->clip_type = CLIP_TYPE_FILE;
2478           cfile->frame_index = cfile->frame_index_back;
2479           if (cfile->undo_action == UNDO_RENDER) {
2480             do_threaded_dialog(_("Clearing frame images"), FALSE);
2481             clean_images_from_virtual(cfile, cfile->undo_start, cfile->undo_end);
2482             save_frame_index(mainw->current_file);
2483             cfile->frame_index_back = NULL;
2484             end_threaded_dialog();
2485           } else {
2486             save_frame_index(mainw->current_file);
2487             cfile->frame_index_back = tmpindex;
2488           }
2489         }
2490       }
2491 
2492       if (cfile->undo_action == UNDO_ATOMIC_RESAMPLE_RESIZE && (cfile->frames != cfile->old_frames ||
2493           cfile->hsize != cfile->ohsize || cfile->vsize != cfile->ovsize)) {
2494 
2495         if (cfile->frames > cfile->old_frames) {
2496           com = lives_strdup_printf("%s cut \"%s\" %d %d %d %d \"%s\" %.3f %d %d %d",
2497                                     prefs->backend, cfile->handle, cfile->old_frames + 1,
2498                                     cfile->frames, FALSE, cfile->frames, get_image_ext_for_type(cfile->img_type),
2499                                     cfile->fps, cfile->arate, cfile->achans, cfile->asampsize);
2500 
2501           cfile->progress_start = cfile->old_frames + 1;
2502           cfile->progress_end = cfile->frames;
2503 
2504           lives_rm(cfile->info_file);
2505           lives_system(com, FALSE);
2506           lives_free(com);
2507 
2508           if (THREADVAR(com_failed)) return;
2509 
2510           // show a progress dialog, not cancellable
2511           do_progress_dialog(TRUE, FALSE, _("Deleting excess frames"));
2512 
2513           if (cfile->clip_type == CLIP_TYPE_FILE) {
2514             delete_frames_from_virtual(mainw->current_file, cfile->old_frames + 1, cfile->frames);
2515           }
2516         }
2517 
2518         cfile->frames = cfile->old_frames;
2519         cfile->hsize = cfile->ohsize;
2520         cfile->vsize = cfile->ovsize;
2521         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_WIDTH, &cfile->hsize)) bad_header = TRUE;
2522         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_HEIGHT, &cfile->vsize)) bad_header = TRUE;
2523         cfile->fps = cfile->undo1_dbl;
2524         if (cfile->clip_type == CLIP_TYPE_FILE && cfile->ext_src) {
2525           lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
2526           double dfps = (double)cdata->fps;
2527           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &dfps)) bad_header = TRUE;
2528         } else {
2529           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &cfile->fps)) bad_header = TRUE;
2530         }
2531         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_FPS, &cfile->fps)) bad_header = TRUE;
2532         cfile->redoable = FALSE;
2533         // force a resize in switch_to_file
2534         switch_file = 0;
2535 
2536         if (bad_header) do_header_write_error(mainw->current_file);
2537       }
2538 
2539       if (cfile->undo_action == UNDO_RENDER) {
2540         cfile->frames = cfile->old_frames;
2541         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FRAMES, &cfile->frames)) bad_header = TRUE;
2542         showclipimgs();
2543         if (bad_header) do_header_write_error(mainw->current_file);
2544       }
2545 
2546       if (cfile->undo_action == UNDO_INSERT || cfile->undo_action == UNDO_MERGE
2547           || cfile->undo_action == UNDO_INSERT_WITH_AUDIO) {
2548         boolean ccpd_with_sound = mainw->ccpd_with_sound;
2549         if (!(cfile->undo_action == UNDO_MERGE && cfile->insert_start == cfile->undo_start && cfile->insert_end == cfile->undo_end)) {
2550           if (cfile->undo_action == UNDO_MERGE) {
2551             if (cfile->insert_start == cfile->undo_start) {
2552               cfile->insert_start = cfile->undo_end + 1;
2553             }
2554             if (cfile->insert_end == cfile->undo_end) {
2555               cfile->insert_end = cfile->undo_start - 1;
2556             }
2557           }
2558           cfile->start = cfile->insert_start;
2559           cfile->end = cfile->insert_end;
2560 
2561           if (cfile->undo_action == UNDO_INSERT_WITH_AUDIO) mainw->ccpd_with_sound = TRUE;
2562           else mainw->ccpd_with_sound = FALSE;
2563           on_delete_activate(NULL, NULL);
2564 
2565           cfile->start = ostart;
2566           if (ostart >= cfile->insert_start) {
2567             cfile->start -= cfile->insert_end - cfile->insert_start + 1;
2568             if (cfile->start < cfile->insert_start - 1) {
2569               cfile->start = cfile->insert_start - 1;
2570             }
2571           }
2572           cfile->end = oend;
2573           if (oend >= cfile->insert_start) {
2574             cfile->end -= cfile->insert_end - cfile->insert_start + 1;
2575             if (cfile->end < cfile->insert_start - 1) {
2576               cfile->end = cfile->insert_start - 1;
2577             }
2578           }
2579           // TODO - use lives_clip_start macro
2580           if (cfile->start < 1) cfile->start = cfile->frames > 0 ? 1 : 0;
2581           if (cfile->end < 1) cfile->end = cfile->frames > 0 ? 1 : 0;
2582 
2583           cfile->insert_start = cfile->insert_end = 0;
2584         }
2585         mainw->ccpd_with_sound = ccpd_with_sound;
2586         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FRAMES, &cfile->frames)) bad_header = TRUE;
2587         showclipimgs();
2588         if (bad_header) do_header_write_error(mainw->current_file);
2589       }
2590 
2591       if (cfile->undo_action == UNDO_REC_AUDIO) {
2592         mainw->fx1_val = cfile->arate;
2593         mainw->fx2_val = cfile->achans;
2594         mainw->fx3_val = cfile->asampsize;
2595         mainw->fx4_val = cfile->signed_endian;
2596         mainw->fx5_val = cfile->arps;
2597       }
2598 
2599       if (cfile->undo_action == UNDO_AUDIO_RESAMPLE || cfile->undo_action == UNDO_REC_AUDIO ||
2600           cfile->undo_action == UNDO_FADE_AUDIO || cfile->undo_action == UNDO_AUDIO_VOL ||
2601           cfile->undo_action == UNDO_TRIM_AUDIO || cfile->undo_action == UNDO_APPEND_AUDIO ||
2602           (cfile->undo_action == UNDO_ATOMIC_RESAMPLE_RESIZE && cfile->arate != cfile->undo1_int)) {
2603         com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, cfile->handle);
2604         mainw->cancelled = CANCEL_NONE;
2605         mainw->error = FALSE;
2606         lives_system(com, FALSE);
2607         lives_free(com);
2608 
2609         if (THREADVAR(com_failed)) {
2610           reget_afilesize(mainw->current_file);
2611           d_print_failed();
2612           return;
2613         }
2614         if (!do_auto_dialog(_("Undoing"), 0)) {
2615           reget_afilesize(mainw->current_file);
2616           d_print_failed();
2617           return;
2618         }
2619       }
2620 
2621       if ((cfile->undo_action == UNDO_AUDIO_RESAMPLE) || (cfile->undo_action == UNDO_ATOMIC_RESAMPLE_RESIZE &&
2622           cfile->arate != cfile->undo1_int)) {
2623         cfile->arate += cfile->undo1_int;
2624         cfile->undo1_int = cfile->arate - cfile->undo1_int;
2625         cfile->arate -= cfile->undo1_int;
2626 
2627         cfile->achans += cfile->undo2_int;
2628         cfile->undo2_int = cfile->achans - cfile->undo2_int;
2629         cfile->achans -= cfile->undo2_int;
2630 
2631         cfile->asampsize += cfile->undo3_int;
2632         cfile->undo3_int = cfile->asampsize - cfile->undo3_int;
2633         cfile->asampsize -= cfile->undo3_int;
2634 
2635         cfile->arps += cfile->undo4_int;
2636         cfile->undo4_int = cfile->arps - cfile->undo4_int;
2637         cfile->arps -= cfile->undo4_int;
2638 
2639         cfile->signed_endian += cfile->undo1_uint;
2640         cfile->undo1_uint = cfile->signed_endian - cfile->undo1_uint;
2641         cfile->signed_endian -= cfile->undo1_uint;
2642 
2643         asigned = !(cfile->signed_endian & AFORM_UNSIGNED);
2644         aendian = cfile->signed_endian & AFORM_BIG_ENDIAN;
2645 
2646         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ARATE, &cfile->arps)) bad_header = TRUE;
2647         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_ARATE, &cfile->arate)) bad_header = TRUE;
2648         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ACHANS, &cfile->achans)) bad_header = TRUE;
2649         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASAMPS, &cfile->asampsize)) bad_header = TRUE;
2650         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_AENDIAN, &aendian)) bad_header = TRUE;
2651         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASIGNED, &asigned)) bad_header = TRUE;
2652 
2653         if (bad_header) do_header_write_error(mainw->current_file);
2654       }
2655 
2656       if (cfile->undo_action == UNDO_NEW_AUDIO) {
2657         lives_rm(cfile->info_file);
2658         com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, cfile->handle);
2659         lives_system(com, FALSE);
2660         lives_free(com);
2661 
2662         if (THREADVAR(com_failed)) return;
2663 
2664         mainw->cancelled = CANCEL_NONE;
2665         mainw->error = FALSE;
2666 
2667         if (!do_auto_dialog(_("Restoring audio"), 0)) {
2668           d_print_failed();
2669           return;
2670         }
2671 
2672         if (cfile->achans != cfile->undo_achans) {
2673           if (cfile->audio_waveform) {
2674             for (i = 0; i < cfile->achans; lives_freep((void **)&cfile->audio_waveform[i++]));
2675             lives_freep((void **)&cfile->audio_waveform);
2676             lives_freep((void **)&cfile->aw_sizes);
2677           }
2678         }
2679 
2680         cfile->achans = cfile->undo_achans;
2681         cfile->arate = cfile->undo_arate;
2682         cfile->arps = cfile->undo_arps;
2683         cfile->asampsize = cfile->undo_asampsize;
2684         cfile->signed_endian = cfile->undo_signed_endian;
2685 
2686         asigned = !(cfile->signed_endian & AFORM_UNSIGNED);
2687         aendian = cfile->signed_endian & AFORM_BIG_ENDIAN;
2688 
2689         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ARATE, &cfile->arps)) bad_header = TRUE;
2690         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_ARATE, &cfile->arate)) bad_header = TRUE;
2691         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ACHANS, &cfile->achans)) bad_header = TRUE;
2692         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASAMPS, &cfile->asampsize)) bad_header = TRUE;
2693         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_AENDIAN, &aendian)) bad_header = TRUE;
2694         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASIGNED, &asigned)) bad_header = TRUE;
2695 
2696         if (bad_header) do_header_write_error(mainw->current_file);
2697       }
2698 
2699       if (cfile->undo_action == UNDO_CHANGE_SPEED) {
2700         cfile->fps += cfile->undo1_dbl;
2701         cfile->undo1_dbl = cfile->fps - cfile->undo1_dbl;
2702         cfile->fps -= cfile->undo1_dbl;
2703 
2704         cfile->arate += cfile->undo1_int;
2705         cfile->undo1_int = cfile->arate - cfile->undo1_int;
2706         cfile->arate -= cfile->undo1_int;
2707         /// DON'T ! we only save the pb_fps now, since otherwise we may lose the orig. clip fps
2708         /* save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &cfile->fps); */
2709         if (cfile->clip_type == CLIP_TYPE_FILE && cfile->ext_src) {
2710           lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
2711           double dfps = (double)cdata->fps;
2712           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &dfps)) bad_header = TRUE;
2713         } else {
2714           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &cfile->fps)) bad_header = TRUE;
2715         }
2716         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_FPS, &cfile->fps)) bad_header = TRUE;
2717         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_ARATE, &cfile->arate)) bad_header = TRUE;
2718 
2719         if (bad_header) do_header_write_error(mainw->current_file);
2720       }
2721 
2722       if (cfile->undo_action == UNDO_INSERT || cfile->undo_action == UNDO_INSERT_WITH_AUDIO || cfile->undo_action == UNDO_MERGE ||
2723           cfile->undo_action == UNDO_NEW_AUDIO) {
2724         cfile->redoable = FALSE;
2725       }
2726 
2727       if (menuitem) {
2728         mainw->no_switch_dprint = TRUE;
2729         d_print_done();
2730         mainw->no_switch_dprint = FALSE;
2731       }
2732 
2733       if (cfile->undo_action == UNDO_RESAMPLE) {
2734         cfile->start = (int)((cfile->start - 1) / cfile->fps * cfile->undo1_dbl + 1.);
2735         if ((cfile->end = (int)(cfile->end / cfile->fps * cfile->undo1_dbl + .49999)) < 1) cfile->end = 1;
2736         cfile->fps += cfile->undo1_dbl;
2737         cfile->undo1_dbl = cfile->fps - cfile->undo1_dbl;
2738         cfile->fps -= cfile->undo1_dbl;
2739 
2740         // deorder the frames
2741         cfile->frames = deorder_frames(cfile->old_frames, mainw->current_file == 0 && !prefs->conserve_space);
2742 
2743         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FRAMES, &cfile->frames)) bad_header = TRUE;
2744         if (cfile->clip_type == CLIP_TYPE_FILE && cfile->ext_src) {
2745           lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
2746           double dfps = (double)cdata->fps;
2747           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &dfps)) bad_header = TRUE;
2748         } else {
2749           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &cfile->fps)) bad_header = TRUE;
2750         }
2751         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_FPS, &cfile->fps)) bad_header = TRUE;
2752 
2753         if (bad_header) do_header_write_error(mainw->current_file);
2754 
2755         if (mainw->current_file > 0) {
2756           com = lives_strdup_printf(_("Length of video is now %d frames at %.3f frames per second.\n"), cfile->frames, cfile->fps);
2757         } else {
2758           mainw->no_switch_dprint = TRUE;
2759           com = lives_strdup_printf(_("Clipboard was resampled to %d frames.\n"), cfile->frames);
2760         }
2761         d_print(com);
2762         lives_free(com);
2763         mainw->no_switch_dprint = FALSE;
2764         showclipimgs();
2765       }
2766 
2767       if (cfile->end > cfile->frames) {
2768         cfile->end = cfile->frames;
2769       }
2770 
2771       if (cfile->undo_action == UNDO_RESIZABLE) {
2772         cfile->vsize += cfile->ovsize;
2773         cfile->ovsize = cfile->vsize - cfile->ovsize;
2774         cfile->vsize -= cfile->ovsize;
2775         cfile->hsize += cfile->ohsize;
2776         cfile->ohsize = cfile->hsize - cfile->ohsize;
2777         cfile->hsize -= cfile->ohsize;
2778         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_WIDTH, &cfile->hsize)) bad_header = TRUE;
2779         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_HEIGHT, &cfile->vsize)) bad_header = TRUE;
2780 
2781         // force a resize in switch_to_file
2782         switch_file = 0;
2783 
2784         if (bad_header) do_header_write_error(mainw->current_file);
2785       }
2786 
2787       if (current_file > 0) {
2788         switch_to_file((mainw->current_file = switch_file), current_file);
2789       }
2790 
2791       if (cfile->undo_action == UNDO_RENDER) {
2792         if (mainw->event_list) event_list_free(mainw->event_list);
2793         mainw->event_list = cfile->event_list_back;
2794         cfile->event_list_back = NULL;
2795         deal_with_render_choice(FALSE);
2796       }
2797       mainw->osc_block = FALSE;
2798 
2799       if (!mainw->multitrack) {
2800         reget_afilesize(mainw->current_file);
2801         if (CURRENT_CLIP_HAS_AUDIO) redraw_timeline(mainw->current_file);
2802       }
2803     }
2804 
2805 
2806     void on_redo_activate(LiVESWidget * menuitem, livespointer user_data) {
2807       char *com;
2808 
2809       int ostart = cfile->start;
2810       int oend = cfile->end;
2811       int current_file = mainw->current_file;
2812       int i;
2813 
2814       mainw->osc_block = TRUE;
2815 
2816       cfile->undoable = TRUE;
2817       cfile->redoable = FALSE;
2818       lives_widget_hide(mainw->redo);
2819       lives_widget_show(mainw->undo);
2820       lives_widget_set_sensitive(mainw->undo, TRUE);
2821       lives_widget_set_sensitive(mainw->redo, FALSE);
2822 
2823       d_print("");
2824 
2825       if (menuitem) {
2826         mainw->no_switch_dprint = TRUE;
2827         d_print("%s...", lives_menu_item_get_text(menuitem));
2828         mainw->no_switch_dprint = FALSE;
2829       }
2830 
2831       if (cfile->undo_action == UNDO_INSERT_SILENCE) {
2832         on_ins_silence_activate(NULL, NULL);
2833         mainw->osc_block = FALSE;
2834         mainw->no_switch_dprint = TRUE;
2835         d_print_done();
2836         mainw->no_switch_dprint = FALSE;
2837         sensitize();
2838         return;
2839       }
2840       if (cfile->undo_action == UNDO_CHANGE_SPEED) {
2841         on_change_speed_ok_clicked(NULL, NULL);
2842         mainw->osc_block = FALSE;
2843         d_print_done();
2844         return;
2845       }
2846       if (cfile->undo_action == UNDO_RESAMPLE) {
2847         on_resample_vid_ok(NULL, NULL);
2848         mainw->osc_block = FALSE;
2849         return;
2850       }
2851       if (cfile->undo_action == UNDO_AUDIO_RESAMPLE) {
2852         on_resaudio_ok_clicked(NULL, NULL);
2853         mainw->osc_block = FALSE;
2854         d_print_done();
2855         return;
2856       }
2857       if (cfile->undo_action == UNDO_CUT || cfile->undo_action == UNDO_DELETE) {
2858         cfile->start = cfile->undo_start;
2859         cfile->end = cfile->undo_end;
2860         mainw->osc_block = FALSE;
2861       }
2862       if (cfile->undo_action == UNDO_CUT) {
2863         on_cut_activate(NULL, NULL);
2864         mainw->osc_block = FALSE;
2865       }
2866       if (cfile->undo_action == UNDO_DELETE) {
2867         on_delete_activate(NULL, NULL);
2868         mainw->osc_block = FALSE;
2869       }
2870       if (cfile->undo_action == UNDO_DELETE_AUDIO) {
2871         on_del_audio_activate(NULL, NULL);
2872         mainw->osc_block = FALSE;
2873         d_print_done();
2874         return;
2875       }
2876       if (cfile->undo_action == UNDO_CUT || cfile->undo_action == UNDO_DELETE) {
2877         cfile->start = ostart;
2878         cfile->end = oend;
2879         if (mainw->current_file == current_file) {
2880           if (cfile->start >= cfile->undo_start) {
2881             cfile->start -= cfile->undo_end - cfile->undo_start + 1;
2882             if (cfile->start < cfile->undo_start - 1) {
2883               cfile->start = cfile->undo_start - 1;
2884             }
2885           }
2886           if (cfile->end >= cfile->undo_start) {
2887             cfile->end -= cfile->undo_end - cfile->undo_start + 1;
2888             if (cfile->end < cfile->undo_start - 1) {
2889               cfile->end = cfile->undo_start - 1;
2890             }
2891           }
2892           switch_to_file(mainw->current_file, mainw->current_file);
2893         }
2894         mainw->osc_block = FALSE;
2895         return;
2896       }
2897 
2898       if (cfile->undo_action == UNDO_REC_AUDIO) {
2899         if (cfile->audio_waveform) {
2900           for (i = 0; i < cfile->achans; lives_freep((void **)&cfile->audio_waveform[i++]));
2901           lives_freep((void **)&cfile->audio_waveform);
2902           lives_freep((void **)&cfile->aw_sizes);
2903         }
2904         cfile->arate = mainw->fx1_val;
2905         cfile->achans = mainw->fx2_val;
2906         cfile->asampsize = mainw->fx3_val;
2907         cfile->signed_endian = mainw->fx4_val;
2908         cfile->arps = mainw->fx5_val;
2909         save_clip_values(mainw->current_file);
2910       }
2911 
2912       if (cfile->undo_action == UNDO_REC_AUDIO || cfile->undo_action == UNDO_FADE_AUDIO
2913           || cfile->undo_action == UNDO_TRIM_AUDIO || cfile->undo_action == UNDO_AUDIO_VOL ||
2914           cfile->undo_action == UNDO_APPEND_AUDIO) {
2915         com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, cfile->handle);
2916         lives_rm(cfile->info_file);
2917         lives_system(com, FALSE);
2918         lives_free(com);
2919 
2920         if (THREADVAR(com_failed)) {
2921           reget_afilesize(mainw->current_file);
2922           d_print_failed();
2923           return;
2924         }
2925 
2926         // show a progress dialog, not cancellable
2927         do_progress_dialog(TRUE, FALSE, _("Redoing"));
2928 
2929         if (mainw->error) {
2930           reget_afilesize(mainw->current_file);
2931           d_print_failed();
2932           return;
2933         }
2934 
2935         d_print_done();
2936         switch_to_file(mainw->current_file, mainw->current_file);
2937         mainw->osc_block = FALSE;
2938         return;
2939       }
2940 
2941       com = lives_strdup_printf("%s redo \"%s\" %d %d \"%s\"", prefs->backend, cfile->handle, cfile->undo_start, cfile->undo_end,
2942                                 get_image_ext_for_type(cfile->img_type));
2943       lives_rm(cfile->info_file);
2944       lives_system(com, FALSE);
2945       lives_free(com);
2946 
2947       if (THREADVAR(com_failed)) {
2948         d_print_failed();
2949         return;
2950       }
2951 
2952       cfile->progress_start = cfile->undo_start;
2953       cfile->progress_end = cfile->undo_end;
2954 
2955       // show a progress dialog, not cancellable
2956       do_progress_dialog(TRUE, FALSE, _("Redoing"));
2957       reget_afilesize(mainw->current_file);
2958 
2959       if (mainw->error) {
2960         d_print_failed();
2961         return;
2962       }
2963 
2964       if (cfile->clip_type == CLIP_TYPE_FILE && (cfile->undo_action == UNDO_EFFECT || cfile->undo_action == UNDO_RESIZABLE)) {
2965         int *tmpindex = cfile->frame_index;
2966         cfile->frame_index = cfile->frame_index_back;
2967         cfile->frame_index_back = tmpindex;
2968         cfile->clip_type = CLIP_TYPE_FILE;
2969         if (!check_if_non_virtual(mainw->current_file, 1, cfile->frames)) save_frame_index(mainw->current_file);
2970       }
2971 
2972       if (cfile->undo_action == UNDO_RESIZABLE) {
2973         cfile->vsize += cfile->ovsize;
2974         cfile->ovsize = cfile->vsize - cfile->ovsize;
2975         cfile->vsize -= cfile->ovsize;
2976         cfile->hsize += cfile->ohsize;
2977         cfile->ohsize = cfile->hsize - cfile->ohsize;
2978         cfile->hsize -= cfile->ohsize;
2979         switch_to_file((mainw->current_file = 0), current_file);
2980       } else {
2981         if (cfile->end <= cfile->undo_end) load_end_image(cfile->end);
2982         if (cfile->start >= cfile->undo_start) load_start_image(cfile->start);
2983       }
2984 
2985       d_print_done();
2986       mainw->osc_block = FALSE;
2987     }
2988 
2989 
2990     //////////////////////////////////////////////////
2991 
2992     void on_copy_activate(LiVESMenuItem * menuitem, livespointer user_data) {
2993       char *com;
2994 
2995       int current_file = mainw->current_file;
2996       int start, end;
2997       int i;
2998 
2999       desensitize();
3000 
3001       d_print(""); // force switchtext
3002 
3003       if (mainw->ccpd_with_sound && cfile->achans > 0)
3004         d_print(_("Copying frames %d to %d (with sound) to the clipboard..."), cfile->start, cfile->end);
3005       else
3006         d_print(lives_strdup_printf(_("Copying frames %d to %d to the clipboard..."), cfile->start, cfile->end));
3007 
3008       init_clipboard();
3009 
3010       lives_rm(cfile->info_file);
3011       mainw->last_transition_loops = 1;
3012 
3013       start = cfile->start;
3014       end = cfile->end;
3015 
3016       if (cfile->clip_type == CLIP_TYPE_FILE) {
3017         // for virtual frames, we copy only the frame_index
3018         clipboard->clip_type = CLIP_TYPE_FILE;
3019         clipboard->interlace = cfile->interlace;
3020         clipboard->deinterlace = cfile->deinterlace;
3021         clipboard->frame_index = frame_index_copy(cfile->frame_index, end - start + 1, start - 1);
3022         clipboard->frames = end - start + 1;
3023         check_if_non_virtual(0, 1, clipboard->frames);
3024         if (clipboard->clip_type == CLIP_TYPE_FILE) {
3025           clipboard->ext_src = clone_decoder(mainw->current_file);
3026           clipboard->ext_src_type = LIVES_EXT_SRC_DECODER;
3027           end = -end; // allow missing frames
3028           lives_snprintf(clipboard->file_name, PATH_MAX, "%s", cfile->file_name);
3029         }
3030       }
3031 
3032       mainw->fx1_val = 1;
3033       mainw->fx1_bool = FALSE;
3034 
3035       clipboard->img_type = cfile->img_type;
3036 
3037       // copy audio and frames
3038       com = lives_strdup_printf("%s insert \"%s\" \"%s\" 0 %d %d \"%s\" %d 0 0 0 %.3f %d %d %d %d %d", prefs->backend,
3039                                 clipboard->handle, get_image_ext_for_type(clipboard->img_type),
3040                                 start, end, cfile->handle, mainw->ccpd_with_sound, cfile->fps, cfile->arate,
3041                                 cfile->achans, cfile->asampsize, !(cfile->signed_endian & AFORM_UNSIGNED),
3042                                 !(cfile->signed_endian & AFORM_BIG_ENDIAN));
3043 
3044       if (clipboard->clip_type == CLIP_TYPE_FILE) end = -end;
3045 
3046       lives_system(com, FALSE);
3047       lives_free(com);
3048 
3049       if (THREADVAR(com_failed)) {
3050         d_print_failed();
3051         sensitize();
3052         return;
3053       }
3054 
3055       // we need to set this to look at the right info_file
3056       mainw->current_file = 0;
3057       cfile->progress_start = clipboard->start = 1;
3058       cfile->progress_end = clipboard->end = end - start + 1;
3059 
3060       // stop the 'preview' and 'pause' buttons from appearing
3061       cfile->nopreview = TRUE;
3062       if (!do_progress_dialog(TRUE, TRUE, _("Copying to the clipboard"))) {
3063 #ifdef IS_MINGW
3064         // kill any active processes: for other OSes the backend does this
3065         lives_kill_subprocesses(cfile->handle, TRUE);
3066 #endif
3067 
3068         // close clipboard, it is invalid
3069         mainw->current_file = CLIPBOARD_FILE;
3070         close_temp_handle(current_file);
3071 
3072         sensitize();
3073         mainw->cancelled = CANCEL_USER;
3074         return;
3075       }
3076 
3077       cfile->nopreview = FALSE;
3078       mainw->current_file = current_file;
3079 
3080       //set all clipboard details
3081       clipboard->frames = clipboard->old_frames = clipboard->end;
3082       clipboard->hsize = cfile->hsize;
3083       clipboard->vsize = cfile->vsize;
3084       clipboard->bpp = cfile->bpp;
3085       clipboard->gamma_type = cfile->gamma_type;
3086       clipboard->undo1_dbl = clipboard->fps = cfile->fps;
3087       clipboard->ratio_fps = cfile->ratio_fps;
3088       clipboard->is_loaded = TRUE;
3089       lives_snprintf(clipboard->type, 40, "Frames");
3090 
3091       clipboard->asampsize = clipboard->arate = clipboard->achans = 0;
3092       clipboard->afilesize = 0l;
3093 
3094       if (mainw->ccpd_with_sound) {
3095         if (clipboard->audio_waveform) {
3096           for (i = 0; i < clipboard->achans; lives_freep((void **)&clipboard->audio_waveform[i++]));
3097           lives_freep((void **)&clipboard->audio_waveform);
3098           lives_freep((void **)&clipboard->aw_sizes);
3099         }
3100         clipboard->achans = cfile->achans;
3101         clipboard->asampsize = cfile->asampsize;
3102 
3103         clipboard->arate = cfile->arate;
3104         clipboard->arps = cfile->arps;
3105         clipboard->signed_endian = cfile->signed_endian;
3106 
3107         reget_afilesize(0);
3108       }
3109 
3110       clipboard->start = 1;
3111       clipboard->end = clipboard->frames;
3112 
3113       get_total_time(clipboard);
3114 
3115       sensitize();
3116       d_print_done();
3117     }
3118 
3119 
3120     void on_cut_activate(LiVESMenuItem * menuitem, livespointer user_data) {
3121       uint32_t chk_mask = 0;
3122       int current_file = mainw->current_file;
3123 
3124       if (menuitem) {
3125         char *tmp = (_("Cutting"));
3126         chk_mask = WARN_MASK_LAYOUT_DELETE_FRAMES | WARN_MASK_LAYOUT_SHIFT_FRAMES | WARN_MASK_LAYOUT_ALTER_FRAMES;
3127         if (mainw->ccpd_with_sound) chk_mask |= WARN_MASK_LAYOUT_DELETE_AUDIO | WARN_MASK_LAYOUT_SHIFT_AUDIO |
3128                                                   WARN_MASK_LAYOUT_ALTER_AUDIO;
3129         if (!check_for_layout_errors(tmp, mainw->current_file, cfile->start, cfile->end, &chk_mask)) {
3130           lives_free(tmp);
3131           return;
3132         }
3133         lives_free(tmp);
3134       }
3135 
3136       on_copy_activate(menuitem, user_data);
3137       if (mainw->cancelled) {
3138         unbuffer_lmap_errors(FALSE);
3139         return;
3140       }
3141 
3142       on_delete_activate(NULL, user_data);
3143       if (mainw->current_file == current_file) {
3144         set_undoable(_("Cut"), TRUE);
3145         cfile->undo_action = UNDO_CUT;
3146       }
3147 
3148       if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
3149     }
3150 
3151 
3152     void on_paste_as_new_activate(LiVESMenuItem * menuitem, livespointer user_data) {
3153 #define VIRT_PASTE
3154 #ifndef VIRT_PASTE
3155       char *msg;
3156 #endif
3157       char *com;
3158       int old_file = mainw->current_file;
3159       frames_t cbframes, lframe;
3160 
3161       if (!clipboard) return;
3162 
3163       mainw->current_file = mainw->first_free_file;
3164 
3165       if (!get_new_handle(mainw->current_file, NULL)) {
3166         mainw->current_file = old_file;
3167         return;
3168       }
3169 
3170       lframe = cbframes = clipboard->frames;
3171 
3172       //set file details
3173       cfile->hsize = clipboard->hsize;
3174       cfile->vsize = clipboard->vsize;
3175       cfile->pb_fps = cfile->fps = clipboard->fps;
3176       cfile->ratio_fps = clipboard->ratio_fps;
3177       cfile->changed = TRUE;
3178       cfile->is_loaded = TRUE;
3179       cfile->img_type = clipboard->img_type;
3180       cfile->gamma_type = clipboard->gamma_type;
3181 
3182       set_default_comment(cfile, NULL);
3183 
3184 #ifndef VIRT_PASTE
3185       msg = (_("Pulling frames from clipboard..."));
3186 
3187       if ((lframe = realize_all_frames(0, msg, TRUE)) < cbframes) {
3188         if (!paste_enough_dlg(lframe - 1)) {
3189           lives_free(msg);
3190           close_current_file(old_file);
3191           sensitize();
3192           return;
3193         }
3194         lframe--;
3195       }
3196       lives_free(msg);
3197 #else
3198       /// copy the frame_index, and do an insert, skipping missing frames
3199       lframe = -lframe;
3200 #endif
3201 
3202       cfile->progress_start = cfile->start = cbframes > 0 ? 1 : 0;
3203       cfile->progress_end = cfile->end = cfile->frames = cbframes;
3204 
3205       mainw->no_switch_dprint = TRUE;
3206       d_print(_("Pasting %d frames to new clip %s..."), lframe, cfile->name);
3207       mainw->no_switch_dprint = FALSE;
3208 
3209       if (clipboard->achans > 0 && clipboard->arate > 0) {
3210         com = lives_strdup_printf("%s insert \"%s\" \"%s\" 0 1 %d \"%s\" %d 0 0 0 %.3f %d %d %d %d %d",
3211                                   prefs->backend, cfile->handle,
3212                                   get_image_ext_for_type(cfile->img_type), lframe, clipboard->handle,
3213                                   mainw->ccpd_with_sound, clipboard->fps, clipboard->arate, clipboard->achans,
3214                                   clipboard->asampsize, !(cfile->signed_endian & AFORM_UNSIGNED),
3215                                   !(cfile->signed_endian & AFORM_BIG_ENDIAN));
3216       } else {
3217         com = lives_strdup_printf("%s insert \"%s\" \"%s\" 0 1 %d \"%s\" %d 0 0 0 %.3f 0 0 0 0 0",
3218                                   prefs->backend, cfile->handle,
3219                                   get_image_ext_for_type(cfile->img_type), lframe, clipboard->handle,
3220                                   FALSE, clipboard->fps);
3221 
3222         if (clipboard->achans > 0 && clipboard->arate < 0) {
3223           int zero = 0;
3224           double chvols = 1.;
3225           double avels = -1.;
3226           double aseeks = (double)clipboard->afilesize / (double)(-clipboard->arate * clipboard->asampsize / 8 * clipboard->achans);
3227           ticks_t tc = (ticks_t)(aseeks * TICKS_PER_SECOND_DBL);
3228           cfile->arate = clipboard->arate = -clipboard->arate;
3229           cfile->arps = clipboard->arps;
3230           cfile->achans = clipboard->achans;
3231           cfile->asampsize = clipboard->asampsize;
3232           cfile->afilesize = clipboard->afilesize;
3233           cfile->signed_endian = clipboard->signed_endian;
3234           render_audio_segment(1, &zero, mainw->current_file, &avels, &aseeks, 0, tc, &chvols, 1., 1., NULL);
3235         }
3236       }
3237 
3238       lives_system(com, FALSE);
3239       lives_free(com);
3240 
3241       if (THREADVAR(com_failed)) {
3242         d_print_failed();
3243         close_current_file(old_file);
3244         return;
3245       }
3246 
3247       cfile->nopreview = TRUE;
3248 
3249       mainw->disk_mon = MONITOR_QUOTA;
3250 
3251       // show a progress dialog, not cancellable
3252       if (!do_progress_dialog(TRUE, TRUE, _("Pasting"))) {
3253         mainw->disk_mon = 0;
3254         if (mainw->error) d_print_failed();
3255         close_current_file(old_file);
3256         return;
3257       }
3258       cfile->nopreview = FALSE;
3259       mainw->disk_mon = 0;
3260 
3261       if (mainw->ccpd_with_sound) {
3262         if (cfile->audio_waveform) {
3263           for (int i = 0; i < cfile->achans; lives_freep((void **)&cfile->audio_waveform[i++]));
3264           lives_freep((void **)&cfile->audio_waveform);
3265           lives_freep((void **)&cfile->aw_sizes);
3266         }
3267         cfile->arate = clipboard->arate;
3268         cfile->arps = clipboard->arps;
3269         cfile->achans = clipboard->achans;
3270         cfile->asampsize = clipboard->asampsize;
3271         cfile->afilesize = clipboard->afilesize;
3272         cfile->signed_endian = clipboard->signed_endian;
3273         if (cfile->afilesize > 0) d_print(_("...added audio..."));
3274       }
3275 
3276 #ifdef VIRT_PASTE
3277       if (clipboard->frame_index) {
3278         cfile->frame_index = frame_index_copy(clipboard->frame_index, cbframes, 0);
3279       }
3280 
3281       cfile->clip_type = clipboard->clip_type;
3282 
3283       if (cfile->clip_type == CLIP_TYPE_FILE) {
3284         cfile->ext_src = clone_decoder(CLIPBOARD_FILE);
3285         lives_snprintf(cfile->file_name, PATH_MAX, "%s", clipboard->file_name);
3286       }
3287 #endif
3288 
3289       if (cfile->frame_index) save_frame_index(mainw->current_file);
3290 
3291       // add entry to window menu
3292       add_to_clipmenu();
3293       if (!save_clip_values(mainw->current_file)) {
3294         close_current_file(old_file);
3295         return;
3296       }
3297 
3298       if (prefs->crash_recovery) add_to_recovery_file(cfile->handle);
3299 
3300       switch_clip(1, mainw->current_file, TRUE);
3301       d_print_done();
3302 
3303       mainw->last_dprint_file = old_file;
3304       d_print(""); // force switchtext
3305 
3306       lives_notify(LIVES_OSC_NOTIFY_CLIP_OPENED, "");
3307       check_storage_space(-1, FALSE);
3308     }
3309 
3310 
3311     void on_insert_pre_activate(LiVESMenuItem * menuitem, livespointer user_data) {
3312       insertw = create_insert_dialog();
3313 
3314       lives_widget_show_all(insertw->insert_dialog);
3315       mainw->fx1_bool = FALSE;
3316       mainw->fx1_val = 1;
3317 
3318       mainw->fx2_bool = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(insertw->with_sound));
3319     }
3320 
3321 
3322     void on_insert_activate(LiVESButton * button, livespointer user_data) {
3323       double times_to_insert;
3324       double audio_stretch;
3325 
3326       char *com;
3327 
3328       boolean with_sound = mainw->fx2_bool;
3329       boolean bad_header = FALSE;
3330       boolean insert_silence = FALSE;
3331 
3332       // have we resampled ?
3333       boolean cb_audio_change = FALSE;
3334       boolean cb_video_change = FALSE;
3335 
3336       boolean virtual_ins = FALSE;
3337       boolean all_virtual = FALSE;
3338 
3339       uint32_t chk_mask = 0;
3340 
3341       int where = cfile->start - 1;
3342       int start = cfile->start, ostart = start;
3343       int end = cfile->end, oend = end;
3344 
3345       int hsize = cfile->hsize;
3346       int vsize = cfile->vsize;
3347 
3348       int cfile_signed = 0, cfile_endian = 0, clipboard_signed = 0, clipboard_endian = 0;
3349       int current_file = mainw->current_file;
3350 
3351       int orig_frames = cfile->frames;
3352       int ocarps = clipboard->arps;
3353       int leave_backup = 1;
3354       int remainder_frames;
3355       int insert_start;
3356       int cb_start = 1, cb_end = clipboard->frames;
3357       int i;
3358 
3359       // if it is an insert into the original file, and we can do fast seek, we can insert virtual frames
3360       if (button && mainw->current_file == clipboard->cb_src && !check_if_non_virtual(0, 1, clipboard->frames)) {
3361         lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
3362         if (cdata->seek_flag & LIVES_SEEK_FAST) {
3363           virtual_ins = TRUE;
3364           if (count_virtual_frames(clipboard->frame_index, 1, clipboard->frames) == clipboard->frames) all_virtual = TRUE;
3365         }
3366       }
3367 
3368       // don't ask smogrify to resize if frames are the same size and type
3369       if (all_virtual || (((cfile->hsize == clipboard->hsize && cfile->vsize == clipboard->vsize) || orig_frames == 0) &&
3370                           (cfile->img_type == clipboard->img_type))) hsize = vsize = 0;
3371       else {
3372         if (!capable->has_convert) {
3373           widget_opts.non_modal = TRUE;
3374           do_error_dialog(
3375             _("This operation requires resizing or converting of frames.\n"
3376               "Please install 'convert' from the Image-magick package, and then restart LiVES.\n"));
3377           widget_opts.non_modal = FALSE;
3378           mainw->error = TRUE;
3379           if (button) {
3380             lives_widget_destroy(insertw->insert_dialog);
3381             lives_free(insertw);
3382           }
3383           return;
3384         }
3385       }
3386 
3387       if (button) {
3388         lives_widget_destroy(insertw->insert_dialog);
3389         lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
3390         // call to update fx1_val, in case activates_default was called from spin entry
3391         lives_free(insertw);
3392       }
3393 
3394       times_to_insert = mainw->fx1_val;
3395 
3396       // fit video to audio if requested
3397       if (mainw->fx1_bool && (cfile->asampsize * cfile->arate * cfile->achans != 0)) {
3398         // "insert to fit audio" : number of inserts is (audio_time - sel_end_time) / clipboard_time
3399         times_to_insert = (cfile->laudio_time - (cfile->frames > 0 ? (double)cfile->end / cfile->fps : 0.)) / ((
3400                             double)clipboard->frames / clipboard->fps);
3401       }
3402 
3403       if (times_to_insert < 0. && (mainw->fx1_bool)) {
3404         widget_opts.non_modal = TRUE;
3405         do_error_dialog(
3406           _("\n\nVideo is longer than audio.\nTry selecting all frames, and then using \n"
3407             "the 'Trim Audio' function from the Audio menu."));
3408         mainw->error = TRUE;
3409         widget_opts.non_modal = FALSE;
3410         return;
3411       }
3412 
3413       if (with_sound) {
3414         cfile_signed = !(cfile->signed_endian & AFORM_UNSIGNED);
3415         cfile_endian = !(cfile->signed_endian & AFORM_BIG_ENDIAN);
3416 
3417         clipboard_signed = !(clipboard->signed_endian & AFORM_UNSIGNED);
3418         clipboard_endian = !(clipboard->signed_endian & AFORM_BIG_ENDIAN);
3419 
3420         if ((cfile->achans * cfile->arps * cfile->asampsize > 0) && (cfile->achans != clipboard->achans ||
3421             (cfile->arps != clipboard->arps && clipboard->achans > 0) ||
3422             cfile->asampsize != clipboard->asampsize ||
3423             cfile_signed != clipboard_signed || cfile_endian != clipboard_endian ||
3424             cfile->arate != clipboard->arate)) {
3425           if (!(capable->has_sox_sox)) {
3426             if (cfile->arps != clipboard->arps) {
3427               widget_opts.non_modal = TRUE;
3428               do_error_dialog(_("LiVES cannot insert because the audio rates do not match.\n"
3429                                 "Please install 'sox', and try again."));
3430               mainw->error = TRUE;
3431               widget_opts.non_modal = FALSE;
3432               return;
3433 	  // *INDENT-OFF*
3434         }}}}
3435   // *INDENT-ON*
3436 
3437       if (mainw->insert_after) insert_start = cfile->end + 1;
3438       else insert_start = cfile->start;
3439 
3440       if (button) {
3441         char *tmp = (_("Insertion"));
3442         chk_mask = WARN_MASK_LAYOUT_SHIFT_FRAMES | WARN_MASK_LAYOUT_ALTER_FRAMES;
3443         if (with_sound) chk_mask |= WARN_MASK_LAYOUT_SHIFT_AUDIO | WARN_MASK_LAYOUT_ALTER_AUDIO;
3444         if (!check_for_layout_errors(tmp, mainw->current_file, insert_start, 0, &chk_mask)) {
3445           lives_free(tmp);
3446           return;
3447         }
3448         lives_free(tmp);
3449       }
3450 
3451       if (button) {
3452         if ((cfile->fps != clipboard->fps && orig_frames > 0) || (cfile->arps != clipboard->arps && clipboard->achans > 0 &&
3453             with_sound)) {
3454           if (!do_clipboard_fps_warning()) {
3455             unbuffer_lmap_errors(FALSE);
3456             mainw->error = TRUE;
3457             return;
3458           }
3459         }
3460         if (prefs->ins_resample && clipboard->fps != cfile->fps && orig_frames != 0) {
3461           cb_end = count_resampled_frames(clipboard->frames, clipboard->fps, cfile->fps);
3462         }
3463       } else {
3464         // called from on_merge_activate()
3465         cb_start = mainw->fx1_start;
3466         cb_end = mainw->fx2_start;
3467 
3468         // we will use leave_backup as this will leave our
3469         // merge backup in place
3470         leave_backup = -1;
3471       }
3472 
3473       cfile->insert_start = insert_start;
3474       cfile->insert_end = cfile->insert_start - 1;
3475 
3476       if (mainw->insert_after) where = cfile->end;
3477 
3478       // at least we should try to convert the audio to match...
3479       // if with_sound is TRUE, and clipboard has no audio, we will insert silence (unless target
3480       // also has no audio
3481       if (with_sound) {
3482         if (clipboard->achans == 0) {
3483           if (cfile->achans > 0) insert_silence = TRUE;
3484           with_sound = FALSE;
3485         } else {
3486           if ((cfile->achans * cfile->arps * cfile->asampsize > 0)
3487               && clipboard->achans > 0 && (cfile->achans != clipboard->achans ||
3488                                            cfile->arps != clipboard->arps || clipboard->vol != 1. || cfile->vol != 1. ||
3489                                            cfile->asampsize != clipboard->asampsize ||
3490                                            cfile_signed != clipboard_signed ||
3491                                            cfile_endian != clipboard_endian || cfile->arate != clipboard->arate)) {
3492 
3493             cb_audio_change = TRUE;
3494 
3495             if (clipboard->arps != clipboard->arps || cfile->arate != clipboard->arate) {
3496               // pb rate != real rate - stretch to pb rate and resample
3497               if ((audio_stretch = (double)clipboard->arps / (double)clipboard->arate *
3498                                    (double)cfile->arate / (double)cfile->arps) != 1.) {
3499                 if (audio_stretch < 0.) {
3500                   // clipboard audio should be reversed
3501                   // we will create a temp handle, copy the audio, and then render it back reversed
3502                   if (!get_temp_handle(-1)) {
3503                     d_print_failed();
3504                     return;
3505                   } else {
3506                     char *fnameto = lives_get_audio_file_name(mainw->current_file);
3507                     char *fnamefrom = lives_get_audio_file_name(0);
3508                     int zero = 0;
3509                     float volx = 1.;
3510                     double chvols = 1.;
3511                     double avels = -1.;
3512                     double aseeks = (double)clipboard->afilesize / (double)(-clipboard->arate
3513                                     * clipboard->asampsize / 8 * clipboard->achans);
3514                     ticks_t tc = (ticks_t)(aseeks * TICKS_PER_SECOND_DBL);
3515                     if (cfile->vol > 0.001) volx = clipboard->vol / cfile->vol;
3516                     render_audio_segment(1, &zero, mainw->current_file, &avels, &aseeks, 0, tc, &chvols, volx, volx, NULL);
3517                     reget_afilesize(0);
3518                     reget_afilesize(mainw->current_file);
3519                     if (cfile->afilesize == clipboard->afilesize) {
3520                       lives_mv(fnameto, fnamefrom);
3521                     }
3522                     close_temp_handle(current_file);
3523                     clipboard->arate = -clipboard->arate;
3524                     lives_free(fnamefrom);
3525                     lives_free(fnameto);
3526                   }
3527                 } else {
3528                   lives_rm(clipboard->info_file);
3529                   com = lives_strdup_printf("%s resample_audio \"%s\" %d %d %d %d %d %d %d %d %d %d %.4f",
3530                                             prefs->backend,
3531                                             clipboard->handle, clipboard->arps, clipboard->achans, clipboard->asampsize,
3532                                             clipboard_signed, clipboard_endian, cfile->arps, clipboard->achans,
3533                                             clipboard->asampsize, clipboard_signed, clipboard_endian, audio_stretch);
3534                   lives_system(com, FALSE);
3535                   lives_free(com);
3536 
3537                   if (THREADVAR(com_failed)) {
3538                     unbuffer_lmap_errors(FALSE);
3539                     return;
3540                   }
3541 
3542                   mainw->current_file = 0;
3543                   mainw->error = FALSE;
3544                   do_progress_dialog(TRUE, FALSE, _("Resampling clipboard audio"));
3545                   mainw->current_file = current_file;
3546                   if (mainw->error) {
3547                     d_print_failed();
3548                     unbuffer_lmap_errors(FALSE);
3549                     return;
3550                   }
3551 
3552                   // not really, but we pretend...
3553                   clipboard->arps = cfile->arps;
3554                 }
3555               }
3556             }
3557 
3558             if (clipboard->achans > 0 && (cfile->achans != clipboard->achans || cfile->arps != clipboard->arps ||
3559                                           cfile->asampsize != clipboard->asampsize || cfile_signed != clipboard_signed ||
3560                                           cfile_endian != clipboard_endian)) {
3561               lives_rm(clipboard->info_file);
3562               com = lives_strdup_printf("%s resample_audio \"%s\" %d %d %d %d %d %d %d %d %d %d",
3563                                         prefs->backend, clipboard->handle,
3564                                         clipboard->arps, clipboard->achans, clipboard->asampsize, clipboard_signed,
3565                                         clipboard_endian, cfile->arps, cfile->achans, cfile->asampsize, cfile_signed, cfile_endian);
3566               lives_system(com, FALSE);
3567               lives_free(com);
3568 
3569               if (THREADVAR(com_failed)) {
3570                 unbuffer_lmap_errors(FALSE);
3571                 return;
3572               }
3573 
3574               mainw->current_file = 0;
3575               do_progress_dialog(TRUE, FALSE, _("Resampling clipboard audio"));
3576               mainw->current_file = current_file;
3577 
3578               if (mainw->error) {
3579                 d_print_failed();
3580                 unbuffer_lmap_errors(FALSE);
3581                 return;
3582               }
3583             }
3584 
3585             if (clipboard->achans > 0 && clipboard->afilesize == 0l) {
3586               if (prefs->conserve_space) {
3587                 // oops...
3588                 if (clipboard->audio_waveform) {
3589                   for (i = 0; i < clipboard->achans; lives_freep((void **)&clipboard->audio_waveform[i++]));
3590                   lives_freep((void **)&clipboard->audio_waveform);
3591                   lives_freep((void **)&clipboard->aw_sizes);
3592                 }
3593                 clipboard->achans = clipboard->arate = clipboard->asampsize = 0;
3594                 with_sound = FALSE;
3595                 widget_opts.non_modal = TRUE;
3596                 do_error_dialog
3597                 (_("\n\nLiVES was unable to resample the clipboard audio. \nClipboard audio has been erased.\n"));
3598                 widget_opts.non_modal = FALSE;
3599               } else {
3600                 lives_rm(clipboard->info_file);
3601                 mainw->current_file = 0;
3602                 com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, clipboard->handle);
3603                 lives_system(com, FALSE);
3604                 lives_free(com);
3605                 mainw->current_file = current_file;
3606 
3607                 clipboard->arps = ocarps;
3608                 reget_afilesize(0);
3609 
3610                 if (!do_yesno_dialog
3611                     (_("\n\nLiVES was unable to resample the clipboard audio.\n"
3612                        "Do you wish to continue with the insert \nusing unchanged audio ?\n"))) {
3613                   mainw->error = TRUE;
3614                   unbuffer_lmap_errors(FALSE);
3615                   return;
3616 		// *INDENT-OFF*
3617             }}}}}}
3618 	  // *INDENT-ON*
3619 
3620       if (!virtual_ins) {
3621         char *msg = (_("Pulling frames from clipboard..."));
3622         if (realize_all_frames(0, msg, FALSE) <= 0) {
3623           lives_free(msg);
3624           sensitize();
3625           unbuffer_lmap_errors(FALSE);
3626           return;
3627         }
3628         lives_free(msg);
3629       }
3630 
3631       d_print(""); // force switchtext
3632 
3633       // if pref is set, resample clipboard video
3634       if (prefs->ins_resample && cfile->fps != clipboard->fps && orig_frames > 0) {
3635         if (!resample_clipboard(cfile->fps)) {
3636           unbuffer_lmap_errors(FALSE);
3637           return;
3638         }
3639         cb_video_change = TRUE;
3640       }
3641 
3642       if (mainw->fx1_bool && (cfile->asampsize * cfile->arate * cfile->achans != 0)) {
3643         // in theory this should not change after resampling, but we will recalculate anyway
3644 
3645         // "insert to fit audio" : number of inserts is (audio_time - video_time) / clipboard_time
3646         times_to_insert = (cfile->laudio_time - cfile->frames > 0 ? (double)cfile->frames / cfile->fps : 0.) / ((
3647                             double)clipboard->frames / clipboard->fps);
3648       }
3649 
3650       switch_clip(1, current_file, TRUE);
3651 
3652       if (cb_end > clipboard->frames) {
3653         cb_end = clipboard->frames;
3654       }
3655 
3656       if (with_sound && cfile->achans == 0) {
3657         int asigned = !(clipboard->signed_endian & AFORM_UNSIGNED);
3658         int endian = clipboard->signed_endian & AFORM_BIG_ENDIAN;
3659 
3660         cfile->achans = clipboard->achans;
3661         cfile->asampsize = clipboard->asampsize;
3662         cfile->arps = cfile->arate = clipboard->arate;
3663         cfile->signed_endian = clipboard->signed_endian;
3664 
3665         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ARATE, &cfile->arps)) bad_header = TRUE;
3666         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_ARATE, &cfile->arate)) bad_header = TRUE;
3667         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ACHANS, &cfile->achans)) bad_header = TRUE;
3668         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASIGNED, &asigned)) bad_header = TRUE;
3669         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_AENDIAN, &endian)) bad_header = TRUE;
3670         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASAMPS, &cfile->asampsize)) bad_header = TRUE;
3671 
3672         if (bad_header) do_header_write_error(mainw->current_file);
3673       }
3674 
3675       // first remainder frames
3676       remainder_frames = (int)(times_to_insert - (double)(int)times_to_insert) * clipboard->frames;
3677 
3678       end = clipboard->frames;
3679       if (virtual_ins) end = -end;
3680 
3681       if (!mainw->insert_after && remainder_frames > 0) {
3682         d_print(_("Inserting %d%s frames from the clipboard..."), remainder_frames,
3683                 times_to_insert > 1. ? " remainder" : "");
3684 
3685         com = lives_strdup_printf("%s insert \"%s\" \"%s\" %d %d %d \"%s\" %d %d %d %d %.3f %d %d %d %d %d",
3686                                   prefs->backend, cfile->handle,
3687                                   get_image_ext_for_type(cfile->img_type), where, clipboard->frames - remainder_frames + 1,
3688                                   end, clipboard->handle, with_sound, cfile->frames, hsize, vsize, cfile->fps,
3689                                   cfile->arate, cfile->achans, cfile->asampsize, !(cfile->signed_endian & AFORM_UNSIGNED),
3690                                   !(cfile->signed_endian & AFORM_BIG_ENDIAN));
3691 
3692         lives_rm(cfile->info_file);
3693         lives_system(com, FALSE);
3694         lives_free(com);
3695 
3696         if (THREADVAR(com_failed)) {
3697           d_print_failed();
3698           unbuffer_lmap_errors(FALSE);
3699           return;
3700         }
3701 
3702         cfile->progress_start = 1;
3703         cfile->progress_end = remainder_frames;
3704 
3705         mainw->disk_mon = MONITOR_QUOTA;
3706         do_progress_dialog(TRUE, FALSE, _("Inserting"));
3707         mainw->disk_mon = 0;
3708 
3709         if (mainw->error) {
3710           d_print_failed();
3711           unbuffer_lmap_errors(FALSE);
3712           check_storage_space(-1, FALSE);
3713           return;
3714         }
3715 
3716         if (cfile->clip_type == CLIP_TYPE_FILE || virtual_ins) {
3717           insert_images_in_virtual(mainw->current_file, where, remainder_frames, clipboard->frame_index,
3718                                    clipboard->frames - remainder_frames + 1);
3719         }
3720 
3721         cfile->frames += remainder_frames;
3722         where += remainder_frames;
3723 
3724         cfile->insert_end += remainder_frames;
3725 
3726         if (!mainw->insert_after) {
3727           cfile->start += remainder_frames;
3728           cfile->end += remainder_frames;
3729         }
3730 
3731         if (with_sound) {
3732           reget_afilesize(mainw->current_file);
3733         } else get_play_times();
3734         d_print_done();
3735       }
3736 
3737       // inserts of whole clipboard
3738       if ((int)times_to_insert > 1) {
3739         d_print("");
3740         d_print(_("Inserting %d times from the clipboard%s..."), (int)times_to_insert, with_sound ?
3741                 " (with sound)" : "");
3742       } else if ((int)times_to_insert > 0) {
3743         d_print("");
3744         d_print(_("Inserting %d frames from the clipboard%s..."), cb_end - cb_start + 1, with_sound ?
3745                 " (with sound)" : "");
3746       }
3747 
3748       if (virtual_ins) cb_end = -cb_end;
3749 
3750       // for an insert after a merge we set our start posn. -ve
3751       // this should indicate to the back end to leave our
3752       // backup frames alone
3753 
3754       com = lives_strdup_printf("%s insert \"%s\" \"%s\" %d %d %d \"%s\" %d %d %d %d %.3f %d %d %d %d %d %d",
3755                                 prefs->backend, cfile->handle,
3756                                 get_image_ext_for_type(cfile->img_type), where, cb_start * leave_backup, cb_end,
3757                                 clipboard->handle, with_sound, cfile->frames, hsize, vsize, cfile->fps, cfile->arate,
3758                                 cfile->achans, cfile->asampsize, !(cfile->signed_endian & AFORM_UNSIGNED),
3759                                 !(cfile->signed_endian & AFORM_BIG_ENDIAN), (int)times_to_insert);
3760 
3761       if (virtual_ins) cb_end = -cb_end;
3762 
3763       cfile->progress_start = 1;
3764       cfile->progress_end = (cb_end - cb_start + 1) * (int)times_to_insert + cfile->frames - where;
3765       lives_rm(cfile->info_file);
3766       lives_system(com, FALSE);
3767       lives_free(com);
3768 
3769       if (THREADVAR(com_failed)) {
3770         d_print_failed();
3771         unbuffer_lmap_errors(FALSE);
3772         return;
3773       }
3774 
3775       // show a progress dialog
3776       cfile->nopreview = TRUE;
3777       mainw->disk_mon = MONITOR_QUOTA;
3778       if (!do_progress_dialog(TRUE, TRUE, _("Inserting"))) {
3779         // cancelled
3780         cfile->nopreview = FALSE;
3781         mainw->disk_mon = 0;
3782 
3783         if (mainw->error) {
3784           d_print_failed();
3785           unbuffer_lmap_errors(FALSE);
3786           return;
3787         }
3788 
3789         // clean up moved/inserted frames
3790         com = lives_strdup_printf("%s undo_insert \"%s\" %d %d %d \"%s\"",
3791                                   prefs->backend, cfile->handle, where + 1,
3792                                   where + (cb_end - cb_start + 1) * (int)times_to_insert, cfile->frames,
3793                                   get_image_ext_for_type(cfile->img_type));
3794         lives_system(com, FALSE);
3795         lives_free(com);
3796 
3797         do_progress_dialog(TRUE, FALSE, _("Cancelling"));
3798 
3799         cfile->start = ostart;
3800         cfile->end = oend;
3801 
3802         if (with_sound) {
3803           // desample clipboard audio
3804           if (cb_audio_change && !prefs->conserve_space) {
3805             lives_rm(clipboard->info_file);
3806             com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, clipboard->handle);
3807             mainw->current_file = 0;
3808             lives_system(com, FALSE);
3809             lives_free(com);
3810             mainw->current_file = current_file;
3811             clipboard->arps = ocarps;
3812             reget_afilesize(0);
3813           }
3814         }
3815 
3816         if (cb_video_change) {
3817           // desample clipboard video
3818           mainw->current_file = 0;
3819           mainw->no_switch_dprint = TRUE;
3820           on_undo_activate(NULL, NULL);
3821           mainw->no_switch_dprint = FALSE;
3822           mainw->current_file = current_file;
3823         }
3824 
3825         switch_clip(1, current_file, TRUE);
3826         set_undoable(NULL, FALSE);
3827         mainw->cancelled = CANCEL_USER;
3828         unbuffer_lmap_errors(FALSE);
3829         return;
3830       }
3831       mainw->disk_mon = 0;
3832 
3833       mainw->cancelled = CANCEL_NONE;
3834       cfile->nopreview = FALSE;
3835 
3836       if (cfile->clip_type == CLIP_TYPE_FILE || virtual_ins) {
3837         insert_images_in_virtual(mainw->current_file, where, (cb_end - cb_start + 1) * (int)times_to_insert, clipboard->frame_index,
3838                                  cb_start * leave_backup);
3839       }
3840 
3841       cfile->frames += (cb_end - cb_start + 1) * (int)times_to_insert;
3842       where += (cb_end - cb_start + 1) * (int)times_to_insert;
3843       cfile->insert_end += (cb_end - cb_start + 1) * (int)times_to_insert;
3844 
3845       if (!mainw->insert_after) {
3846         cfile->start += (cb_end - cb_start + 1) * (int)times_to_insert;
3847         cfile->end += (cb_end - cb_start + 1) * (int)times_to_insert;
3848       }
3849 
3850       if (with_sound == 1) {
3851         reget_afilesize(mainw->current_file);
3852       } else get_play_times();
3853       d_print_done();
3854 
3855       // last remainder frames
3856 
3857       if (mainw->insert_after && remainder_frames > 0) {
3858         d_print(_("Inserting %d%s frames from the clipboard..."), remainder_frames,
3859                 times_to_insert > 1. ? " remainder" : "");
3860 
3861         if (virtual_ins) remainder_frames = -remainder_frames;
3862 
3863         com = lives_strdup_printf("%s insert \"%s\" \"%s\" %d %d %d \"%s\" %d %d %d %d %3f %d %d %d %d %d",
3864                                   prefs->backend, cfile->handle,
3865                                   get_image_ext_for_type(cfile->img_type), where, 1, remainder_frames, clipboard->handle,
3866                                   with_sound, cfile->frames, hsize, vsize, cfile->fps, cfile->arate, cfile->achans,
3867                                   cfile->asampsize, !(cfile->signed_endian & AFORM_UNSIGNED),
3868                                   !(cfile->signed_endian & AFORM_BIG_ENDIAN));
3869 
3870         lives_rm(cfile->info_file);
3871         lives_system(com, FALSE);
3872 
3873         if (THREADVAR(com_failed)) {
3874           unbuffer_lmap_errors(TRUE);
3875           d_print_failed();
3876           return;
3877         }
3878 
3879         if (virtual_ins) remainder_frames = -remainder_frames;
3880 
3881         cfile->progress_start = 1;
3882         cfile->progress_end = remainder_frames;
3883 
3884         mainw->disk_mon = MONITOR_QUOTA;
3885         do_progress_dialog(TRUE, FALSE, _("Inserting"));
3886         mainw->disk_mon = 0;
3887 
3888         if (mainw->error) {
3889           d_print_failed();
3890           unbuffer_lmap_errors(TRUE);
3891           return;
3892         }
3893 
3894         if (cfile->clip_type == CLIP_TYPE_FILE || virtual_ins) {
3895           insert_images_in_virtual(mainw->current_file, where, remainder_frames, clipboard->frame_index, 1);
3896         }
3897 
3898         cfile->frames += remainder_frames;
3899         cfile->insert_end += remainder_frames;
3900         lives_free(com);
3901 
3902         if (!mainw->insert_after) {
3903           cfile->start += remainder_frames;
3904           cfile->end += remainder_frames;
3905         }
3906         get_play_times();
3907 
3908         d_print_done();
3909       }
3910 
3911       // if we had deferred audio, we insert silence in selection
3912       if (insert_silence) {
3913         cfile->undo1_dbl = calc_time_from_frame(mainw->current_file, cfile->insert_start);
3914         cfile->undo2_dbl = calc_time_from_frame(mainw->current_file, cfile->insert_end + 1);
3915         cfile->undo_arate = cfile->arate;
3916         cfile->undo_signed_endian = cfile->signed_endian;
3917         cfile->undo_achans = cfile->achans;
3918         cfile->undo_asampsize = cfile->asampsize;
3919         cfile->undo_arps = cfile->arps;
3920 
3921         on_ins_silence_activate(NULL, NULL);
3922 
3923         with_sound = TRUE;
3924       }
3925 
3926       // insert done
3927 
3928       // start or end can be zero if we inserted into pure audio
3929       if (cfile->start == 0 && cfile->frames > 0) cfile->start = 1;
3930       if (cfile->end == 0) cfile->end = cfile->frames;
3931 
3932       if (cfile->frames > 0 && orig_frames == 0) {
3933         lives_snprintf(cfile->type, 40, "Frames");
3934         cfile->orig_file_name = FALSE;
3935         cfile->hsize = clipboard->hsize;
3936         cfile->vsize = clipboard->vsize;
3937         cfile->bpp = clipboard->bpp;
3938         cfile->fps = cfile->pb_fps = clipboard->fps;
3939         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_WIDTH, &cfile->hsize)) bad_header = TRUE;
3940         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_HEIGHT, &cfile->vsize)) bad_header = TRUE;
3941         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_BPP, &cfile->bpp)) bad_header = TRUE;
3942         if (cfile->clip_type == CLIP_TYPE_FILE && cfile->ext_src) {
3943           lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
3944           double dfps = (double)cdata->fps;
3945           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &dfps)) bad_header = TRUE;
3946         } else {
3947           if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &cfile->fps)) bad_header = TRUE;
3948         }
3949         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_FPS, &cfile->fps)) bad_header = TRUE;
3950 
3951         if (bad_header) do_header_write_error(mainw->current_file);
3952       }
3953 
3954       lives_signal_handler_block(mainw->spinbutton_end, mainw->spin_end_func);
3955       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->frames == 0 ? 0 : 1, cfile->frames);
3956       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->end);
3957       lives_signal_handler_unblock(mainw->spinbutton_end, mainw->spin_end_func);
3958 
3959       lives_signal_handler_block(mainw->spinbutton_start, mainw->spin_start_func);
3960       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->frames == 0 ? 0 : 1, cfile->frames);
3961       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->start);
3962       lives_signal_handler_unblock(mainw->spinbutton_start, mainw->spin_start_func);
3963 
3964       set_undoable(_("Insert"), TRUE);
3965       cfile->undo1_boolean = with_sound;
3966       lives_widget_set_sensitive(mainw->select_new, TRUE);
3967 
3968       // mark new file size as 'Unknown'
3969       cfile->f_size = 0l;
3970       cfile->changed = TRUE;
3971 
3972       if (with_sound) {
3973         cfile->undo_action = UNDO_INSERT_WITH_AUDIO;
3974         if (cb_audio_change && !prefs->conserve_space && clipboard->achans > 0) {
3975           lives_rm(clipboard->info_file);
3976           mainw->current_file = 0;
3977           com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, clipboard->handle);
3978           lives_system(com, FALSE);
3979           lives_free(com);
3980           mainw->current_file = current_file;
3981           clipboard->arps = ocarps;
3982           reget_afilesize(0);
3983         }
3984       } else cfile->undo_action = UNDO_INSERT;
3985 
3986       if (cb_video_change) {
3987         mainw->current_file = 0;
3988         mainw->no_switch_dprint = TRUE;
3989         on_undo_activate(NULL, NULL);
3990         mainw->no_switch_dprint = FALSE;
3991         mainw->current_file = current_file;
3992       }
3993 
3994       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FRAMES, &cfile->frames)) bad_header = TRUE;
3995 
3996       if (bad_header) do_header_write_error(mainw->current_file);
3997       switch_clip(1, current_file, TRUE);
3998       mainw->error = FALSE;
3999 
4000       check_storage_space(-1, FALSE);
4001 
4002       if (mainw->sl_undo_mem && (cfile->stored_layout_frame != 0 || (with_sound && cfile->stored_layout_audio != 0.))) {
4003         // need to invalidate undo/redo stack, in case file was used in some layout undo
4004         stored_event_list_free_undos();
4005       }
4006 
4007       if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
4008     }
4009 
4010     /**
4011        @brief check for layout errors, using in_mask as a guide
4012        (mask values are taken from prefs->warn_mask, but with opposite sense)
4013 
4014        This function should ALWAYS be called before any operations are performed which may do any of the following:
4015        - delete frames, shift frames, alter frames (e.g. insert, delete, resample, apply rendered effects).
4016        - delete audio, shift audio, alter audio (e.g. insert / delete with audio, resample, adjust the volume)
4017        - (deletion includes closing the clip)
4018 
4019        - some operations are exempt (i.e resizing frames, converting the frame / sample format, appending after the end, temporary
4020        changes which affect only playback such as applying real time effects, letterboxing, altering the playback rate)
4021 
4022        -- changing clip audio volume is somewhat undefined as this is a new feature and can be both a playback change and / or
4023        permanent change. However fade in / out and insert silence are more permanent and should call this function.
4024 
4025        - the order of priority for both frames and audio is always: delete > shift > alter
4026          the default settings are to warn on delete / shift and not to warn on alter; however the user preferences may override this
4027 
4028        start and end represent frame values for the affected region.
4029        For audio, the values should approximate the start and end points,
4030        (though normally they would be the correct points) and end may extend beyond the actual frame count.
4031 
4032        After checking, in_mask is set (reduced) to to any 'trangressions' found - this will be the intersection (AND) of the check_mask,
4033        AND detected transgressions AND the conjunction of non-disabled warnings.
4034 
4035        if the resulting set is non-empty, then prior to returning, a warning dialog is displayed.
4036        'operation' is a descriptive word / phrase for what the operation intends to be doing, e.g "deletion", "cutting", "pasting" which
4037        used in the dialog message.
4038 
4039        if the user cancels, FALSE is returned. and the caller should abort whatever operation.
4040 
4041        if the user chooses to continue, warnings are added to the layout errors buffer am TRUE is returned.
4042        (within the buffer, errors of the same type are collated and organised by priority,
4043        so there is no harm in calling this function multiple times).
4044 
4045        If no warnings are shown (either because there were no conflicts, or because the user disabled the warnings) then TRUE is also
4046        returned, and in this case chk_mask will be 0.
4047 
4048        After return from this function, if the return value is TRUE the operation may proceed, but the following must be observed:
4049 
4050        - If the operation fails or is cancelled. any buffered warnings should be cleared by calling:  unbuffer_lmap_errors(FALSE);
4051        (this function may always be called, even if chk_mask was returned as 0)
4052 
4053        Otherwise, after the operation completes something like the following must be done:
4054 
4055        if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
4056 
4057        this latter function must be called if and only if chk_mask was returned with a non-zero value.
4058     */
4059     boolean check_for_layout_errors(const char *operation, int fileno, int start, int end, uint32_t *in_mask) {
4060       lives_clip_t *sfile;
4061       LiVESList *xlays = NULL;
4062       uint32_t ret_mask = 0, mask = *in_mask;
4063       boolean cancelled = FALSE;
4064 
4065       if (!IS_VALID_CLIP(fileno)) return 0;
4066       sfile = mainw->files[fileno];
4067       if (start < 1) start = 1;
4068 
4069       if (mask & WARN_MASK_LAYOUT_DELETE_FRAMES) {
4070         if ((xlays = layout_frame_is_affected(fileno, start, end, NULL)) != NULL) {
4071           if (sfile->tcache_dubious_from > 0) free_thumb_cache(fileno, sfile->tcache_dubious_from);
4072           sfile->tcache_dubious_from = start;
4073           ret_mask |= WARN_MASK_LAYOUT_DELETE_FRAMES | (mask & WARN_MASK_LAYOUT_SHIFT_FRAMES)
4074                       | (mask & WARN_MASK_LAYOUT_ALTER_FRAMES);
4075           if ((prefs->warning_mask & WARN_MASK_LAYOUT_DELETE_FRAMES) == 0) {
4076             mainw->xlays = xlays;
4077             if (!do_warning_dialogf
4078                 (_("%s will cause missing frames in some multitrack layouts.\nAre you sure you wish to continue ?\n"), operation)) {
4079               cancelled = TRUE;
4080             }
4081           }
4082 
4083           if (!cancelled) {
4084             buffer_lmap_error(LMAP_ERROR_DELETE_FRAMES, sfile->name, (livespointer)sfile->layout_map, fileno,
4085                               start, 0., count_resampled_frames(sfile->stored_layout_frame,
4086                                   sfile->stored_layout_fps, sfile->fps) >= start);
4087           }
4088           lives_list_free_all(&xlays);
4089         }
4090       }
4091 
4092       if (mask & WARN_MASK_LAYOUT_DELETE_AUDIO) {
4093         if ((xlays = layout_audio_is_affected(fileno, (start - 1.) / sfile->fps, (end - 1.) / sfile->fps, NULL)) != NULL) {
4094           ret_mask |= WARN_MASK_LAYOUT_DELETE_AUDIO | (mask & WARN_MASK_LAYOUT_SHIFT_AUDIO)
4095                       | (mask & WARN_MASK_LAYOUT_ALTER_AUDIO);
4096           if (!cancelled) {
4097             if ((prefs->warning_mask & WARN_MASK_LAYOUT_DELETE_AUDIO) == 0) {
4098               mainw->xlays = xlays;
4099               if (!do_warning_dialogf
4100                   (_("%s will cause missing audio in some multitrack layouts.\nAre you sure you wish to continue ?\n"), operation)) {
4101                 cancelled = TRUE;
4102               }
4103             }
4104             if (!cancelled) {
4105               buffer_lmap_error(LMAP_ERROR_DELETE_AUDIO, sfile->name, (livespointer)sfile->layout_map, fileno, 0,
4106                                 (start - 1.) / sfile->fps, (start - 1.) / sfile->fps < sfile->stored_layout_audio);
4107             }
4108           }
4109         }
4110         lives_list_free_all(&xlays);
4111       }
4112 
4113       if ((ret_mask & WARN_MASK_LAYOUT_DELETE_FRAMES) == 0) {
4114         if (mask & WARN_MASK_LAYOUT_SHIFT_FRAMES) {
4115           if ((xlays = layout_frame_is_affected(fileno, start, 0, NULL)) != NULL) {
4116             if (sfile->tcache_dubious_from > 0) free_thumb_cache(fileno, sfile->tcache_dubious_from);
4117             sfile->tcache_dubious_from = start;
4118             ret_mask |= WARN_MASK_LAYOUT_SHIFT_FRAMES | (mask & WARN_MASK_LAYOUT_ALTER_FRAMES);
4119             if ((prefs->warning_mask & WARN_MASK_LAYOUT_SHIFT_FRAMES) == 0) {
4120               mainw->xlays = xlays;
4121               if (!do_warning_dialogf
4122                   (_("%s will cause frames to shift in some multitrack layouts.\nAre you sure you wish to continue ?\n"),
4123                    operation)) {
4124                 cancelled = TRUE;
4125               }
4126             }
4127             if (!cancelled) {
4128               buffer_lmap_error(LMAP_ERROR_SHIFT_FRAMES, sfile->name, (livespointer)sfile->layout_map, fileno,
4129                                 start, 0., start <= count_resampled_frames(sfile->stored_layout_frame,
4130                                     sfile->stored_layout_fps, sfile->fps));
4131             }
4132           }
4133           lives_list_free_all(&xlays);
4134         }
4135       }
4136 
4137       if ((ret_mask & WARN_MASK_LAYOUT_DELETE_AUDIO) == 0) {
4138         if (mask & WARN_MASK_LAYOUT_SHIFT_AUDIO) {
4139           if ((xlays = layout_audio_is_affected(fileno, (start - 1.) / sfile->fps, 0., NULL)) != NULL) {
4140             ret_mask |= WARN_MASK_LAYOUT_SHIFT_AUDIO | (mask & WARN_MASK_LAYOUT_ALTER_AUDIO);
4141             if (!cancelled) {
4142               if ((prefs->warning_mask & WARN_MASK_LAYOUT_SHIFT_AUDIO)) {
4143                 mainw->xlays = xlays;
4144                 if (!do_warning_dialogf
4145                     (_("%s will cause audio to shift in some multitrack layouts.\nAre you sure you wish to continue ?\n"),
4146                      operation)) {
4147                   cancelled = TRUE;
4148                 }
4149               }
4150               if (!cancelled) {
4151                 buffer_lmap_error(LMAP_ERROR_SHIFT_AUDIO, sfile->name, (livespointer)sfile->layout_map, fileno, 0,
4152                                   (start - 1.) / sfile->fps, (start - 1.) / sfile->fps <= sfile->stored_layout_audio);
4153               }
4154             }
4155             lives_list_free_all(&xlays);
4156           }
4157         }
4158       }
4159 
4160       if ((ret_mask & (WARN_MASK_LAYOUT_DELETE_FRAMES | WARN_MASK_LAYOUT_SHIFT_FRAMES)) == 0) {
4161         if (mask & WARN_MASK_LAYOUT_ALTER_FRAMES) {
4162           if ((xlays = layout_frame_is_affected(fileno, start, end, NULL)) != NULL) {
4163             if (sfile->tcache_dubious_from > 0) free_thumb_cache(fileno, sfile->tcache_dubious_from);
4164             sfile->tcache_dubious_from = start;
4165             ret_mask |= WARN_MASK_LAYOUT_ALTER_FRAMES;
4166             if ((prefs->warning_mask & WARN_MASK_LAYOUT_ALTER_FRAMES) == 0) {
4167               mainw->xlays = xlays;
4168               if (!do_layout_alter_frames_warning()) {
4169                 cancelled = TRUE;
4170               }
4171             }
4172             if (!cancelled) {
4173               buffer_lmap_error(LMAP_ERROR_ALTER_FRAMES, sfile->name, (livespointer)sfile->layout_map, fileno, 0, 0.,
4174                                 sfile->stored_layout_frame > 0);
4175             }
4176             lives_list_free_all(&xlays);
4177           }
4178         }
4179       }
4180 
4181       if ((ret_mask & (WARN_MASK_LAYOUT_DELETE_AUDIO | WARN_MASK_LAYOUT_SHIFT_AUDIO)) == 0) {
4182         if (mask & WARN_MASK_LAYOUT_ALTER_AUDIO) {
4183           if ((xlays = layout_audio_is_affected(fileno, 0., 0., NULL)) != NULL) {
4184             ret_mask |= WARN_MASK_LAYOUT_ALTER_AUDIO;
4185             if (!cancelled) {
4186               if ((prefs->warning_mask & WARN_MASK_LAYOUT_ALTER_AUDIO) == 0) {
4187                 mainw->xlays = xlays;
4188                 if (!do_layout_alter_audio_warning()) {
4189                   cancelled = TRUE;
4190                 }
4191               }
4192             }
4193             if (!cancelled) {
4194               buffer_lmap_error(LMAP_ERROR_ALTER_AUDIO, sfile->name, (livespointer)sfile->layout_map, fileno, 0, 0.,
4195                                 sfile->stored_layout_audio > 0.);
4196             }
4197             lives_list_free_all(&xlays);
4198           }
4199         }
4200       }
4201 
4202       mainw->xlays = NULL;
4203       *in_mask = ret_mask;
4204       return !cancelled;
4205     }
4206 
4207 
4208     void on_delete_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4209       char *com;
4210 
4211       boolean bad_header = FALSE;
4212 
4213       uint32_t chk_mask = 0;
4214 
4215       int frames_cut = cfile->end - cfile->start + 1;
4216       int start = cfile->start;
4217       int end = cfile->end;
4218 
4219       // occasionally we get a keyboard misread, so this should prevent that
4220       if (LIVES_IS_PLAYING) return;
4221 
4222       if (cfile->start <= 1 && cfile->end == cfile->frames) {
4223         if (!mainw->osc_auto && menuitem != LIVES_MENU_ITEM(mainw->cut) && (cfile->achans == 0 ||
4224             ((cfile->end - 1.) / cfile->fps >= cfile->laudio_time &&
4225              mainw->ccpd_with_sound))) {
4226           if (do_warning_dialog
4227               (_("\nDeleting all frames will close this file.\nAre you sure ?"))) close_current_file(0);
4228           return;
4229         }
4230       }
4231 
4232       if (menuitem) {
4233         char *tmp = (_("Deletion"));
4234         chk_mask = WARN_MASK_LAYOUT_DELETE_FRAMES | WARN_MASK_LAYOUT_SHIFT_FRAMES | WARN_MASK_LAYOUT_ALTER_FRAMES;
4235         if (mainw->ccpd_with_sound) chk_mask |= WARN_MASK_LAYOUT_DELETE_AUDIO | WARN_MASK_LAYOUT_SHIFT_AUDIO |
4236                                                   WARN_MASK_LAYOUT_ALTER_AUDIO;
4237         if (!check_for_layout_errors(tmp, mainw->current_file, cfile->start, cfile->end, &chk_mask)) {
4238           lives_free(tmp);
4239           return;
4240         }
4241         lives_free(tmp);
4242       }
4243 
4244       if (cfile->start <= 1 && cfile->end == cfile->frames) {
4245         cfile->ohsize = cfile->hsize;
4246         cfile->ovsize = cfile->vsize;
4247       }
4248 
4249       cfile->undo_start = cfile->start;
4250       cfile->undo_end = cfile->end;
4251       cfile->undo1_boolean = mainw->ccpd_with_sound;
4252 
4253       if (menuitem || mainw->osc_auto) {
4254         d_print(""); // force switchtext
4255         d_print(_("Deleting frames %d to %d%s..."), cfile->start, cfile->end,
4256                 mainw->ccpd_with_sound && cfile->achans > 0 ? " (with sound)" : "");
4257       }
4258 
4259       com = lives_strdup_printf("%s cut \"%s\" %d %d %d %d \"%s\" %.3f %d %d %d",
4260                                 prefs->backend, cfile->handle, cfile->start, cfile->end,
4261                                 mainw->ccpd_with_sound, cfile->frames, get_image_ext_for_type(cfile->img_type),
4262                                 cfile->fps, cfile->arate, cfile->achans, cfile->asampsize);
4263       lives_rm(cfile->info_file);
4264       lives_system(com, FALSE);
4265       lives_free(com);
4266 
4267       if (THREADVAR(com_failed)) {
4268         unbuffer_lmap_errors(FALSE);
4269         d_print_failed();
4270         return;
4271       }
4272 
4273       cfile->progress_start = cfile->start;
4274       cfile->progress_end = cfile->frames;
4275 
4276       // show a progress dialog, not cancellable
4277       do_progress_dialog(TRUE, FALSE, _("Deleting"));
4278 
4279       if (cfile->clip_type == CLIP_TYPE_FILE) {
4280         delete_frames_from_virtual(mainw->current_file, cfile->start, cfile->end);
4281       }
4282 
4283       cfile->frames -= frames_cut;
4284 
4285       cfile->undo_arate = cfile->arate;
4286       cfile->undo_signed_endian = cfile->signed_endian;
4287       cfile->undo_achans = cfile->achans;
4288       cfile->undo_asampsize = cfile->asampsize;
4289       cfile->undo_arps = cfile->arps;
4290 
4291       if (mainw->ccpd_with_sound) {
4292         reget_afilesize(mainw->current_file);
4293       } else get_play_times();
4294 
4295       if (cfile->frames == 0) {
4296         if (cfile->afilesize == 0l) {
4297           close_current_file(0);
4298           return;
4299         }
4300         lives_snprintf(cfile->type, 40, "Audio");
4301         cfile->hsize = cfile->vsize = 0;
4302         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_WIDTH, &cfile->hsize)) bad_header = TRUE;
4303         if (!save_clip_value(mainw->current_file, CLIP_DETAILS_HEIGHT, &cfile->vsize)) bad_header = TRUE;
4304 
4305         if (bad_header) do_header_write_error(mainw->current_file);
4306         cfile->orig_file_name = FALSE;
4307         desensitize();
4308         sensitize();
4309       }
4310 
4311       if (!mainw->selwidth_locked || cfile->start > cfile->frames) {
4312         if (--start == 0 && cfile->frames > 0) {
4313           start = 1;
4314         }
4315       }
4316 
4317       cfile->start = start;
4318 
4319       if (!mainw->selwidth_locked) {
4320         cfile->end = start;
4321       } else {
4322         cfile->end = end;
4323         if (cfile->end > cfile->frames) {
4324           cfile->end = cfile->frames;
4325         }
4326       }
4327 
4328       lives_signal_handler_block(mainw->spinbutton_end, mainw->spin_end_func);
4329       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->frames == 0 ? 0 : 1, cfile->frames);
4330       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->end);
4331       lives_signal_handler_unblock(mainw->spinbutton_end, mainw->spin_end_func);
4332 
4333       lives_signal_handler_block(mainw->spinbutton_start, mainw->spin_start_func);
4334       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->frames == 0 ? 0 : 1, cfile->frames);
4335       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->start);
4336       lives_signal_handler_unblock(mainw->spinbutton_start, mainw->spin_start_func);
4337 
4338       // menuitem is NULL if we came here from undo_insert
4339       if (!menuitem && !mainw->osc_auto) return;
4340 
4341       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FRAMES, &cfile->frames)) bad_header = TRUE;
4342 
4343       showclipimgs();
4344 
4345       get_play_times();
4346 
4347       if (bad_header) do_header_write_error(mainw->current_file);
4348 
4349       // mark new file size as 'Unknown'
4350       cfile->f_size = 0l;
4351       cfile->changed = TRUE;
4352 
4353       set_undoable(_("Delete"), TRUE);
4354       cfile->undo_action = UNDO_DELETE;
4355       d_print_done();
4356 
4357       if (mainw->sl_undo_mem && (cfile->stored_layout_frame != 0 || (mainw->ccpd_with_sound &&
4358                                  cfile->stored_layout_audio != 0.))) {
4359         // need to invalidate undo/redo stack, in case file was used in some layout undo
4360         stored_event_list_free_undos();
4361       }
4362 
4363       if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
4364     }
4365 
4366 
4367     void on_select_all_activate(LiVESWidget * widget, livespointer user_data) {
4368       if (!CURRENT_CLIP_IS_VALID) return;
4369 
4370       if (mainw->selwidth_locked) {
4371         widget_opts.non_modal = TRUE;
4372         if (widget) do_error_dialog(_("\n\nSelection is locked.\n"));
4373         widget_opts.non_modal = FALSE;
4374         return;
4375       }
4376 
4377       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), 1);
4378       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->frames);
4379 
4380       cfile->start = cfile->frames > 0 ? 1 : 0;
4381       cfile->end = cfile->frames;
4382 
4383       get_play_times();
4384 
4385       showclipimgs();
4386     }
4387 
4388 
4389     void on_select_start_only_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4390       if (mainw->current_file == -1) return;
4391       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->start);
4392     }
4393 
4394 
4395     void on_select_end_only_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4396       if (mainw->current_file == -1) return;
4397       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->end);
4398     }
4399 
4400 
4401     void on_select_invert_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4402       if (cfile->start == 1) {
4403         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->end + 1);
4404         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->frames);
4405       } else {
4406         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->start - 1);
4407         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), 1);
4408       }
4409 
4410       get_play_times();
4411 
4412       showclipimgs();
4413     }
4414 
4415 
4416     void on_select_last_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4417       if (cfile->undo_start > cfile->frames) cfile->undo_start = cfile->frames;
4418       if (cfile->undo_end > cfile->frames) cfile->undo_end = cfile->frames;
4419 
4420       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->undo_start);
4421       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->undo_end);
4422 
4423       cfile->start = cfile->undo_start;
4424       cfile->end = cfile->undo_end;
4425 
4426       get_play_times();
4427 
4428       showclipimgs();
4429     }
4430 
4431 
4432     void on_select_new_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4433       if (cfile->insert_start > cfile->frames) cfile->insert_start = cfile->frames;
4434       if (cfile->insert_end > cfile->frames) cfile->insert_end = cfile->frames;
4435 
4436       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->insert_start);
4437       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->insert_end);
4438 
4439       cfile->start = cfile->insert_start;
4440       cfile->end = cfile->insert_end;
4441 
4442       get_play_times();
4443 
4444       showclipimgs();
4445     }
4446 
4447 
4448     void on_select_to_end_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4449       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->frames);
4450       cfile->end = cfile->frames;
4451       get_play_times();
4452       load_end_image(cfile->end);
4453     }
4454 
4455 
4456     void on_select_to_aend_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4457       int end = calc_frame_from_time4(mainw->current_file, cfile->laudio_time);
4458       if (end > cfile->frames) end = cfile->frames;
4459       if (end < cfile->start) end = cfile->start;
4460       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), end);
4461       cfile->end = end;
4462       get_play_times();
4463       load_end_image(cfile->end);
4464     }
4465 
4466 
4467     void on_select_from_start_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4468       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), 1);
4469       cfile->start = cfile->frames > 0 ? 1 : 0;
4470       get_play_times();
4471       load_start_image(cfile->start);
4472     }
4473 
4474 
4475     void on_lock_selwidth_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4476       mainw->selwidth_locked = !mainw->selwidth_locked;
4477       lives_widget_set_sensitive(mainw->select_submenu, !mainw->selwidth_locked);
4478       update_sel_menu();
4479     }
4480 
4481 
4482     void play_all(boolean from_menu) {
4483       if (!CURRENT_CLIP_IS_VALID || CURRENT_CLIP_IS_CLIPBOARD) return;
4484 
4485       if (mainw->multitrack) {
4486         if (!LIVES_IS_PLAYING) {
4487           if (!mainw->multitrack->playing_sel) multitrack_playall(mainw->multitrack);
4488           else multitrack_play_sel(NULL, mainw->multitrack);
4489         } else on_pause_clicked();
4490         return;
4491       }
4492 
4493       if (!LIVES_IS_PLAYING) {
4494         if (mainw->proc_ptr && from_menu) {
4495           on_preview_clicked(LIVES_BUTTON(mainw->proc_ptr->preview_button), NULL);
4496           return;
4497         }
4498 
4499         if (!mainw->osc_auto) {
4500           if (cfile->frames > 0) {
4501             mainw->play_start = calc_frame_from_time(mainw->current_file,
4502                                 cfile->pointer_time);
4503           } else {
4504             mainw->play_start = calc_frame_from_time4(mainw->current_file,
4505                                 cfile->pointer_time);  // real_pointer_time ???
4506           }
4507           mainw->play_end = cfile->frames;
4508         }
4509 
4510         mainw->playing_sel = FALSE;
4511         if (CURRENT_CLIP_IS_NORMAL) lives_rm(cfile->info_file);
4512 
4513         play_file();
4514 
4515         /* if (CURRENT_CLIP_IS_VALID) { */
4516         /*   if (1 || !cfile->play_paused) { */
4517         /*     //cfile->pointer_time = (cfile->last_frameno - 1.) / cfile->fps; */
4518         /*     lives_ce_update_timeline(0, cfile->real_pointer_time); */
4519         /*   } else { */
4520         /* 	lives_ce_update_timeline(cfile->frameno, 0); */
4521         /*     //cfile->pointer_time = (cfile->last_frameno - 1.) / cfile->fps; */
4522         /*     //cfile->play_paused = TRUE; */
4523         /*     mainw->cancelled = CANCEL_USER; */
4524         /*   } */
4525         //}
4526       }
4527     }
4528 
4529 
4530     void on_playall_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4531       if (menuitem && mainw->go_away) return;
4532       start_playback(menuitem ? 8 : 0);
4533     }
4534 
4535 
4536     void play_sel(void) {
4537       if (!mainw->is_rendering) {
4538         mainw->play_start = cfile->start;
4539         mainw->play_end = cfile->end;
4540         mainw->clip_switched = FALSE;
4541       }
4542 
4543       if (!mainw->preview) {
4544         int orig_play_frame = calc_frame_from_time(mainw->current_file, cfile->pointer_time);
4545         if (orig_play_frame > mainw->play_start && orig_play_frame < mainw->play_end) {
4546           mainw->play_start = orig_play_frame;
4547         }
4548       }
4549 
4550       mainw->playing_sel = TRUE;
4551 
4552       play_file();
4553 
4554       mainw->playing_sel = FALSE;
4555       lives_ce_update_timeline(0, cfile->real_pointer_time);
4556 
4557       // in case we are rendering and previewing, in case we now have audio
4558       if (mainw->preview && mainw->is_rendering && mainw->is_processing) reget_afilesize(mainw->current_file);
4559       if (mainw->cancelled == CANCEL_AUDIO_ERROR) {
4560         handle_audio_timeout();
4561         mainw->cancelled = CANCEL_ERROR;
4562       }
4563     }
4564 
4565 
4566     void on_playsel_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4567       // play part of a clip (in clip editor)
4568       if (!CURRENT_CLIP_IS_VALID || CURRENT_CLIP_IS_CLIPBOARD) return;
4569 
4570       if (mainw->proc_ptr && menuitem) {
4571         on_preview_clicked(LIVES_BUTTON(mainw->proc_ptr->preview_button), NULL);
4572         return;
4573       }
4574       if (LIVES_POINTER_TO_INT(user_data)) play_file();
4575       else start_playback(1);
4576     }
4577 
4578 
4579     void on_playclip_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4580       // play the clipboard
4581       int current_file;
4582       if (mainw->multitrack) return;
4583 
4584       current_file = mainw->pre_play_file = mainw->current_file;
4585       mainw-> oloop = mainw->loop;
4586       mainw->oloop_cont = mainw->loop_cont;
4587 
4588       // switch to the clipboard
4589       switch_to_file(current_file, 0);
4590       lives_widget_set_sensitive(mainw->loop_video, FALSE);
4591       lives_widget_set_sensitive(mainw->loop_continue, FALSE);
4592       mainw->loop = mainw->loop_cont = FALSE;
4593 
4594       mainw->play_start = 1;
4595       mainw->play_end = clipboard->frames;
4596       mainw->playing_sel = FALSE;
4597       mainw->loop = FALSE;
4598 
4599       lives_rm(cfile->info_file);
4600 
4601       start_playback(5);
4602     }
4603 
4604 
4605     void on_record_perf_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4606       // real time recording
4607 
4608       if (mainw->multitrack) return;
4609 
4610       if (LIVES_IS_PLAYING) {
4611         // we are playing a clip
4612         if (!mainw->record || mainw->record_paused) {
4613           // recording is starting
4614           mainw->record_starting = TRUE;
4615 
4616           toggle_record();
4617 
4618           if ((prefs->rec_opts & REC_AUDIO) && (mainw->agen_key != 0 || mainw->agen_needs_reinit
4619                                                 || prefs->audio_src == AUDIO_SRC_EXT) &&
4620               (prefs->audio_player == AUD_PLAYER_JACK || prefs->audio_player == AUD_PLAYER_PULSE)) {
4621             if (mainw->ascrap_file == -1) {
4622               open_ascrap_file();
4623             }
4624             if (mainw->ascrap_file != -1) {
4625               mainw->rec_samples = -1; // record unlimited
4626               mainw->rec_aclip = mainw->ascrap_file;
4627               mainw->rec_avel = 1.;
4628               mainw->rec_aseek = (double)mainw->files[mainw->ascrap_file]->aseek_pos /
4629                                  (double)(mainw->files[mainw->ascrap_file]->arps * mainw->files[mainw->ascrap_file]->achans *
4630                                           mainw->files[mainw->ascrap_file]->asampsize >> 3);
4631 
4632 #ifdef ENABLE_JACK
4633               if (prefs->audio_player == AUD_PLAYER_JACK) {
4634                 char *lives_header = lives_build_filename(prefs->workdir, mainw->files[mainw->ascrap_file]->handle,
4635                                      LIVES_CLIP_HEADER, NULL);
4636                 mainw->clip_header = fopen(lives_header, "w"); // speed up clip header writes
4637                 lives_free(lives_header);
4638 
4639                 if (mainw->agen_key == 0 && !mainw->agen_needs_reinit) {
4640                   jack_rec_audio_to_clip(mainw->ascrap_file, -1, RECA_EXTERNAL);
4641                   mainw->jackd_read->is_paused = FALSE;
4642                   mainw->jackd_read->in_use = TRUE;
4643                 } else {
4644                   if (mainw->jackd) {
4645                     jack_rec_audio_to_clip(mainw->ascrap_file, -1, RECA_GENERATED);
4646                   }
4647                 }
4648                 if (mainw->clip_header) fclose(mainw->clip_header);
4649                 mainw->clip_header = NULL;
4650               }
4651 
4652 #endif
4653 #ifdef HAVE_PULSE_AUDIO
4654               if (prefs->audio_player == AUD_PLAYER_PULSE) {
4655                 char *lives_header = lives_build_filename(prefs->workdir, mainw->files[mainw->ascrap_file]->handle,
4656                                      LIVES_CLIP_HEADER, NULL);
4657                 mainw->clip_header = fopen(lives_header, "w"); // speed up clip header writes
4658                 lives_free(lives_header);
4659 
4660                 if (mainw->agen_key == 0 && !mainw->agen_needs_reinit) {
4661                   pulse_rec_audio_to_clip(mainw->ascrap_file, -1, RECA_EXTERNAL);
4662                   mainw->pulsed_read->is_paused = FALSE;
4663                   mainw->pulsed_read->in_use = TRUE;
4664                 } else {
4665                   if (mainw->pulsed) {
4666                     pulse_rec_audio_to_clip(mainw->ascrap_file, -1, RECA_GENERATED);
4667                   }
4668                 }
4669                 if (mainw->clip_header) fclose(mainw->clip_header);
4670                 mainw->clip_header = NULL;
4671               }
4672 #endif
4673             }
4674             return;
4675           }
4676 
4677           if (prefs->rec_opts & REC_AUDIO) {
4678             // recording INTERNAL audio
4679 #ifdef ENABLE_JACK
4680             if (prefs->audio_player == AUD_PLAYER_JACK && mainw->jackd) {
4681               jack_get_rec_avals(mainw->jackd);
4682             }
4683 #endif
4684 #ifdef HAVE_PULSE_AUDIO
4685             if (prefs->audio_player == AUD_PLAYER_PULSE && mainw->pulsed) {
4686               pulse_get_rec_avals(mainw->pulsed);
4687             }
4688 #endif
4689           }
4690           return;
4691         }
4692 
4693         // end record during playback
4694         event_list_add_end_events(mainw->event_list, FALSE);
4695         mainw->record_paused = TRUE; // pause recording of further events
4696         enable_record();
4697         return;
4698       }
4699 
4700       // out of playback
4701 
4702       // record performance
4703       if (!mainw->record) {
4704         // TODO - change message depending on rec_opts
4705         d_print(_("Ready to record. Use 'control' and cursor keys during playback to record your performance.\n"
4706                   "(To cancel, press 'r' or click on Play|Record Performance again before you play.)\n"));
4707         mainw->record = TRUE;
4708         toggle_record();
4709         get_play_times();
4710       } else {
4711         d_print(_("Record cancelled.\n"));
4712         enable_record();
4713         mainw->record = FALSE;
4714       }
4715     }
4716 
4717 
4718     boolean record_toggle_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
4719                                    livespointer user_data) {
4720       // from osc
4721       boolean start = (boolean)LIVES_POINTER_TO_INT(user_data);
4722 
4723       if ((start && (!mainw->record || mainw->record_paused)) || (!start && (mainw->record && !mainw->record_paused)))
4724         on_record_perf_activate(NULL, NULL);
4725 
4726       return TRUE;
4727     }
4728 
4729 
4730     void on_rewind_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4731       if (LIVES_IS_PLAYING) return;
4732 
4733       if (mainw->multitrack) {
4734         mt_tl_move(mainw->multitrack, 0.);
4735         return;
4736       }
4737 
4738       cfile->pointer_time = lives_ce_update_timeline(0, 0.);
4739       lives_widget_queue_draw_if_visible(mainw->hruler);
4740       mainw->ptrtime = cfile->pointer_time;
4741       lives_widget_queue_draw(mainw->eventbox2);
4742       lives_widget_set_sensitive(mainw->rewind, FALSE);
4743       lives_widget_set_sensitive(mainw->m_rewindbutton, FALSE);
4744       lives_widget_set_sensitive(mainw->trim_to_pstart, FALSE);
4745     }
4746 
4747 
4748     void on_stop_activate(LiVESMenuItem * menuitem, livespointer user_data) {
4749       // stop during playback
4750 
4751       if (mainw->multitrack && mainw->multitrack->is_paused && !LIVES_IS_PLAYING) {
4752         mainw->multitrack->is_paused = FALSE;
4753         mainw->multitrack->playing_sel = FALSE;
4754         mt_tl_move(mainw->multitrack, mainw->multitrack->pb_unpaused_start_time);
4755         lives_widget_set_sensitive(mainw->stop, FALSE);
4756         lives_widget_set_sensitive(mainw->m_stopbutton, FALSE);
4757         return;
4758       }
4759 
4760       mainw->cancelled = CANCEL_USER;
4761     }
4762 
4763 
4764     boolean on_stop_activate_by_del(LiVESWidget * widget, LiVESXEventDelete * event, livespointer user_data) {
4765       // called if the user closes the separate play window
4766       if (prefs->sepwin_type == SEPWIN_TYPE_STICKY) {
4767         on_sepwin_pressed(NULL, NULL);
4768       }
4769       return TRUE;
4770     }
4771 
4772 
4773     void on_pause_clicked(void) {
4774       mainw->jack_can_stop = FALSE;
4775       mainw->cancelled = CANCEL_USER_PAUSED;
4776     }
4777 
4778 
4779     void on_encoder_entry_changed(LiVESCombo * combo, livespointer ptr) {
4780       LiVESList *encoder_capabilities = NULL;
4781       LiVESList *ofmt_all = NULL;
4782       LiVESList *ofmt = NULL;
4783 
4784       const char *new_encoder_name = lives_combo_get_active_text(combo);
4785       char *msg;
4786       char **array;
4787       int i;
4788       render_details *rdet = (render_details *)ptr;
4789       LiVESList *dummy_list;
4790 
4791       if (!strlen(new_encoder_name)) return;
4792 
4793       if (!strcmp(new_encoder_name, mainw->string_constants[LIVES_STRING_CONSTANT_ANY])) {
4794         LiVESList *ofmt = NULL;
4795         ofmt = lives_list_append(ofmt, lives_strdup(mainw->string_constants[LIVES_STRING_CONSTANT_ANY]));
4796 
4797         lives_signal_handler_block(rdet->encoder_combo, rdet->encoder_name_fn);
4798         // ---
4799         lives_combo_set_active_string(LIVES_COMBO(rdet->encoder_combo), mainw->string_constants[LIVES_STRING_CONSTANT_ANY]);
4800         // ---
4801         lives_signal_handler_unblock(rdet->encoder_combo, rdet->encoder_name_fn);
4802 
4803         lives_combo_populate(LIVES_COMBO(rdet->ofmt_combo), ofmt);
4804         lives_signal_handler_block(rdet->ofmt_combo, rdet->encoder_ofmt_fn);
4805         lives_combo_set_active_string(LIVES_COMBO(rdet->ofmt_combo), mainw->string_constants[LIVES_STRING_CONSTANT_ANY]);
4806         lives_signal_handler_unblock(rdet->ofmt_combo, rdet->encoder_ofmt_fn);
4807 
4808         lives_list_free(ofmt);
4809         if (prefs->acodec_list) {
4810           lives_list_free_all(&prefs->acodec_list);
4811         }
4812         prefs->acodec_list = lives_list_append(prefs->acodec_list, lives_strdup(mainw->string_constants[LIVES_STRING_CONSTANT_ANY]));
4813 
4814         lives_combo_populate(LIVES_COMBO(rdet->acodec_combo), prefs->acodec_list);
4815 
4816         lives_combo_set_active_string(LIVES_COMBO(rdet->acodec_combo), mainw->string_constants[LIVES_STRING_CONSTANT_ANY]);
4817 
4818         rdet->enc_changed = FALSE;
4819 
4820         return;
4821       }
4822 
4823       // finalise old plugin
4824       plugin_request(PLUGIN_ENCODERS, prefs->encoder.name, "finalise");
4825 
4826       clear_mainw_msg();
4827       // initialise new plugin
4828       if ((dummy_list = plugin_request(PLUGIN_ENCODERS, new_encoder_name, "init")) == NULL) {
4829         if (*mainw->msg) {
4830           msg = lives_strdup_printf(_("\n\nThe '%s' plugin reports:\n%s\n"), new_encoder_name, mainw->msg);
4831         } else {
4832           msg = lives_strdup_printf
4833                 (_("\n\nUnable to find the 'init' method in the %s plugin.\nThe plugin may be broken or not installed correctly."),
4834                  new_encoder_name);
4835         }
4836 
4837         if (mainw->is_ready) {
4838           do_error_dialog_with_check(msg, 0);
4839         }
4840 
4841         lives_free(msg);
4842 
4843         if (prefsw) {
4844           lives_signal_handler_block(prefsw->encoder_combo, prefsw->encoder_name_fn);
4845           // ---
4846           lives_combo_set_active_string(LIVES_COMBO(prefsw->encoder_combo), prefs->encoder.name);
4847           // ---
4848           lives_signal_handler_unblock(prefsw->encoder_combo, prefsw->encoder_name_fn);
4849         }
4850 
4851         if (rdet) {
4852           lives_signal_handler_block(rdet->encoder_combo, rdet->encoder_name_fn);
4853           // ---
4854           lives_combo_set_active_string(LIVES_COMBO(rdet->encoder_combo), rdet->encoder_name);
4855           // ---
4856           lives_signal_handler_unblock(rdet->encoder_combo, rdet->encoder_name_fn);
4857         }
4858 
4859         dummy_list = plugin_request(PLUGIN_ENCODERS, prefs->encoder.name, "init");
4860         lives_list_free_all(&dummy_list);
4861         return;
4862       }
4863       lives_list_free_all(&dummy_list);
4864 
4865       lives_snprintf(future_prefs->encoder.name, 64, "%s", new_encoder_name);
4866 
4867       if ((encoder_capabilities = plugin_request(PLUGIN_ENCODERS, future_prefs->encoder.name, "get_capabilities")) == NULL) {
4868         do_plugin_encoder_error(future_prefs->encoder.name);
4869 
4870         if (prefsw) {
4871           lives_signal_handler_block(prefsw->encoder_combo, prefsw->encoder_name_fn);
4872           // ---
4873           lives_combo_set_active_string(LIVES_COMBO(prefsw->encoder_combo), prefs->encoder.name);
4874           // ---
4875           lives_signal_handler_unblock(prefsw->encoder_combo, prefsw->encoder_name_fn);
4876         }
4877 
4878         if (rdet) {
4879           lives_signal_handler_block(rdet->encoder_combo, rdet->encoder_name_fn);
4880           // ---
4881           lives_combo_set_active_string(LIVES_COMBO(rdet->encoder_combo), rdet->encoder_name);
4882           // ---
4883           lives_signal_handler_unblock(rdet->encoder_combo, rdet->encoder_name_fn);
4884         }
4885 
4886         plugin_request(PLUGIN_ENCODERS, prefs->encoder.name, "init");
4887         lives_snprintf(future_prefs->encoder.name, 64, "%s", prefs->encoder.name);
4888         return;
4889       }
4890       prefs->encoder.capabilities = atoi((char *)lives_list_nth_data(encoder_capabilities, 0));
4891       lives_list_free_all(&encoder_capabilities);
4892 
4893       // fill list with new formats
4894       if ((ofmt_all = plugin_request_by_line(PLUGIN_ENCODERS, future_prefs->encoder.name, "get_formats"))) {
4895         for (i = 0; i < lives_list_length(ofmt_all); i++) {
4896           if (get_token_count((char *)lives_list_nth_data(ofmt_all, i), '|') > 2) {
4897             array = lives_strsplit((char *)lives_list_nth_data(ofmt_all, i), "|", -1);
4898             ofmt = lives_list_append(ofmt, lives_strdup(array[1]));
4899             lives_strfreev(array);
4900           }
4901         }
4902 
4903         if (prefsw) {
4904           // we have to block here, otherwise on_ofmt_changed gets called for every added entry !
4905           lives_signal_handler_block(prefsw->ofmt_combo, prefsw->encoder_ofmt_fn);
4906 
4907           lives_combo_populate(LIVES_COMBO(prefsw->ofmt_combo), ofmt);
4908 
4909           lives_signal_handler_unblock(prefsw->ofmt_combo, prefsw->encoder_ofmt_fn);
4910         }
4911 
4912         if (rdet) {
4913           // we have to block here, otherwise on_ofmt_changed gets called for every added entry !
4914           lives_signal_handler_block(rdet->ofmt_combo, rdet->encoder_ofmt_fn);
4915 
4916           lives_combo_populate(LIVES_COMBO(rdet->ofmt_combo), ofmt);
4917 
4918           lives_signal_handler_unblock(rdet->ofmt_combo, rdet->encoder_ofmt_fn);
4919         }
4920 
4921         lives_list_free(ofmt);
4922 
4923         // set default (first) output type
4924         array = lives_strsplit((char *)lives_list_nth_data(ofmt_all, 0), "|", -1);
4925 
4926         if (rdet) {
4927           lives_combo_set_active_string(LIVES_COMBO(rdet->ofmt_combo), array[1]);
4928 
4929           if (!prefsw && strcmp(prefs->encoder.name, future_prefs->encoder.name)) {
4930             lives_snprintf(prefs->encoder.name, 64, "%s", future_prefs->encoder.name);
4931             set_string_pref(PREF_ENCODER, prefs->encoder.name);
4932             lives_snprintf(prefs->encoder.of_restrict, 1024, "%s", future_prefs->encoder.of_restrict);
4933             prefs->encoder.of_allowed_acodecs = future_prefs->encoder.of_allowed_acodecs;
4934           }
4935           rdet->enc_changed = TRUE;
4936           rdet->encoder_name = lives_strdup(prefs->encoder.name);
4937           lives_widget_set_sensitive(rdet->okbutton, TRUE);
4938         }
4939 
4940         if (prefsw) {
4941           lives_combo_set_active_string(LIVES_COMBO(prefsw->ofmt_combo), array[1]);
4942         }
4943         on_encoder_ofmt_changed(NULL, rdet);
4944         lives_strfreev(array);
4945         if (ofmt_all) {
4946           lives_list_free_all(&ofmt_all);
4947         }
4948       }
4949     }
4950 
4951 
4952     void on_insertwsound_toggled(LiVESToggleButton * togglebutton, livespointer user_data) {
4953       if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(togglebutton))) {
4954         lives_widget_set_sensitive(insertw->fit_checkbutton, FALSE);
4955       } else {
4956         lives_widget_set_sensitive(insertw->fit_checkbutton, CURRENT_CLIP_HAS_AUDIO);
4957       }
4958       mainw->fx2_bool = !mainw->fx2_bool;
4959     }
4960 
4961 
4962     /// stored values for loop locking
4963     static int loop_lock_frame = -1;
4964     static lives_direction_t ofwd;
4965 
4966     void unlock_loop_lock(void) {
4967       mainw->loop = mainw->oloop;
4968       mainw->loop_cont = mainw->oloop_cont;
4969       mainw->ping_pong = mainw->oping_pong;
4970       mainw->loop_locked = FALSE;
4971       if (CURRENT_CLIP_IS_NORMAL) {
4972         mainw->play_start = cfile->start;
4973         mainw->play_end = cfile->end;
4974       }
4975       loop_lock_frame = -1;
4976       mainw->clip_switched = TRUE;
4977     }
4978 
4979 
4980     boolean clip_can_reverse(int clipno) {
4981       if (!LIVES_IS_PLAYING || mainw->internal_messaging || mainw->is_rendering || mainw->is_processing
4982           || !IS_VALID_CLIP(clipno) || mainw->preview) return FALSE;
4983       else {
4984         lives_clip_t *sfile = mainw->files[clipno];
4985         if (sfile->clip_type == CLIP_TYPE_DISK) return TRUE;
4986         if (sfile->next_event) return FALSE;
4987         if (sfile->clip_type == CLIP_TYPE_FILE) {
4988           lives_clip_data_t *cdata = ((lives_decoder_t *)sfile->ext_src)->cdata;
4989           if (!cdata || !(cdata->seek_flag & LIVES_SEEK_FAST_REV)) return FALSE;
4990         }
4991       }
4992       return TRUE;
4993     }
4994 
4995 
4996     boolean dirchange_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
4997                                livespointer area_enum) {
4998       int area = LIVES_POINTER_TO_INT(area_enum);
4999 
5000       if (!(mod & LIVES_ALT_MASK) && (mod & LIVES_CONTROL_MASK) && mainw->loop_locked) {
5001         boolean do_ret = FALSE;
5002         if (!clip_can_reverse(mainw->current_file) || !mainw->ping_pong || ((cfile->pb_fps >= 0. && ofwd == LIVES_DIRECTION_FORWARD)
5003             || (cfile->pb_fps < 0. && ofwd == LIVES_DIRECTION_BACKWARD))) do_ret = TRUE;
5004         unlock_loop_lock();
5005         if (do_ret) return TRUE;
5006       }
5007 
5008       if (area == SCREEN_AREA_FOREGROUND) {
5009         if (!CURRENT_CLIP_IS_NORMAL
5010             || (!clip_can_reverse(mainw->current_file) && cfile->pb_fps > 0.)) return TRUE;
5011         // change play direction
5012         if (cfile->play_paused) {
5013           if (!clip_can_reverse(mainw->current_file) && cfile->freeze_fps > 0.) return TRUE;
5014           cfile->freeze_fps = -cfile->freeze_fps;
5015           return TRUE;
5016         }
5017 
5018         /// set this so we invalid preload cache
5019         if (mainw->scratch == SCRATCH_NONE) mainw->scratch = SCRATCH_REV;
5020 
5021         lives_signal_handler_block(mainw->spinbutton_pb_fps, mainw->pb_fps_func);
5022         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), -cfile->pb_fps);
5023         lives_signal_handler_unblock(mainw->spinbutton_pb_fps, mainw->pb_fps_func);
5024 
5025         // make sure this is called, sometimes we switch clips too soon...
5026         changed_fps_during_pb(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), NULL);
5027       } else if (area == SCREEN_AREA_BACKGROUND) {
5028         if (!IS_NORMAL_CLIP(mainw->blend_file)
5029             || (!clip_can_reverse(mainw->blend_file) && mainw->files[mainw->blend_file]->pb_fps >= 0.)) return TRUE;
5030         mainw->files[mainw->blend_file]->pb_fps = -mainw->files[mainw->blend_file]->pb_fps;
5031       }
5032       return TRUE;
5033     }
5034 
5035 
5036     /**
5037        @brief set in / out points for video looping
5038        during free playback, it is possible to set in / out points for video looping
5039        after setting both points, the video becomes "loop locked", i.e when one point
5040        is reached, it will jump to the other point (if the clip can reversee the it will instead
5041        ping-pong between the 2 points, ie. when one end point is reached,, the playdirection will flip)
5042 
5043        loop lock can be disabled by any of the following:
5044        switching to another clip
5045        chaging clip direction manually
5046        triggering a bookmark
5047        ending playback
5048        when loop lock is released, the original play direction from before entering the lock is
5049        applied, if the lock was ended by triggering a direction change then the actual
5050        direction change is ignored
5051 
5052        If both are already set, then triggering this again will reduce the loop length
5053     */
5054     boolean dirchange_lock_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
5055                                     livespointer area_enum) {
5056 
5057       //if (!clip_can_reverse(mainw->current_file)) return TRUE;
5058 
5059       if (!mainw->loop_locked && loop_lock_frame == -1) loop_lock_frame = mainw->actual_frame;
5060       else {
5061         // temporary loop_cont / ping-pong
5062         mainw->clip_switched = FALSE;
5063         if ((!mainw->loop_locked && mainw->actual_frame < loop_lock_frame) || (mainw->loop_locked && cfile->pb_fps < 0)) {
5064           if (!mainw->loop_locked || mainw->play_end - mainw->actual_frame > LOOP_LOCK_MIN_FRAMES)
5065             mainw->play_start = mainw->actual_frame;
5066           if (!mainw->loop_locked) mainw->play_end = loop_lock_frame;
5067         } else {
5068           if (!mainw->loop_locked) mainw->play_start = loop_lock_frame;
5069           if (!mainw->loop_locked || mainw->actual_frame - mainw->play_start > LOOP_LOCK_MIN_FRAMES)
5070             mainw->play_end = mainw->actual_frame;
5071         }
5072         if (!mainw->loop_locked) {
5073           mainw->oloop = mainw->loop;
5074           mainw->oloop_cont = mainw->loop_cont;
5075           mainw->oping_pong = mainw->ping_pong;
5076           /// store original direction so when we unlock loop lock we come out with original
5077           /// this is reversed because we already had one reversal
5078           ofwd = cfile->pb_fps < 0. ? LIVES_DIRECTION_FORWARD : LIVES_DIRECTION_BACKWARD;
5079         }
5080         mainw->loop_cont = TRUE;
5081         if (clip_can_reverse(mainw->current_file))
5082           mainw->ping_pong = TRUE;
5083         mainw->loop = FALSE;
5084         mainw->loop_locked = TRUE;
5085         loop_lock_frame = -1;
5086       }
5087       if (clip_can_reverse(mainw->current_file))
5088         return dirchange_callback(group, obj, keyval, mod, area_enum);
5089       return TRUE;
5090     }
5091 
5092 
5093     void on_volch_pressed(LiVESButton * button, livespointer user_data) {
5094       lives_direction_t dirn = LIVES_POINTER_TO_INT(user_data);
5095       if (!CURRENT_CLIP_IS_VALID || mainw->preview || (mainw->is_processing && cfile->is_loaded) || !mainw->cliplist) return;
5096       if (dirn == LIVES_DIRECTION_UP) cfile->vol += .01;
5097       else cfile->vol -= .01;
5098       if (cfile->vol > 2.) cfile->vol = 2.;
5099       if (cfile->vol < 0.) cfile->vol = 0.;
5100       if (prefs->show_overlay_msgs && !(mainw->urgency_msg && prefs->show_urgency_msgs))
5101         d_print_overlay(.5, _("Clip volume: %.2f"), cfile->vol);
5102     }
5103 
5104 
5105     boolean fps_reset_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
5106                                livespointer area_enum) {
5107       // reset playback fps (cfile->pb_fps) to normal fps (cfile->fps)
5108       // also resync the audio
5109       int area;
5110       if (!LIVES_IS_PLAYING || mainw->multitrack) return TRUE;
5111 
5112       area = LIVES_POINTER_TO_INT(area_enum);
5113       if (area == SCREEN_AREA_BACKGROUND) {
5114         if (!IS_NORMAL_CLIP(mainw->blend_file)) return TRUE;
5115         mainw->files[mainw->blend_file]->pb_fps = mainw->files[mainw->blend_file]->fps;
5116         return TRUE;
5117       }
5118 
5119       mainw->scratch = SCRATCH_JUMP_NORESYNC;
5120 
5121       if (mainw->loop_locked) {
5122         dirchange_callback(group, obj, keyval, LIVES_CONTROL_MASK, SCREEN_AREA_FOREGROUND);
5123       }
5124 
5125       if (prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS) {
5126         resync_audio(((double)cfile->frameno));
5127         /* + (double)(lives_get_current_playback_ticks(mainw->origsecs, mainw->orignsecs, NULL) */
5128         /* - mainw->startticks) / TICKS_PER_SECOND_DBL * cfile->pb_fps); */
5129       }
5130 
5131       // change play direction
5132       if (cfile->play_paused) {
5133         if (cfile->freeze_fps < 0.) cfile->freeze_fps = -cfile->fps;
5134         else cfile->freeze_fps = cfile->fps;
5135         return TRUE;
5136       }
5137 
5138       lives_signal_handler_block(mainw->spinbutton_pb_fps, mainw->pb_fps_func);
5139       if (cfile->pb_fps > 0.) lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), cfile->fps);
5140       else lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), -cfile->fps);
5141       lives_signal_handler_unblock(mainw->spinbutton_pb_fps, mainw->pb_fps_func);
5142 
5143       // make sure this is called, sometimes we switch clips too soon...
5144       changed_fps_during_pb(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), NULL);
5145 
5146       return TRUE;
5147     }
5148 
5149 
5150     boolean prevclip_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
5151                               livespointer user_data) {
5152       LiVESList *list_index;
5153       int i = 0;
5154       int num_tried = 0, num_clips;
5155       int type = 0;
5156 
5157       // prev clip
5158       // type = 0 : if the effect is a transition, this will change the background clip
5159       // type = 1 fg only
5160       // type = 2 bg only
5161 
5162       if (!LIVES_IS_INTERACTIVE) return TRUE;
5163       if (mainw->go_away) return TRUE;
5164 
5165       if (!CURRENT_CLIP_IS_VALID || mainw->preview || (mainw->is_processing && cfile->is_loaded) ||
5166           !mainw->cliplist) return TRUE;
5167 
5168       if (user_data) type = LIVES_POINTER_TO_INT(user_data);
5169 
5170       if (type == 1 && mainw->new_clip != -1) return TRUE;
5171 
5172       if (type == 2 || (mainw->active_sa_clips == SCREEN_AREA_BACKGROUND && mainw->playing_file > 0 && type != 1
5173                         && !(type == 0 && !IS_NORMAL_CLIP(mainw->blend_file)))) {
5174         if (!IS_VALID_CLIP(mainw->blend_file)) return TRUE;
5175         list_index = lives_list_find(mainw->cliplist, LIVES_INT_TO_POINTER(mainw->blend_file));
5176       } else {
5177         list_index = lives_list_find(mainw->cliplist,
5178                                      LIVES_INT_TO_POINTER(mainw->swapped_clip == -1 ? mainw->current_file : mainw->swapped_clip));
5179       }
5180       mainw->swapped_clip = -1;
5181 
5182       num_clips = lives_list_length(mainw->cliplist);
5183 
5184       do {
5185         if (num_tried++ == num_clips) return TRUE; // we might have only audio clips, and then we will block here
5186         if (!list_index || ((list_index = list_index->prev) == NULL)) list_index = lives_list_last(mainw->cliplist);
5187         i = LIVES_POINTER_TO_INT(list_index->data);
5188       } while ((!mainw->files[i] || mainw->files[i]->opening || mainw->files[i]->restoring || i == mainw->scrap_file ||
5189                 i == mainw->ascrap_file || (!mainw->files[i]->frames && LIVES_IS_PLAYING)) &&
5190                i != ((type == 2 || (mainw->playing_file > 0 && mainw->active_sa_clips == SCREEN_AREA_BACKGROUND && type != 1)) ?
5191                      mainw->blend_file : mainw->current_file));
5192 
5193       switch_clip(type, i, FALSE);
5194 
5195       return TRUE;
5196     }
5197 
5198 
5199     boolean nextclip_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
5200                               livespointer user_data) {
5201       LiVESList *list_index;
5202       int i;
5203       int num_tried = 0, num_clips;
5204 
5205       int type = 0; ///< auto (switch bg if a transition is active, otherwise foreground)
5206 
5207       if (!LIVES_IS_INTERACTIVE) return TRUE;
5208       if (mainw->go_away) return TRUE;
5209       // next clip
5210       // if the effect is a transition, this will change the background clip
5211       if (!CURRENT_CLIP_IS_VALID || mainw->preview || (mainw->is_processing && cfile->is_loaded) ||
5212           !mainw->cliplist) return TRUE;
5213 
5214       if (user_data) type = LIVES_POINTER_TO_INT(user_data);
5215 
5216       if (type == 1 && mainw->new_clip != -1) return TRUE;
5217 
5218       if (type == 2 || (mainw->active_sa_clips == SCREEN_AREA_BACKGROUND && mainw->playing_file > 0 && type != 1
5219                         && !(type == 0 && !IS_NORMAL_CLIP(mainw->blend_file)))) {
5220         if (!IS_VALID_CLIP(mainw->blend_file)) return TRUE;
5221         list_index = lives_list_find(mainw->cliplist, LIVES_INT_TO_POINTER(mainw->blend_file));
5222       } else {
5223         list_index = lives_list_find(mainw->cliplist,
5224                                      LIVES_INT_TO_POINTER(mainw->swapped_clip == -1 ? mainw->current_file : mainw->swapped_clip));
5225       }
5226       mainw->swapped_clip = -1;
5227 
5228       num_clips = lives_list_length(mainw->cliplist);
5229 
5230       do {
5231         if (num_tried++ == num_clips) return TRUE; // we might have only audio clips, and then we will block here
5232         if (!list_index || ((list_index = list_index->next) == NULL)) list_index = mainw->cliplist;
5233         i = LIVES_POINTER_TO_INT(list_index->data);
5234       } while ((!mainw->files[i] || mainw->files[i]->opening || mainw->files[i]->restoring || i == mainw->scrap_file ||
5235                 i == mainw->ascrap_file || (!mainw->files[i]->frames && LIVES_IS_PLAYING)) &&
5236                i != ((type == 2 || (mainw->playing_file > 0 && mainw->active_sa_clips == SCREEN_AREA_BACKGROUND && type != 1)) ?
5237                      mainw->blend_file : mainw->current_file));
5238 
5239       switch_clip(type, i, FALSE);
5240 
5241       return TRUE;
5242     }
5243 
5244 
5245     static LiVESResponseType rewrite_orderfile(boolean is_append, boolean add, boolean * got_new_handle) {
5246       char *ordfile = lives_build_filename(prefs->workdir, mainw->set_name, CLIP_ORDER_FILENAME, NULL);
5247       char *ordfile_new = lives_build_filename(prefs->workdir, mainw->set_name, CLIP_ORDER_FILENAME "." LIVES_FILE_EXT_NEW, NULL);
5248       char *cwd = lives_get_current_dir();
5249       char *new_dir;
5250       char *dfile, *ord_entry;
5251       char buff[PATH_MAX] = {0};
5252       char new_handle[256] = {0};
5253       LiVESResponseType retval;
5254       LiVESList *cliplist;
5255       int ord_fd, i;
5256 
5257       do {
5258         // create the orderfile which lists all the clips in order
5259         retval = LIVES_RESPONSE_NONE;
5260         if (!is_append) ord_fd = creat(ordfile_new, DEF_FILE_PERMS);
5261         else {
5262           lives_cp(ordfile, ordfile_new);
5263           ord_fd = open(ordfile_new, O_CREAT | O_WRONLY | O_APPEND, DEF_FILE_PERMS);
5264         }
5265 
5266         if (ord_fd < 0) {
5267           retval = do_write_failed_error_s_with_retry(ordfile, lives_strerror(errno));
5268           if (retval == LIVES_RESPONSE_CANCEL) {
5269             lives_free(ordfile);
5270             lives_free(ordfile_new);
5271             lives_chdir(cwd, FALSE);
5272             lives_free(cwd);
5273             return retval;
5274           }
5275         }
5276 
5277         else {
5278           char *oldval, *newval;
5279 
5280           for (cliplist = mainw->cliplist; cliplist; cliplist = cliplist->next) {
5281             if (THREADVAR(write_failed)) break;
5282             threaded_dialog_spin(0.);
5283             lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
5284 
5285             i = LIVES_POINTER_TO_INT(cliplist->data);
5286             if (IS_NORMAL_CLIP(i) && i != mainw->scrap_file && i != mainw->ascrap_file) {
5287               lives_snprintf(buff, PATH_MAX, "%s", mainw->files[i]->handle);
5288               get_basename(buff);
5289               if (*buff) {
5290                 lives_snprintf(new_handle, 256, "%s/%s/%s", mainw->set_name, CLIPS_DIRNAME, buff);
5291               } else {
5292                 lives_snprintf(new_handle, 256, "%s/%s/%s", mainw->set_name, CLIPS_DIRNAME, mainw->files[i]->handle);
5293               }
5294 
5295               if (strcmp(new_handle, mainw->files[i]->handle)) {
5296                 if (!add) continue;
5297 
5298                 new_dir = lives_build_path(prefs->workdir, new_handle, NULL);
5299                 if (lives_file_test(new_dir, LIVES_FILE_TEST_IS_DIR)) {
5300                   // get a new unique handle
5301                   get_temp_handle(i);
5302                   lives_snprintf(new_handle, 256, "%s/%s/%s", mainw->set_name, CLIPS_DIRNAME, mainw->files[i]->handle);
5303                 }
5304                 lives_free(new_dir);
5305 
5306                 // move the files
5307                 oldval = lives_build_path(prefs->workdir, mainw->files[i]->handle, NULL);
5308                 newval = lives_build_path(prefs->workdir, new_handle, NULL);
5309 
5310                 lives_mv(oldval, newval);
5311                 lives_free(oldval);
5312                 lives_free(newval);
5313 
5314                 if (THREADVAR(com_failed)) {
5315                   close(ord_fd);
5316                   end_threaded_dialog();
5317                   lives_free(ordfile);
5318                   lives_free(ordfile_new);
5319                   lives_chdir(cwd, FALSE);
5320                   lives_free(cwd);
5321                   return FALSE;
5322                 }
5323 
5324                 *got_new_handle = TRUE;
5325 
5326                 lives_snprintf(mainw->files[i]->handle, 256, "%s", new_handle);
5327                 dfile = lives_build_filename(prefs->workdir, mainw->files[i]->handle, LIVES_STATUS_FILE_NAME, NULL);
5328                 lives_snprintf(mainw->files[i]->info_file, PATH_MAX, "%s", dfile);
5329                 lives_free(dfile);
5330               }
5331 
5332               ord_entry = lives_strdup_printf("%s\n", mainw->files[i]->handle);
5333               lives_write(ord_fd, ord_entry, strlen(ord_entry), FALSE);
5334               lives_free(ord_entry);
5335             }
5336           }
5337 
5338           if (THREADVAR(write_failed) == ord_fd) {
5339             THREADVAR(write_failed) = 0;
5340             retval = do_write_failed_error_s_with_retry(ordfile, NULL);
5341           }
5342         }
5343       } while (retval == LIVES_RESPONSE_RETRY);
5344 
5345       close(ord_fd);
5346 
5347       if (retval == LIVES_RESPONSE_CANCEL) lives_rm(ordfile_new);
5348       else  lives_cp(ordfile_new, ordfile);
5349 
5350       lives_free(ordfile);
5351       lives_free(ordfile_new);
5352 
5353       lives_chdir(cwd, FALSE);
5354       lives_free(cwd);
5355       return retval;
5356     }
5357 
5358 
5359     boolean on_save_set_activate(LiVESWidget * widget, livespointer user_data) {
5360       // here is where we save clipsets
5361       // SAVE CLIPSET FUNCTION
5362       // also handles migration and merging of sets
5363       // new_set_name can be passed in userdata, it should be in filename encoding
5364       // TODO - caller to do end_threaded_dialog()
5365 
5366       /////////////////
5367       /// IMPORTANT !!!  mainw->no_exit must be set. otherwise the app will exit
5368       ////////////
5369 
5370       char new_set_name[MAX_SET_NAME_LEN] = {0};
5371 
5372       char *old_set = lives_strdup(mainw->set_name);
5373       char *layout_map_file, *layout_map_dir, *new_clips_dir, *current_clips_dir;
5374       //char *tmp;
5375       char *text;
5376 
5377       char *tmp;
5378       char *osetn, *nsetn, *dfile;
5379 
5380       boolean is_append = FALSE; // we will overwrite the target layout.map file
5381       boolean response;
5382       boolean got_new_handle = FALSE;
5383 
5384       int retval;
5385 
5386       if (!mainw->cliplist) return FALSE;
5387 
5388       // warn the user what will happen
5389       if (!user_data && !do_save_clipset_warn()) return FALSE;
5390 
5391       if (mainw->stored_event_list && mainw->stored_event_list_changed) {
5392         // if we have a current layout, give the user the chance to change their mind
5393         if (!check_for_layout_del(NULL, FALSE)) return FALSE;
5394       }
5395 
5396       if (!user_data) {
5397         // this was called from the GUI
5398         do {
5399           // prompt for a set name, advise user to save set
5400           renamew = create_rename_dialog(2);
5401           response = lives_dialog_run(LIVES_DIALOG(renamew->dialog));
5402           if (response == LIVES_RESPONSE_CANCEL) return FALSE;
5403           lives_snprintf(new_set_name, MAX_SET_NAME_LEN, "%s",
5404                          (tmp = U82F(lives_entry_get_text(LIVES_ENTRY(renamew->entry)))));
5405           lives_free(tmp);
5406           lives_widget_destroy(renamew->dialog);
5407           lives_free(renamew);
5408           lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
5409         } while (!is_legal_set_name(new_set_name, TRUE, FALSE));
5410       } else lives_snprintf(new_set_name, MAX_SET_NAME_LEN, "%s", (char *)user_data);
5411 
5412       lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
5413 
5414       lives_snprintf(mainw->set_name, MAX_SET_NAME_LEN, "%s", new_set_name);
5415 
5416       if (lives_strcmp(mainw->set_name, old_set)) {
5417         // The user CHANGED the set name
5418         // we must migrate all physical files for the set, and possibly merge with another set
5419 
5420         new_clips_dir = CLIPS_DIR(mainw->set_name);
5421         // check if target clips dir exists, ask if user wants to append files
5422         if (lives_file_test(new_clips_dir, LIVES_FILE_TEST_IS_DIR)) {
5423           lives_free(new_clips_dir);
5424           if (mainw->osc_auto == 0) {
5425             if (!do_set_duplicate_warning(mainw->set_name)) {
5426               lives_snprintf(mainw->set_name, MAX_SET_NAME_LEN, "%s", old_set);
5427               return FALSE;
5428             }
5429           } else if (mainw->osc_auto == 1) return FALSE;
5430 
5431           is_append = TRUE;
5432         } else {
5433           lives_free(new_clips_dir);
5434           layout_map_file = lives_build_filename(prefs->workdir, mainw->set_name, LAYOUTS_DIRNAME,
5435                                                  LAYOUT_MAP_FILENAME, NULL);
5436           // if target has layouts dir but no clips, it means we have old layouts !
5437           if (lives_file_test(layout_map_file, LIVES_FILE_TEST_EXISTS)) {
5438             if (do_set_rename_old_layouts_warning(mainw->set_name)) {
5439               // user answered "yes" - delete
5440               // clear _old_layout maps
5441               char *dfile = lives_build_filename(prefs->workdir, mainw->set_name, LAYOUTS_DIRNAME, NULL);
5442               lives_rm(dfile);
5443               //lives_free(dfile);
5444             }
5445           }
5446           lives_free(layout_map_file);
5447         }
5448       }
5449 
5450       text = lives_strdup_printf(_("Saving set %s"), mainw->set_name);
5451       do_threaded_dialog(text, FALSE);
5452       lives_free(text);
5453 
5454       /////////////////////////////////////////////////////////////
5455 
5456       THREADVAR(com_failed) = FALSE;
5457 
5458       current_clips_dir = lives_build_filename(prefs->workdir, old_set, CLIPS_DIRNAME "/", NULL);
5459       if (*old_set && strcmp(mainw->set_name, old_set)
5460           && lives_file_test(current_clips_dir, LIVES_FILE_TEST_IS_DIR)) {
5461         // set name was changed for an existing set
5462         if (!is_append) {
5463           // create new dir, in case it doesn't already exist
5464           dfile = lives_build_filename(prefs->workdir, mainw->set_name, CLIPS_DIRNAME, NULL);
5465           if (!lives_make_writeable_dir(dfile)) {
5466             // abort if we cannot create the new subdir
5467             LIVES_ERROR("Could not create directory");
5468             LIVES_ERROR(dfile);
5469             d_print_file_error_failed();
5470             lives_snprintf(mainw->set_name, MAX_SET_NAME_LEN, "%s", old_set);
5471             lives_free(dfile);
5472             end_threaded_dialog();
5473             return FALSE;
5474           }
5475           lives_free(dfile);
5476         }
5477       } else {
5478         // saving as same name (or as new set)
5479         dfile = lives_build_filename(prefs->workdir, mainw->set_name, CLIPS_DIRNAME, NULL);
5480         if (!lives_make_writeable_dir(dfile)) {
5481           // abort if we cannot create the new subdir
5482           LIVES_ERROR("Could not create directory");
5483           LIVES_ERROR(dfile);
5484           d_print_file_error_failed();
5485           lives_snprintf(mainw->set_name, MAX_SET_NAME_LEN, "%s", old_set);
5486           lives_free(dfile);
5487           end_threaded_dialog();
5488           return FALSE;
5489         }
5490         lives_free(dfile);
5491       }
5492       lives_free(current_clips_dir);
5493 
5494       if (mainw->scrap_file > -1) close_scrap_file(TRUE);
5495       if (mainw->ascrap_file > -1) close_ascrap_file(TRUE);
5496 
5497       retval = rewrite_orderfile(is_append, TRUE, &got_new_handle);
5498 
5499       if (retval == LIVES_RESPONSE_CANCEL) {
5500         end_threaded_dialog();
5501         return FALSE;
5502       }
5503 
5504       if (mainw->num_sets > -1) mainw->num_sets++;
5505       if (!*old_set) mainw->set_list = lives_list_prepend(mainw->set_list, mainw->set_name);
5506 
5507       if (got_new_handle && !*old_set) migrate_layouts(NULL, mainw->set_name);
5508 
5509       if (*old_set && lives_strcmp(old_set, mainw->set_name)) {
5510         layout_map_dir = lives_build_path(prefs->workdir, old_set, LAYOUTS_DIRNAME, NULL);
5511         layout_map_file = lives_build_filename(layout_map_dir, LAYOUT_MAP_FILENAME, NULL);
5512         // update details for layouts - needs_set, current_layout_map and affected_layout_map
5513         if (lives_file_test(layout_map_file, LIVES_FILE_TEST_EXISTS)) {
5514           migrate_layouts(old_set, mainw->set_name);
5515           // save updated layout.map (with new handles), we will move it below
5516 
5517           save_layout_map(NULL, NULL, NULL, layout_map_dir);
5518 
5519           got_new_handle = FALSE;
5520         }
5521         lives_free(layout_map_file);
5522         lives_free(layout_map_dir);
5523 
5524         if (is_append) {
5525           osetn = lives_build_filename(prefs->workdir, old_set, LAYOUTS_DIRNAME, LAYOUT_MAP_FILENAME, NULL);
5526           nsetn = lives_build_filename(prefs->workdir, mainw->set_name, LAYOUTS_DIRNAME, LAYOUT_MAP_FILENAME, NULL);
5527 
5528           if (lives_file_test(osetn, LIVES_FILE_TEST_EXISTS)) {
5529             //append current layout.map to target one
5530             lives_cat(osetn, nsetn, TRUE); /// command may not fail, so we check first
5531             lives_rm(osetn);
5532           }
5533           lives_free(osetn);
5534           lives_free(nsetn);
5535         }
5536 
5537         osetn = lives_build_path(prefs->workdir, old_set, LAYOUTS_DIRNAME, NULL);
5538 
5539         if (lives_file_test(osetn, LIVES_FILE_TEST_IS_DIR)) {
5540           nsetn = lives_build_filename(prefs->workdir, mainw->set_name, NULL);
5541 
5542           // move any layouts from old set to new (including layout.map)
5543           lives_cp_keep_perms(osetn, nsetn);
5544 
5545           lives_free(nsetn);
5546         }
5547 
5548         lives_free(osetn);
5549 
5550         // remove the old set (should be empty now)
5551         cleanup_set_dir(old_set);
5552       }
5553 
5554       if (!mainw->was_set && !strcmp(old_set, mainw->set_name)) {
5555         // set name was set by export or save layout, now we need to update our layout map
5556         layout_map_dir = lives_build_filename(prefs->workdir, old_set, LAYOUTS_DIRNAME, NULL);
5557         layout_map_file = lives_build_filename(layout_map_dir, LAYOUT_MAP_FILENAME, NULL);
5558         if (lives_file_test(layout_map_file, LIVES_FILE_TEST_EXISTS)) save_layout_map(NULL, NULL, NULL, layout_map_dir);
5559         mainw->was_set = TRUE;
5560         got_new_handle = FALSE;
5561         lives_free(layout_map_dir);
5562         lives_free(layout_map_file);
5563         if (mainw->multitrack && !mainw->multitrack->changed) recover_layout_cancelled(FALSE);
5564       }
5565 
5566       if (mainw->current_layouts_map && strcmp(old_set, mainw->set_name) && !mainw->suppress_layout_warnings) {
5567         // warn the user about layouts if the set name changed
5568         // but, don't bother the user with errors if we are exiting
5569         add_lmap_error(LMAP_INFO_SETNAME_CHANGED, old_set, mainw->set_name, 0, 0, 0., FALSE);
5570         popup_lmap_errors(NULL, NULL);
5571       }
5572 
5573       lives_notify(LIVES_OSC_NOTIFY_CLIPSET_SAVED, old_set);
5574 
5575       lives_free(old_set);
5576       mainw->leave_files = TRUE;
5577       if (mainw->multitrack && !mainw->only_close) mt_memory_free();
5578       else if (mainw->multitrack) wipe_layout(mainw->multitrack);
5579 
5580       // do a lot of cleanup here, but leave files
5581       lives_exit(0);
5582       mainw->leave_files = FALSE;
5583       //end_threaded_dialog();
5584 
5585       lives_widget_set_sensitive(mainw->vj_load_set, TRUE);
5586       return TRUE;
5587     }
5588 
5589 
5590     char *on_load_set_activate(LiVESMenuItem * menuitem, livespointer user_data) {
5591       // get set name (use a modified rename window)
5592       char *set_name = NULL;
5593       LiVESResponseType resp;
5594       mainw->mt_needs_idlefunc = FALSE;
5595 
5596       if (mainw->multitrack) {
5597         if (mainw->multitrack->idlefunc > 0) {
5598           lives_source_remove(mainw->multitrack->idlefunc);
5599           mainw->multitrack->idlefunc = 0;
5600           mainw->mt_needs_idlefunc = TRUE;
5601         }
5602         mt_desensitise(mainw->multitrack);
5603       }
5604 
5605       renamew = create_rename_dialog(3);
5606       if (!renamew) return NULL; ///< no sets available
5607 
5608       resp = lives_dialog_run(LIVES_DIALOG(renamew->dialog));
5609 
5610       if (resp == LIVES_RESPONSE_OK) {
5611         set_name = U82F(lives_entry_get_text(LIVES_ENTRY(renamew->entry)));
5612       }
5613 
5614       // need to clean up renamew
5615       lives_widget_destroy(renamew->dialog);
5616       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
5617       lives_freep((void **)&renamew);
5618 
5619       if (resp == LIVES_RESPONSE_OK) {
5620         if (!is_legal_set_name(set_name, TRUE, TRUE)) {
5621           lives_freep((void **)&set_name);
5622         } else {
5623           if (!user_data) {
5624             if (mainw->cliplist)
5625               if (!do_reload_set_query()) return NULL;
5626             reload_set(set_name);
5627             lives_free(set_name);
5628             if (mainw->num_sets > -1) mainw->num_sets--;
5629             return NULL;
5630           }
5631         }
5632       }
5633 
5634       return set_name;
5635     }
5636 
5637 
5638     void lock_set_file(const char *set_name) {
5639       // function is called when a set is opened, to prevent multiple acces to the same set
5640       char *setdir = lives_build_path(prefs->workdir, set_name, NULL);
5641       if (lives_file_test(setdir, LIVES_FILE_TEST_IS_DIR)) {
5642         char *set_lock_file = lives_strdup_printf("%s.%d", SET_LOCK_FILENAME, capable->mainpid);
5643         char *set_locker = SET_LOCK_FILE(set_name, set_lock_file);
5644         lives_touch(set_locker);
5645         lives_free(set_locker);
5646         lives_free(set_lock_file);
5647       }
5648       lives_free(setdir);
5649     }
5650 
5651 
5652     void unlock_set_file(const char *set_name) {
5653       char *set_lock_file = lives_strdup_printf("%s.%d", SET_LOCK_FILENAME, capable->mainpid);
5654       char *set_locker = SET_LOCK_FILE(set_name, set_lock_file);
5655       lives_rm(set_locker);
5656       lives_free(set_lock_file);
5657       lives_free(set_locker);
5658     }
5659 
5660 
5661     boolean reload_set(const char *set_name) {
5662       // this is the main clip set loader
5663 
5664       // CLIP SET LOADER
5665 
5666       // setname should be in filesystem encoding
5667 
5668       FILE *orderfile;
5669       lives_clip_t *sfile;
5670 
5671       char *msg, *com, *ordfile, *cwd, *clipdir, *handle = NULL;
5672 
5673       boolean added_recovery = FALSE;
5674       boolean keep_threaded_dialog = FALSE;
5675       boolean hadbad = FALSE;
5676 
5677       int last_file = -1, new_file = -1;
5678       int current_file = mainw->current_file;
5679       int clipnum = 0;
5680       int maxframe;
5681       mainw->mt_needs_idlefunc = FALSE;
5682 
5683       if (mainw->multitrack) {
5684         if (mainw->multitrack->idlefunc > 0) {
5685           lives_source_remove(mainw->multitrack->idlefunc);
5686           mainw->multitrack->idlefunc = 0;
5687           mainw->mt_needs_idlefunc = TRUE;
5688         }
5689       }
5690       lives_memset(mainw->set_name, 0, 1);
5691 
5692       // check if set is locked
5693       if (!check_for_lock_file(set_name, 0)) {
5694         if (!mainw->recovering_files) {
5695           d_print_cancelled();
5696           if (mainw->multitrack) {
5697             mainw->current_file = mainw->multitrack->render_file;
5698             mt_sensitise(mainw->multitrack);
5699             maybe_add_mt_idlefunc();
5700           }
5701         }
5702         return FALSE;
5703       }
5704 
5705       lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "none");
5706 
5707       // check if we already have a threaded dialog running (i.e. we are called from startup)
5708       if (mainw->threaded_dialog) keep_threaded_dialog = TRUE;
5709 
5710       if (prefs->show_gui && !keep_threaded_dialog) {
5711         char *tmp;
5712         msg = lives_strdup_printf(_("Loading clips from set %s"), (tmp = F2U8(set_name)));
5713         do_threaded_dialog(msg, FALSE);
5714         lives_free(msg);
5715         lives_free(tmp);
5716       }
5717 
5718       ordfile = lives_build_filename(prefs->workdir, set_name, CLIP_ORDER_FILENAME, NULL);
5719 
5720       orderfile = fopen(ordfile, "r"); // no we can't assert this, because older sets did not have this file
5721       lives_free(ordfile);
5722 
5723       mainw->suppress_dprint = TRUE;
5724       THREADVAR(read_failed) = FALSE;
5725 
5726       // lock the set
5727       lock_set_file(set_name);
5728 
5729       cwd = lives_get_current_dir();
5730 
5731       while (1) {
5732         if (prefs->show_gui) threaded_dialog_spin(0.);
5733 
5734         if (mainw->hdrs_cache) cached_list_free(&mainw->hdrs_cache);
5735 
5736         if (!orderfile) {
5737           // old style (pre 0.9.6)
5738           com = lives_strdup_printf("%s get_next_in_set \"%s\" \"%s\" %d", prefs->backend_sync, mainw->msg,
5739                                     set_name, capable->mainpid);
5740           lives_system(com, FALSE);
5741           lives_free(com);
5742         } else {
5743           if (!lives_fgets(mainw->msg, MAINW_MSG_SIZE, orderfile)) clear_mainw_msg();
5744           else lives_memset(mainw->msg + lives_strlen(mainw->msg) - 1, 0, 1);
5745         }
5746 
5747         if (!(*mainw->msg) || (!strncmp(mainw->msg, "none", 4))) {
5748           if (!mainw->recovering_files) mainw->suppress_dprint = FALSE;
5749           if (!keep_threaded_dialog) end_threaded_dialog();
5750 
5751           if (orderfile) fclose(orderfile);
5752 
5753           //mainw->current_file = current_file;
5754 
5755           if (!mainw->multitrack) {
5756             if (last_file > 0) {
5757               threaded_dialog_spin(0.);
5758               switch_to_file(current_file, last_file);
5759               threaded_dialog_spin(0.);
5760             }
5761           }
5762 
5763           if (clipnum == 0) {
5764             do_set_noclips_error(set_name);
5765           } else {
5766             char *tmp;
5767             reset_clipmenu();
5768             lives_widget_set_sensitive(mainw->vj_load_set, FALSE);
5769 
5770             // MUST set set_name before calling this
5771             recover_layout_map(MAX_FILES);
5772 
5773             // TODO - check for missing frames and audio in layouts
5774 
5775             if (hadbad) rewrite_orderfile(FALSE, FALSE, NULL);
5776 
5777             d_print(_("%d clips and %d layouts were recovered from set (%s).\n"),
5778                     clipnum, lives_list_length(mainw->current_layouts_map), (tmp = F2U8(set_name)));
5779             lives_free(tmp);
5780             lives_snprintf(mainw->set_name, MAX_SET_NAME_LEN, "%s", set_name);
5781             lives_notify(LIVES_OSC_NOTIFY_CLIPSET_OPENED, mainw->set_name);
5782           }
5783 
5784           threaded_dialog_spin(0.);
5785           if (!mainw->multitrack) {
5786             if (mainw->is_ready) {
5787               if (clipnum > 0 && CURRENT_CLIP_IS_VALID) {
5788                 showclipimgs();
5789               }
5790               // force a redraw
5791               update_play_times();
5792               lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
5793             }
5794           } else {
5795             mainw->current_file = mainw->multitrack->render_file;
5796             polymorph(mainw->multitrack, POLY_NONE);
5797             polymorph(mainw->multitrack, POLY_CLIPS);
5798             mt_sensitise(mainw->multitrack);
5799             maybe_add_mt_idlefunc();
5800           }
5801           if (!keep_threaded_dialog) end_threaded_dialog();
5802           lives_chdir(cwd, FALSE);
5803           lives_free(cwd);
5804           if (mainw->multitrack)
5805             mt_clip_select(mainw->multitrack, TRUE); // scroll clip on screen
5806           sensitize();
5807           return TRUE;
5808         }
5809 
5810         if (clipnum > 0)
5811           mainw->was_set = TRUE;
5812 
5813         if (prefs->crash_recovery && !added_recovery) {
5814           char *recovery_entry = lives_build_filename(set_name, "*", NULL);
5815           add_to_recovery_file(recovery_entry);
5816           lives_free(recovery_entry);
5817           added_recovery = TRUE;
5818         }
5819 
5820         clipdir = lives_build_filename(prefs->workdir, mainw->msg, NULL);
5821         if (orderfile) {
5822           // newer style (0.9.6+)
5823 
5824           if (!lives_file_test(clipdir, LIVES_FILE_TEST_IS_DIR)) {
5825             lives_free(clipdir);
5826             continue;
5827           }
5828           threaded_dialog_spin(0.);
5829 
5830           //create a new cfile and fill in the details
5831           handle = lives_strndup(mainw->msg, 256);
5832         }
5833 
5834         // changes mainw->current_file on success
5835         sfile = create_cfile(-1, handle, FALSE);
5836         if (handle) lives_free(handle);
5837         handle = NULL;
5838         threaded_dialog_spin(0.);
5839 
5840         if (!sfile) {
5841           lives_free(clipdir);
5842           mainw->suppress_dprint = FALSE;
5843 
5844           if (!keep_threaded_dialog) end_threaded_dialog();
5845 
5846           if (mainw->multitrack) {
5847             mainw->current_file = mainw->multitrack->render_file;
5848             polymorph(mainw->multitrack, POLY_NONE);
5849             polymorph(mainw->multitrack, POLY_CLIPS);
5850             mt_sensitise(mainw->multitrack);
5851             maybe_add_mt_idlefunc();
5852           }
5853 
5854           recover_layout_map(MAX_FILES);
5855           lives_chdir(cwd, FALSE);
5856           lives_free(cwd);
5857           fclose(orderfile);
5858           return FALSE;
5859         }
5860 
5861         // get file details
5862         if (!read_headers(mainw->current_file, clipdir, NULL)) {
5863           /// set clip failed to load, we need to do something with it
5864           /// else it will keep trying to reload each time.
5865           /// for now we shall just move it out of the set
5866           /// this is fine as the user can try to recover it at a later time
5867           lives_free(clipdir);
5868           lives_free(mainw->files[mainw->current_file]);
5869           mainw->files[mainw->current_file] = NULL;
5870           if (mainw->first_free_file > mainw->current_file) mainw->first_free_file = mainw->current_file;
5871           if (mainw->hdrs_cache) cached_list_free(&mainw->hdrs_cache);
5872           hadbad = TRUE;
5873           continue;
5874         }
5875         lives_free(clipdir);
5876 
5877         threaded_dialog_spin(0.);
5878 
5879         /** read_headers() will have read the clip metadata file, so we 'know' how many frames there ought to be.
5880           however this could be wrong if the file was damaged for some reason. So the first step is to read the file_index if any.
5881           - if present we assume we are dealing with CLIP_TYPE_FILE (original clip + decoded frames).
5882           -- If it contains more frames then we will increase cfile->frames.
5883           - If it is not present then we assume are dealing with CLIP_TYPE_DISK (all frames decoded to images).
5884 
5885           we want to do as little checking here as possible, since it can slow down the startup, but if we detect a problem then we'll
5886           do increasingly more checking.
5887         */
5888 
5889         if ((maxframe = load_frame_index(mainw->current_file)) > 0) {
5890           // CLIP_TYPE_FILE
5891           /** here we attempt to reload the clip. First we load the frame_index if any, If it contains more frames than the metadata says, then
5892             we trust the frame_index for now, since the metadata may have been corrupted.
5893             If it contains fewer frames, then we will warn the user, but we cannot know whether the metadata is correct or not,
5894             and in any case we cannot reconstruct the frame_index, so we have to use what it tells us.
5895             If file_index is absent then we assume we are dealing with CLIP_TYPE_DISK (below).
5896 
5897             Next we attempt to reload the original clip using the same decoder plugin as last time if possible.
5898             The decoder may return fewer frames from the original clip than the size of frame_index,
5899             This is OK provided the final frames are decoded frames.
5900             We then check backwards from the end of file_index to find the final decoded frame.
5901             If this is the frame we expected then we assume all is OK */
5902 
5903           if (!reload_clip(mainw->current_file, maxframe)) {
5904             if (mainw->hdrs_cache) cached_list_free(&mainw->hdrs_cache);
5905             continue;
5906           }
5907           if (cfile->clip_type == CLIP_TYPE_FILE && cfile->header_version >= 102) cfile->fps = cfile->pb_fps;
5908 
5909           /** if the image type is still unkown it means either there were no decoded frames, or the final decoded frame was absent
5910             so we count the virtual frames. If all are virtual then we set img_type to prefs->img_type and assume all is OK
5911             (or at least we recovered as many frames as we could using frame_index),
5912             and we'll accept whatever the decoder returns if there is a divergence with the clip metadata */
5913 
5914           if (!cfile->checked && cfile->img_type == IMG_TYPE_UNKNOWN) {
5915             lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
5916             int fvirt = count_virtual_frames(cfile->frame_index, 1, cfile->frames);
5917             /** if there are some decoded frames then we have a problem.
5918               Since the img type was not found it means that the final decoded
5919                 frame was missing. So we check backwards to find where the last actual decoded frame is and the frame count is set to
5920                 final decoded frame + any virtual frames immediately following, and warn the user.
5921                 If other frames are missing then the clip is corrupt, but we'll continue as best we can. */
5922             if (fvirt < cfile->frames) check_clip_integrity(mainw->current_file, cdata, cfile->frames);
5923           }
5924           cfile->checked = TRUE;
5925         } else {
5926           /// CLIP_TYPE_DISK
5927           /** in this case we find the last decoded frame and check the frame size and get the image_type.
5928             If there is a discrepancy with the metadata then we trust the empirical evidence.
5929             If the final frame is absent then we find the real final frame, warn the user, and adjust frame count.
5930           */
5931           boolean isok = TRUE;
5932           if (!cfile->checked) isok = check_clip_integrity(mainw->current_file, NULL, cfile->frames);
5933           cfile->checked = TRUE;
5934           /** here we do a simple check: make sure the final frame is present and frame + 1 isn't
5935             if either check fails then we count all the frames (since we don't have a frame_index to guide us),
5936             - this can be pretty slow so we wan't to avoid it unless  we detected a problem. */
5937           if (!check_frame_count(mainw->current_file, isok)) {
5938             cfile->frames = get_frame_count(mainw->current_file, 1);
5939             if (cfile->frames == -1) {
5940               close_current_file(0);
5941               if (mainw->hdrs_cache) cached_list_free(&mainw->hdrs_cache);
5942               continue;
5943             }
5944             cfile->needs_update = TRUE;
5945           }
5946         }
5947 
5948         if (!prefs->vj_mode) {
5949           if (cfile->achans > 0 && cfile->afilesize == 0) {
5950             reget_afilesize_inner(mainw->current_file);
5951           }
5952         }
5953 
5954         last_file = new_file;
5955 
5956         if (++clipnum == 1) {
5957           /** we need to set the set_name before calling add_to_clipmenu(), so that the clip gets the name of the set in
5958             its menuentry, and also prior to loading any layouts since they specirfy the clipset they need */
5959           lives_snprintf(mainw->set_name, MAX_SET_NAME_LEN, "%s", set_name);
5960         }
5961 
5962         /// read the playback fps, play frame, and name
5963         open_set_file(clipnum); ///< must do before calling save_clip_values()
5964         if (cfile->clip_type == CLIP_TYPE_FILE && cfile->header_version >= 102) cfile->fps = cfile->pb_fps;
5965 
5966         /// if this is set then it means we are auto reloading the clipset from the previous session, so restore full details
5967         if (future_prefs->ar_clipset) restore_clip_binfmt(mainw->current_file);
5968 
5969         threaded_dialog_spin(0.);
5970         cfile->was_in_set = TRUE;
5971 
5972         if (cfile->frameno > cfile->frames) cfile->frameno = cfile->last_frameno = 1;
5973 
5974         if (cfile->needs_update || cfile->needs_silent_update) {
5975           if (cfile->needs_update) do_clip_divergence_error(mainw->current_file);
5976           save_clip_values(mainw->current_file);
5977           cfile->needs_silent_update = cfile->needs_update = FALSE;
5978         }
5979 
5980         if (mainw->hdrs_cache) cached_list_free(&mainw->hdrs_cache);
5981 
5982         if (prefs->autoload_subs) {
5983           reload_subs(mainw->current_file);
5984           threaded_dialog_spin(0.);
5985         }
5986 
5987         get_total_time(cfile);
5988 
5989         cfile->saved_frameno = cfile->frameno;
5990         if (cfile->frameno > cfile->frames && cfile->frameno > 1) cfile->frameno = cfile->frames;
5991         cfile->last_frameno = cfile->frameno;
5992 
5993         cfile->pointer_time = cfile->real_pointer_time = calc_time_from_frame(mainw->current_file, cfile->frameno);
5994         if (cfile->real_pointer_time > CLIP_TOTAL_TIME(mainw->current_file))
5995           cfile->real_pointer_time = CLIP_TOTAL_TIME(mainw->current_file);
5996         if (cfile->pointer_time > cfile->video_time) cfile->pointer_time = 0.;
5997 
5998         if (cfile->achans) {
5999           cfile->aseek_pos = (off64_t)((double)(cfile->real_pointer_time * cfile->arate) * cfile->achans *
6000                                        (cfile->asampsize / 8));
6001           if (cfile->aseek_pos > cfile->afilesize) cfile->aseek_pos = 0.;
6002         }
6003 
6004         // add to clip menu
6005         threaded_dialog_spin(0.);
6006         add_to_clipmenu();
6007         cfile->start = cfile->frames > 0 ? 1 : 0;
6008         cfile->end = cfile->frames;
6009         cfile->is_loaded = TRUE;
6010         cfile->changed = TRUE;
6011         lives_rm(cfile->info_file);
6012         set_main_title(cfile->name, 0);
6013         restore_clip_binfmt(mainw->current_file);
6014 
6015         if (!mainw->multitrack) {
6016           resize(1);
6017         }
6018 
6019         if (mainw->multitrack && mainw->multitrack->is_ready) {
6020           new_file = mainw->current_file;
6021           mainw->current_file = mainw->multitrack->render_file;
6022           mt_init_clips(mainw->multitrack, new_file, TRUE);
6023           lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
6024           mt_clip_select(mainw->multitrack, TRUE);
6025         }
6026 
6027         lives_notify(LIVES_OSC_NOTIFY_CLIP_OPENED, "");
6028       }
6029 
6030       // should never reach here
6031       lives_chdir(cwd, FALSE);
6032       lives_free(cwd);
6033       return TRUE;
6034     }
6035 
6036 
6037     static void recover_lost_clips(LiVESList * reclist) {
6038       if (!do_foundclips_query()) return;
6039 
6040       //d_print_cancelled();
6041 
6042       // save set
6043       if (!CURRENT_CLIP_IS_CLIPBOARD && CURRENT_CLIP_IS_VALID) {
6044         on_quit_activate(NULL, LIVES_INT_TO_POINTER(1));
6045         if (mainw->clips_available) return;
6046       }
6047 
6048       // recover files
6049       mainw->recovery_list = reclist;
6050       recover_files(NULL, TRUE);
6051 
6052       if (prefs->crash_recovery) rewrite_recovery_file();
6053 
6054       if (!CURRENT_CLIP_IS_VALID) {
6055         int start_file;
6056         for (start_file = MAX_FILES; start_file > 0; start_file--) {
6057           if (IS_VALID_CLIP(start_file)
6058               && (mainw->files[start_file]->frames > 0 || mainw->files[start_file]->afilesize > 0)) break;
6059         }
6060         switch_to_file(-1, start_file);
6061       }
6062       do_info_dialogf(P_("$d clip was recovered", "%d clips were recovered\n",
6063                          mainw->clips_available), mainw->clips_available);
6064     }
6065 
6066 
6067     static boolean handle_remnants(LiVESList * recnlist, const char *trashremdir, LiVESList **rem_list) {
6068       LiVESResponseType resp;
6069       LiVESList *list = recnlist;
6070       char *unrecdir = lives_build_path(prefs->workdir, UNREC_CLIPS_DIR, NULL);
6071       char *text = lives_strdup_printf(_("Some clips could not be recovered.\n"
6072                                          "These items can be deleted or moved to the directory\n%s\n"
6073                                          "What would you like to do with them ?"), unrecdir);
6074       LiVESWidget *dialog = create_question_dialog(_("Unrecoverable Clips"), text), *bbox, *cancelbutton;
6075 
6076       lives_free(text);
6077 
6078       cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, _("Ignore"),
6079                      LIVES_RESPONSE_CANCEL);
6080 
6081       lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_DELETE, _("Delete them"),
6082                                          LIVES_RESPONSE_NO);
6083 
6084       lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_SAVE, _("Move them"),
6085                                          LIVES_RESPONSE_OK);
6086 
6087       bbox = lives_dialog_get_action_area(LIVES_DIALOG(dialog));
6088       trash_rb(LIVES_BUTTON_BOX(bbox));
6089       lives_dialog_add_escape(LIVES_DIALOG(dialog), cancelbutton);
6090 
6091       resp = lives_dialog_run(LIVES_DIALOG(dialog));
6092       lives_widget_destroy(dialog);
6093       lives_widget_context_update();
6094       THREADVAR(com_failed) = FALSE;
6095 
6096       if (resp == LIVES_RESPONSE_CANCEL) return FALSE;
6097 
6098       if (resp == LIVES_RESPONSE_OK) {
6099         // move from workdir to workdir/unrec
6100         char *from, *to, *norem;
6101         char *ucdir = lives_build_path(prefs->workdir, UNREC_CLIPS_DIR, NULL);
6102         lives_mkdir_with_parents(ucdir, capable->umask);
6103         if (!lives_file_test(ucdir, LIVES_FILE_TEST_IS_DIR)) return FALSE;
6104         norem = lives_build_filename(ucdir, LIVES_FILENAME_NOREMOVE, NULL);
6105         lives_touch(norem);
6106         lives_free(norem);
6107         for (; list; list = list->next) {
6108           from = lives_build_path(prefs->workdir, (char *)list->data, NULL);
6109           to = lives_build_path(ucdir, (char *)list->data, NULL);
6110           lives_mv(from, to);
6111           lives_free(from); lives_free(to);
6112           if (THREADVAR(com_failed)) {
6113             THREADVAR(com_failed) = FALSE;
6114             return FALSE;
6115           }
6116         }
6117         lives_free(ucdir);
6118       } else {
6119         char *tfile;
6120         lives_file_dets_t *fdets;
6121         // touch files in remdir, also prepend to remdir so we dont skip removal
6122         for (; list; list = list->next) {
6123           tfile = lives_build_filename(trashremdir, (char *)list->data, NULL);
6124           lives_touch(tfile);
6125           lives_free(tfile);
6126           if (THREADVAR(com_failed)) {
6127             THREADVAR(com_failed) = FALSE;
6128             return FALSE;
6129           }
6130           fdets = (lives_file_dets_t *)struct_from_template(LIVES_STRUCT_FILE_DETS_T);
6131           fdets->name = lives_strdup((char *)list->data);
6132           *rem_list = lives_list_prepend(*rem_list, fdets);
6133         }
6134       }
6135       return TRUE;
6136     }
6137 
6138 
6139     void on_cleardisk_activate(LiVESWidget * widget, livespointer user_data) {
6140       // recover disk space
6141       lives_file_dets_t *filedets;
6142       lives_proc_thread_t tinfo;
6143       LiVESTextBuffer *tbuff;
6144       LiVESWidget *top_vbox;
6145 
6146       LiVESList *lists[3];
6147       LiVESList **left_list, **rec_list, **rem_list;
6148       LiVESList *list;
6149 
6150       int64_t bytes = 0, fspace = -1;
6151       int64_t ds_warn_level = mainw->next_ds_warn_level;
6152 
6153       uint64_t ukey;
6154 
6155       char *uidgid;
6156       char *trashdir = NULL, *full_trashdir = NULL;
6157 
6158       char *markerfile, *filedir;
6159       char *com, *msg, *tmp;
6160       char *extra = lives_strdup("");
6161 
6162       LiVESResponseType retval = LIVES_RESPONSE_NONE, resp;
6163 
6164       boolean gotsize = FALSE;
6165 
6166       int current_file = mainw->current_file;
6167       int marker_fd;
6168       int i, ntok, nitems = 0;
6169 
6170       mainw->mt_needs_idlefunc = FALSE;
6171 
6172       mainw->next_ds_warn_level = 0; /// < avoid nested warnings
6173 
6174       if (user_data) lives_widget_hide(lives_widget_get_toplevel(LIVES_WIDGET(user_data)));
6175 
6176       rec_list = &lists[0];
6177       rem_list = &lists[1];
6178       left_list = &lists[2];
6179 
6180       *rec_list = *rem_list = *left_list = NULL;
6181 
6182       mainw->tried_ds_recover = TRUE; ///< indicates we tried ds recovery already
6183       mainw->add_clear_ds_adv = TRUE; ///< auto reset by do_warning_dialog()
6184       if (!(prefs->clear_disk_opts & LIVES_CDISK_REMOVE_ORPHAN_CLIPS)) {
6185         lives_free(extra);
6186         extra = (_("\n\nIf potential missing clips are detected, you will be provided "
6187                    "the option to try to recover them\n"
6188                    "before they are removed permanently from the disk.\n\n"
6189                    "<b>You will also have an opportunity to view and revise the list of items to be removed "
6190                    "before continuing.</b>\n"));
6191       }
6192       widget_opts.use_markup = TRUE;
6193       if (!do_warning_dialogf(
6194             _("LiVES will attempt to recover some disk space.\n"
6195               "Unnecessary files will be removed from %s\n"
6196               "You should <b>ONLY</b> run this if you have no other copies of LiVES running on this machine.\n"
6197               "%s\nClick OK to proceed.\n"), tmp = lives_markup_escape_text(prefs->workdir, -1), extra)) {
6198         widget_opts.use_markup = FALSE;
6199         lives_free(tmp);
6200         lives_free(extra);
6201         mainw->next_ds_warn_level = ds_warn_level;
6202         goto end;
6203       }
6204       widget_opts.use_markup = FALSE;
6205       lives_free(extra);
6206 
6207       if (CURRENT_CLIP_IS_VALID) cfile->cb_src = current_file;
6208 
6209       lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
6210 
6211       d_print(_("Cleaning up disk space..."));
6212       // get a temporary clip for receiving data from backend
6213       if (!get_temp_handle(-1)) {
6214         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
6215         d_print_failed();
6216         mainw->next_ds_warn_level = ds_warn_level;
6217         return;
6218       }
6219 
6220       if (mainw->multitrack) {
6221         if (mainw->multitrack->idlefunc > 0) {
6222           lives_source_remove(mainw->multitrack->idlefunc);
6223           mainw->multitrack->idlefunc = 0;
6224           mainw->mt_needs_idlefunc = TRUE;
6225         }
6226         mt_desensitise(mainw->multitrack);
6227       }
6228 
6229       ukey = gen_unique_id();
6230       uidgid = lives_strdup_printf("-%d-%d", lives_getuid(), lives_getgid());
6231       trashdir = lives_strdup_printf("%s%lu%s", TRASH_NAME, ukey, uidgid);
6232 
6233       for (i = 0; i < MAX_FILES; i++) {
6234         // mark all free-floating files (directories) which we do not want to remove
6235         // we do error checking here
6236         if (mainw->files[i] && *mainw->files[i]->handle) {
6237           filedir = lives_build_path(prefs->workdir, mainw->files[i]->handle, NULL);
6238           if (lives_file_test(filedir, LIVES_FILE_TEST_IS_DIR)) {
6239             markerfile = lives_build_filename(filedir, LIVES_FILENAME_INUSE, NULL);
6240             lives_echo(trashdir, markerfile, FALSE);
6241             lives_free(markerfile);
6242             if (mainw->files[i]->undo_action != UNDO_NONE) {
6243               markerfile = lives_build_filename(filedir, LIVES_FILENAME_NOCLEAN, NULL);
6244               do {
6245                 retval = LIVES_RESPONSE_NONE;
6246                 marker_fd = creat(markerfile, S_IRUSR | S_IWUSR);
6247                 if (marker_fd < 0) {
6248                   retval = do_write_failed_error_s_with_retry(markerfile, lives_strerror(errno));
6249                 }
6250               } while (retval == LIVES_RESPONSE_RETRY);
6251               if (marker_fd >= 0) close(marker_fd);
6252               lives_free(markerfile);
6253               if (retval == LIVES_RESPONSE_CANCEL) goto cleanup;
6254 	  // *INDENT-OFF*
6255 	}}}}
6256   // *INDENT-ON*
6257 
6258       full_trashdir = lives_build_path(prefs->workdir, trashdir, NULL);
6259 
6260       do {
6261         resp = LIVES_RESPONSE_NONE;
6262         if (!lives_make_writeable_dir(full_trashdir) || !check_dir_access(full_trashdir, TRUE)) {
6263           resp = do_dir_perm_error(full_trashdir, TRUE);
6264           if (resp == LIVES_RESPONSE_CANCEL) goto cleanup;
6265         }
6266       } while (resp == LIVES_RESPONSE_RETRY);
6267 
6268       // get space before
6269       fspace = get_ds_free(prefs->workdir);
6270 
6271       //if (prefs->autoclean) {
6272       // clearup old
6273       mainw->cancelled = CANCEL_NONE;
6274 
6275       com = lives_strdup_printf("%s empty_trash %s general %s", prefs->backend, cfile->handle,
6276                                 TRASH_NAME);
6277       lives_rm(cfile->info_file);
6278       lives_system(com, FALSE);
6279       lives_free(com);
6280       do_auto_dialog(_("Removing general trash"), 0);
6281 
6282       THREADVAR(com_failed) = FALSE;
6283 
6284       if (CURRENT_CLIP_IS_VALID) lives_rm(cfile->info_file);
6285 
6286       tinfo = lives_proc_thread_create(LIVES_THRDATTR_NONE, (lives_funcptr_t)do_auto_dialog, -1,
6287                                        "si", _("Analysing Disk"), 0);
6288       tbuff = lives_text_buffer_new();
6289       com = lives_strdup_printf("%s disk_check %s %u %s", prefs->backend, cfile->handle,
6290                                 prefs->clear_disk_opts, trashdir);
6291       lives_free(uidgid);
6292       lives_popen(com, TRUE, (char *)tbuff, 0);
6293       lives_free(com);
6294 
6295       lives_proc_thread_join(tinfo);
6296 
6297       if (*mainw->msg && (ntok = get_token_count(mainw->msg, '|')) > 1) {
6298         char **array = lives_strsplit(mainw->msg, "|", 2);
6299         if (!strcmp(array[0], "completed")) {
6300           nitems = atoi(array[1]);
6301         }
6302         lives_strfreev(array);
6303       }
6304 
6305       // remove the protective markers
6306       for (i = 0; i < MAX_FILES; i++) {
6307         if (mainw->files[i] && *mainw->files[i]->handle) {
6308           filedir = lives_build_path(prefs->workdir, mainw->files[i]->handle, NULL);
6309           if (lives_file_test(filedir, LIVES_FILE_TEST_IS_DIR)) {
6310             markerfile = lives_build_filename(prefs->workdir, mainw->files[i]->handle,
6311                                               LIVES_FILENAME_INUSE, NULL);
6312             lives_rm(markerfile);
6313             lives_free(markerfile);
6314             if (mainw->files[i]->undo_action != UNDO_NONE) {
6315               markerfile = lives_build_filename(prefs->workdir, mainw->files[i]->handle,
6316                                                 LIVES_FILENAME_NOCLEAN, NULL);
6317               lives_rm(markerfile);
6318               lives_free(markerfile);
6319 	  // *INDENT-OFF*
6320 	}}}}
6321   // *INDENT-ON*
6322 
6323       if (THREADVAR(com_failed)) {
6324         THREADVAR(com_failed) = FALSE;
6325       } else {
6326         LiVESAccelGroup *accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
6327         LiVESWidget *button, *accb, *hbox, *label;
6328         lives_proc_thread_t recinfo, reminfo, leaveinfo;
6329         char *remtrashdir, *op, *from, *to;
6330         int orig;
6331 
6332         if (nitems) {
6333           char *dirname = lives_build_path(full_trashdir, TRASH_RECOVER, NULL);
6334           recinfo =
6335             dir_to_file_details(rec_list, dirname, prefs->workdir,
6336                                 EXTRA_DETAILS_CLIPHDR);
6337           lives_free(dirname);
6338 
6339           dirname = lives_build_path(full_trashdir, TRASH_REMOVE, NULL);
6340           reminfo =
6341             dir_to_file_details(rem_list, dirname, prefs->workdir,
6342                                 EXTRA_DETAILS_EMPTY_DIRS | EXTRA_DETAILS_DIRSIZE);
6343           lives_free(dirname);
6344 
6345           dirname = lives_build_path(full_trashdir, TRASH_LEAVE, NULL);
6346           leaveinfo =
6347             dir_to_file_details(left_list, dirname, prefs->workdir,
6348                                 EXTRA_DETAILS_EMPTY_DIRS | EXTRA_DETAILS_DIRSIZE);
6349           lives_free(dirname);
6350         } else {
6351           *rec_list = lives_list_append(*rec_list, NULL);
6352           *rem_list = lives_list_append(*rem_list, NULL);
6353           *left_list = lives_list_append(*left_list, NULL);
6354         }
6355 
6356         widget_opts.expand = LIVES_EXPAND_EXTRA_WIDTH;
6357         textwindow = create_text_window(_("Disk Analysis Log"), NULL, tbuff, FALSE);
6358         widget_opts.expand = LIVES_EXPAND_DEFAULT;;
6359 
6360         lives_window_add_accel_group(LIVES_WINDOW(textwindow->dialog), accel_group);
6361 
6362         top_vbox = lives_dialog_get_content_area(LIVES_DIALOG(textwindow->dialog));
6363 
6364         hbox = lives_hbox_new(FALSE, 0);
6365         lives_box_pack_start(LIVES_BOX(top_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
6366         msg = lives_strdup_printf(_("\nAnalysis of directory %s complete.\n\n"), prefs->workdir);
6367         label = lives_standard_label_new(msg);
6368         lives_free(msg);
6369 
6370         lives_box_pack_start(LIVES_BOX(hbox), label, TRUE, TRUE, 0);
6371         lives_widget_set_halign(label, LIVES_ALIGN_CENTER);
6372 
6373         if (!nitems) {
6374           hbox = lives_hbox_new(FALSE, 0);
6375           lives_box_pack_start(LIVES_BOX(top_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
6376           msg = _("No items to be removed or recovered were detected.\n");
6377           label = lives_standard_label_new(msg);
6378           lives_free(msg);
6379 
6380           lives_box_pack_start(LIVES_BOX(hbox), label, TRUE, TRUE, 0);
6381           lives_widget_set_halign(label, LIVES_ALIGN_CENTER);
6382         }
6383 
6384         lives_widget_object_ref(textwindow->vbox);
6385         lives_widget_unparent(textwindow->vbox);
6386 
6387         widget_opts.justify = LIVES_JUSTIFY_CENTER;
6388         lives_standard_expander_new(_("Show _Log"), LIVES_BOX(top_vbox),
6389                                     textwindow->vbox);
6390         widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
6391         lives_widget_object_unref(textwindow->vbox);
6392 
6393         add_fill_to_box(LIVES_BOX(top_vbox));
6394 
6395         button =
6396           lives_dialog_add_button_from_stock(LIVES_DIALOG(textwindow->dialog),
6397                                              LIVES_STOCK_CANCEL, nitems ? NULL :
6398                                              LIVES_STOCK_LABEL_CLOSE_WINDOW, LIVES_RESPONSE_CANCEL);
6399 
6400         lives_widget_add_accelerator(button, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
6401                                      LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
6402 
6403         widget_opts.expand = LIVES_EXPAND_DEFAULT_HEIGHT | LIVES_EXPAND_EXTRA_WIDTH;
6404         accb = lives_dialog_add_button_from_stock(LIVES_DIALOG(textwindow->dialog),
6405                LIVES_STOCK_EDIT, nitems ? _("_Check and Filter Results")
6406                : _("Show Results"), LIVES_RESPONSE_BROWSE);
6407 
6408         widget_opts.expand = LIVES_EXPAND_DEFAULT;
6409         lives_button_grab_default_special(accb);
6410 
6411         if (nitems) {
6412           LiVESWidget *bbox = lives_dialog_get_action_area(LIVES_DIALOG(textwindow->dialog));
6413           lives_button_box_set_child_non_homogeneous(LIVES_BUTTON_BOX(bbox), button, TRUE);
6414           lives_button_box_set_layout(LIVES_BUTTON_BOX(bbox), LIVES_BUTTONBOX_START);
6415         }
6416 
6417         retval = lives_dialog_run(LIVES_DIALOG(textwindow->dialog));
6418         lives_widget_destroy(textwindow->dialog);
6419         lives_free(textwindow);
6420 
6421         if (retval != LIVES_RESPONSE_CANCEL) {
6422           retval = filter_cleanup(full_trashdir, rec_list, rem_list, left_list);
6423         }
6424 
6425         if (!nitems) {
6426           gotsize = TRUE;
6427           goto cleanup;
6428         }
6429 
6430         lives_proc_thread_cancel(recinfo);
6431         lives_proc_thread_cancel(reminfo);
6432         lives_proc_thread_cancel(leaveinfo);
6433 
6434         if (retval == LIVES_RESPONSE_CANCEL) {
6435           com = lives_strdup_printf("%s restore_trash %s", prefs->backend, trashdir);
6436           lives_system(com, FALSE);
6437           lives_free(com);
6438           goto cleanup;
6439         }
6440 
6441         /// user accepted
6442 
6443         // first we need to move some entries at the list starts
6444         // type now indicates the origin list, since moved entries were prepended
6445         // we can stop when orig == current list
6446 
6447         THREADVAR(com_failed) = FALSE;
6448 
6449         for (list = *rem_list; list && list->data; list = list->next) {
6450           filedets = (lives_file_dets_t *)list->data;
6451           orig = filedets->type & ~LIVES_FILE_TYPE_FLAG_SPECIAL;
6452           if (orig == 1) break;
6453           to = lives_build_path(full_trashdir, TRASH_REMOVE, filedets->name, NULL);
6454           if (!orig) {
6455             // moved from rec_list to rem_list
6456             from = lives_build_path(full_trashdir, TRASH_RECOVER, filedets->name, NULL);
6457           } else {
6458             // moved from left_list to rem_list
6459             from = lives_build_path(full_trashdir, TRASH_LEAVE, filedets->name, NULL);
6460           }
6461           lives_mv(from, to);
6462           lives_free(from);
6463           lives_free(to);
6464           if (THREADVAR(com_failed)) {
6465             THREADVAR(com_failed) = FALSE;
6466             goto cleanup;
6467           }
6468         }
6469 
6470         for (list = *left_list; list && list->data; list = list->next) {
6471           filedets = (lives_file_dets_t *)list->data;
6472           orig = filedets->type;
6473           if (orig == 2) break;
6474           to = lives_build_path(full_trashdir, TRASH_LEAVE, filedets->name, NULL);
6475           if (!orig) {
6476             // moved from rec_list to left_list
6477             from = lives_build_path(full_trashdir, TRASH_RECOVER, filedets->name, NULL);
6478           } else {
6479             // moved from rem_list to left_list
6480             from = lives_build_path(full_trashdir, TRASH_REMOVE, filedets->name, NULL);
6481           }
6482           lives_mv(from, to);
6483           lives_free(from);
6484           lives_free(to);
6485           if (THREADVAR(com_failed)) {
6486             THREADVAR(com_failed) = FALSE;
6487             goto cleanup;
6488           }
6489         }
6490 
6491         list = *rec_list;
6492 
6493         if (list && list->data) {
6494           /// try to recover lost files first
6495           // create a list with just the names
6496           LiVESList *recnlist = NULL;
6497 
6498           for (; list && list->data; list = list->next) {
6499             filedets = (lives_file_dets_t *)list->data;
6500             if (*filedets->name)
6501               recnlist = lives_list_prepend(recnlist, lives_strdup(filedets->name));
6502           }
6503           recnlist = lives_list_reverse(recnlist);
6504 
6505           // close the temporary clip
6506           close_temp_handle(current_file);
6507 
6508           lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
6509           recover_lost_clips(recnlist);
6510           lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
6511 
6512           /// handle any remnants. Remove any entries from recnlist which are now loaded
6513           /// the remainder will be deleted or moved to "unrecoverable_clips"
6514 
6515           for (list = mainw->cliplist; list; list = list->next) {
6516             int clipno = LIVES_POINTER_TO_INT(list->data);
6517             LiVESList *list2 = recnlist;
6518             for (; list2; list2 = list2->next) {
6519               if (!strcmp(mainw->files[clipno]->handle, (char *)list2->data)) {
6520                 if (list2->prev) list2->prev->next = list2->next;
6521                 if (list2->next) list2->next->prev = list2->prev;
6522                 if (recnlist == list2) recnlist = list2->next;
6523                 list2->next = list2->prev = NULL;
6524                 lives_list_free(list2);
6525                 break;
6526               }
6527             }
6528           }
6529 
6530           current_file = mainw->current_file;
6531 
6532           // get a temporary clip for receiving data from backend
6533           if (!get_temp_handle(-1)) {
6534             lives_list_free_all(&recnlist);
6535             mainw->next_ds_warn_level = ds_warn_level;
6536             goto cleanup;
6537           }
6538 
6539           if (recnlist) {
6540             boolean bresp;
6541             remtrashdir = lives_build_path(full_trashdir, TRASH_REMOVE, NULL);
6542 
6543             /// handle unrecovered items
6544             lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
6545             bresp = handle_remnants(recnlist, remtrashdir, rem_list);
6546             lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
6547             if (!bresp) {
6548               // if failed / cancelled, add to left_list
6549               lives_file_dets_t *fdets;
6550               for (list = recnlist; list; list = list->next) {
6551                 fdets = (lives_file_dets_t *)struct_from_template(LIVES_STRUCT_FILE_DETS_T);
6552                 fdets->name = lives_strdup((char *)list->data);
6553                 *left_list = lives_list_prepend(*left_list, fdets);
6554               }
6555             }
6556             lives_list_free_all(&recnlist);
6557             lives_free(remtrashdir);
6558           }
6559         }
6560         // now finally we remove all in rem_list, this is done in the backend
6561         list = *rem_list;
6562         if (list && list->data) {
6563           if (prefs->pref_trash) op = lives_strdup("giotrash");
6564           else op = lives_strdup("delete");
6565 
6566           remtrashdir = lives_build_path(trashdir, TRASH_REMOVE, NULL);
6567 
6568           if (CURRENT_CLIP_IS_VALID) lives_rm(cfile->info_file);
6569 
6570           tinfo = lives_proc_thread_create(LIVES_THRDATTR_NONE, (lives_funcptr_t)do_auto_dialog, -1,
6571                                            "si", _("Clearing Disk"), 0);
6572           tbuff = lives_text_buffer_new();
6573 
6574           com = lives_strdup_printf("%s empty_trash \"%s\" %s \"%s\"",
6575                                     prefs->backend, cfile->handle, op, remtrashdir);
6576           lives_free(op);
6577           lives_free(remtrashdir);
6578 
6579           lives_popen(com, TRUE, (char *)tbuff, 0);
6580           lives_free(com);
6581           lives_proc_thread_join(tinfo);
6582 
6583           lives_rm(cfile->info_file);
6584         }
6585 
6586         if (THREADVAR(com_failed)) {
6587           THREADVAR(com_failed) = FALSE;
6588           goto cleanup;
6589         }
6590 
6591         bytes = get_ds_free(prefs->workdir) - fspace;
6592         gotsize = TRUE;
6593       }
6594 
6595 cleanup:
6596 
6597       if (trashdir) lives_free(trashdir);
6598 
6599       if (full_trashdir) {
6600         lives_rmdir(full_trashdir, TRUE);
6601         lives_free(full_trashdir);
6602       }
6603 
6604       if (*rec_list) free_fdets_list(rec_list);
6605 
6606       // close the temporary clip
6607       close_temp_handle(current_file);
6608 
6609       if (bytes < 0) bytes = 0;
6610       lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
6611 
6612       if (gotsize && retval != LIVES_RESPONSE_CANCEL && !THREADVAR(com_failed) && fspace > -1) {
6613         LiVESWidget *dialog, *tview;
6614 
6615         d_print_done();
6616 
6617         msg = lives_strdup_printf(_("%s of disk space was recovered.\n"),
6618                                   lives_format_storage_space_string((uint64_t)bytes));
6619 
6620         dialog = create_message_dialog(LIVES_DIALOG_INFO, msg, 0);
6621         lives_free(msg);
6622 
6623         list = *rem_list;
6624         top_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
6625 
6626         if (list && list->data) {
6627           lives_label_chomp(LIVES_LABEL(widget_opts.last_label));
6628           widget_opts.expand = LIVES_EXPAND_DEFAULT_WIDTH;
6629           tview = scrolled_textview(NULL, tbuff, RFX_WINSIZE_H * 2, NULL);
6630           widget_opts.expand = LIVES_EXPAND_DEFAULT;
6631           widget_opts.justify = LIVES_JUSTIFY_CENTER;
6632           lives_standard_expander_new(_("Show _Log"), LIVES_BOX(top_vbox), tview);
6633           widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
6634         }
6635 
6636         list = *left_list;
6637 
6638         if (list && list->data) {
6639           char *text = NULL, *item;
6640           LiVESWidget *label = lives_standard_label_new(_("Some files and directories may be removed manually "
6641                                "if desired.\nClick for details:\n"));
6642           LiVESWidget *hbox = lives_hbox_new(FALSE, 0);
6643 
6644           lives_box_pack_start(LIVES_BOX(top_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
6645           lives_box_pack_start(LIVES_BOX(hbox), label, TRUE, TRUE, 0);
6646           lives_widget_set_halign(label, LIVES_ALIGN_CENTER);
6647 
6648           for (; list && list->data; list = list->next) {
6649             filedets = (lives_file_dets_t *)list->data;
6650             item = lives_build_path(prefs->workdir, filedets->name, NULL);
6651             text = lives_concat_sep(text, "\n", item);
6652           }
6653 
6654           widget_opts.expand = LIVES_EXPAND_DEFAULT_WIDTH;
6655           tview = scrolled_textview(text, NULL, RFX_WINSIZE_H * 2, NULL);
6656           widget_opts.expand = LIVES_EXPAND_DEFAULT;
6657           lives_free(text);
6658 
6659           widget_opts.justify = LIVES_JUSTIFY_CENTER;
6660           lives_standard_expander_new(_("Show _Remaining Items"),
6661                                       LIVES_BOX(top_vbox), tview);
6662           widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
6663         }
6664 
6665         lives_dialog_run(LIVES_DIALOG(dialog));
6666       } else {
6667         if (retval != LIVES_RESPONSE_CANCEL) d_print_failed();
6668         else d_print_cancelled();
6669       }
6670 
6671       if (*rem_list) free_fdets_list(rem_list);
6672       if (*left_list) free_fdets_list(left_list);
6673 
6674       mainw->next_ds_warn_level = ds_warn_level;
6675 
6676       if (user_data) {
6677         mainw->dsu_valid = FALSE;
6678         if (!disk_monitor_running(prefs->workdir)) {
6679           mainw->dsu_valid = TRUE;
6680           lives_idle_add_simple(update_dsu, NULL);
6681         }
6682         lives_widget_show(lives_widget_get_toplevel(LIVES_WIDGET(user_data)));
6683       } else {
6684         if (!mainw->multitrack && !mainw->is_processing && !LIVES_IS_PLAYING) {
6685           sensitize();
6686         }
6687 
6688 
6689 end:
6690         if (mainw->multitrack) {
6691           if (!mainw->is_processing && !LIVES_IS_PLAYING) {
6692             mt_sensitise(mainw->multitrack);
6693             maybe_add_mt_idlefunc();
6694 	// *INDENT-OFF*
6695       }}}
6696   // *INDENT-ON*
6697     }
6698 
6699 
6700     void on_cleardisk_advanced_clicked(LiVESWidget * widget, livespointer user_data) {
6701       // make cleardisk adv window
6702 
6703       // show various options and OK/Cancel button
6704 
6705       // on OK set clear_disk opts
6706       int response;
6707       LiVESWidget *dialog;
6708       do {
6709         dialog = create_cleardisk_advanced_dialog();
6710         lives_widget_show_all(dialog);
6711         response = lives_dialog_run(LIVES_DIALOG(dialog));
6712         lives_widget_destroy(dialog);
6713         if (response == LIVES_RESPONSE_RETRY) prefs->clear_disk_opts = 0;
6714       } while (response == LIVES_RESPONSE_RETRY);
6715 
6716       set_int_pref(PREF_CLEAR_DISK_OPTS, prefs->clear_disk_opts);
6717     }
6718 
6719 
6720     void on_show_keys_activate(LiVESMenuItem * menuitem, livespointer user_data) {do_keys_window();}
6721 
6722 
6723     void on_vj_realize_activate(LiVESMenuItem * menuitem, livespointer user_data) {
6724       frames_t ret;
6725       char *msg = (_("Pre-decoding all frames in this clip..."));
6726       d_print(msg);
6727 
6728       desensitize();
6729 
6730       ret = realize_all_frames(mainw->current_file, msg, TRUE);
6731       lives_free(msg);
6732       if (ret <= 0) d_print_failed();
6733       else if (ret < cfile->frames) d_print_enough(ret);
6734       else d_print_done();
6735 
6736       sensitize();
6737     }
6738 
6739 
6740     void on_vj_reset_activate(LiVESMenuItem * menuitem, livespointer user_data) {
6741       LiVESList *clip_list = mainw->cliplist;
6742 
6743       boolean bad_header = FALSE;
6744 
6745       int i;
6746 
6747       //mainw->soft_debug=TRUE;
6748 
6749       do_threaded_dialog(_("Resetting frame rates and frame values..."), FALSE);
6750 
6751       while (clip_list) {
6752         i = LIVES_POINTER_TO_INT(clip_list->data);
6753         mainw->files[i]->pb_fps = mainw->files[i]->fps;
6754         mainw->files[i]->frameno = 1;
6755         mainw->files[i]->aseek_pos = 0;
6756 
6757         if (!save_clip_value(i, CLIP_DETAILS_PB_FPS, &mainw->files[i]->fps)) bad_header = TRUE;
6758         if (!save_clip_value(i, CLIP_DETAILS_PB_FRAMENO, &mainw->files[i]->frameno)) bad_header = TRUE;
6759 
6760         threaded_dialog_spin((double)i / (double)mainw->clips_available);
6761 
6762         if (bad_header) {
6763           if (!do_header_write_error(i)) break;
6764         } else clip_list = clip_list->next;
6765       }
6766 
6767       end_threaded_dialog();
6768     }
6769 
6770 
6771     void on_show_messages_activate(LiVESMenuItem * menuitem, livespointer user_data) {
6772       do_messages_window(FALSE);
6773     }
6774 
6775 
6776     void on_show_file_info_activate(LiVESMenuItem * menuitem, livespointer user_data) {
6777       char buff[512];
6778       lives_clipinfo_t *filew;
6779 
6780       char *sigs, *ends, *tmp;
6781 
6782       if (!CURRENT_CLIP_IS_VALID) return;
6783 
6784       filew = create_clip_info_window(cfile->achans, FALSE);
6785 
6786       if (cfile->frames > 0) {
6787         // type
6788         lives_snprintf(buff, 512, _("External: %s\nInternal: %s (%d bpp) / %s"), cfile->type,
6789                        (tmp = lives_strdup((cfile->clip_type == CLIP_TYPE_YUV4MPEG ||
6790                                             cfile->clip_type == CLIP_TYPE_VIDEODEV) ? (_("buffered")) :
6791                                            (cfile->img_type == IMG_TYPE_JPEG ? LIVES_IMAGE_TYPE_JPEG : LIVES_IMAGE_TYPE_PNG))),
6792                        cfile->bpp, LIVES_AUDIO_TYPE_PCM);
6793         lives_free(tmp);
6794 
6795         if (cfile->clip_type == CLIP_TYPE_FILE) {
6796           lives_decoder_t *dplug = (lives_decoder_t *)cfile->ext_src;
6797           lives_decoder_sys_t *dpsys = (lives_decoder_sys_t *)dplug->decoder;
6798           const char *decname = dpsys->name;
6799           lives_strappendf(buff, 512, _("\ndecoder: %s"), decname);
6800         }
6801         lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_type), buff, -1);
6802         // fps
6803         lives_snprintf(buff, 512, "  %.3f%s", cfile->fps, cfile->ratio_fps ? "..." : "");
6804 
6805         lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_fps), buff, -1);
6806         // image size
6807         lives_snprintf(buff, 512, "  %dx%d", cfile->hsize, cfile->vsize);
6808         lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_size), buff, -1);
6809         // frames
6810         if ((cfile->opening && !cfile->opening_audio && cfile->frames == 0) || cfile->frames == 123456789) {
6811           lives_snprintf(buff, 512, "%s", _("  Opening..."));
6812         } else {
6813           lives_snprintf(buff, 512, "  %d", cfile->frames);
6814 
6815           if (cfile->frame_index) {
6816             int fvirt = count_virtual_frames(cfile->frame_index, 1, cfile->frames);
6817             char *tmp = lives_strdup_printf(_("\n(%d virtual)"), fvirt);
6818             lives_strappend(buff, 512, tmp);
6819             lives_free(tmp);
6820             tmp = lives_strdup_printf(_("\n(%d decoded)"), cfile->frames - fvirt);
6821             lives_strappend(buff, 512, tmp);
6822             lives_free(tmp);
6823           }
6824 
6825         }
6826         lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_frames), buff, -1);
6827         // video time
6828         if ((cfile->opening && !cfile->opening_audio && cfile->frames == 0) || cfile->frames == 123456789) {
6829           lives_snprintf(buff, 512, "%s", _("  Opening..."));
6830         } else {
6831           lives_snprintf(buff, 512, _("  %.2f sec."), cfile->video_time);
6832         }
6833         lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_vtime), buff, -1);
6834         // file size
6835         if (cfile->f_size > 0l) {
6836           char *file_ds = lives_format_storage_space_string((uint64_t)cfile->f_size);
6837           lives_snprintf(buff, 512, "  %s", file_ds);
6838           lives_free(file_ds);
6839         } else lives_snprintf(buff, 512, "%s", _("  Unknown"));
6840         lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_fsize), buff, -1);
6841       }
6842 
6843       if (cfile->achans > 0) {
6844         if (cfile->opening) {
6845           lives_snprintf(buff, 512, "%s", _("  Opening..."));
6846         } else {
6847           lives_snprintf(buff, 512, _("  %.2f sec."), cfile->laudio_time);
6848         }
6849         lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_ltime), buff, -1);
6850 
6851         if (cfile->signed_endian & AFORM_UNSIGNED) sigs = (_("unsigned"));
6852         else sigs = (_("signed"));
6853 
6854         if (cfile->signed_endian & AFORM_BIG_ENDIAN) ends = (_("big-endian"));
6855         else ends = (_("little-endian"));
6856 
6857         lives_snprintf(buff, 512, _("  %d Hz %d bit\n%s %s"), cfile->arate, cfile->asampsize, sigs, ends);
6858         lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_lrate), buff, -1);
6859 
6860         lives_free(sigs);
6861         lives_free(ends);
6862       }
6863 
6864       if (cfile->achans > 1) {
6865         if (cfile->signed_endian & AFORM_UNSIGNED) sigs = (_("unsigned"));
6866         else sigs = (_("signed"));
6867 
6868         if (cfile->signed_endian & AFORM_BIG_ENDIAN) ends = (_("big-endian"));
6869         else ends = (_("little-endian"));
6870 
6871         lives_snprintf(buff, 512, _("  %d Hz %d bit\n%s %s"), cfile->arate, cfile->asampsize, sigs, ends);
6872         lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_rrate), buff, -1);
6873 
6874         lives_free(sigs);
6875         lives_free(ends);
6876 
6877         if (cfile->opening) {
6878           lives_snprintf(buff, 512, "%s", _("  Opening..."));
6879         } else {
6880           lives_snprintf(buff, 512, _("  %.2f sec."), cfile->raudio_time);
6881         }
6882         lives_text_view_set_text(LIVES_TEXT_VIEW(filew->textview_rtime), buff, -1);
6883       }
6884     }
6885 
6886 
6887     void on_show_file_comments_activate(LiVESMenuItem * menuitem, livespointer user_data) {
6888       do_comments_dialog(mainw->current_file, NULL);
6889     }
6890 
6891 
6892     void on_show_clipboard_info_activate(LiVESMenuItem * menuitem, livespointer user_data) {
6893       int current_file = mainw->current_file;
6894       mainw->current_file = 0;
6895       on_show_file_info_activate(menuitem, user_data);
6896       mainw->current_file = current_file;
6897     }
6898 
6899 
6900     void switch_clip(int type, int newclip, boolean force) {
6901       // generic switch clip callback
6902 
6903       // This is the new single entry function for switching clips.
6904       // It should eventually replace switch_to_file() and do_quick_switch()
6905 
6906       // type = 0 : if we are playing and a transition is active, this will change the background clip
6907       // type = 1 fg only
6908       // type = 2 bg only
6909 
6910       if (mainw->current_file < 1 || mainw->multitrack || mainw->preview || mainw->internal_messaging ||
6911           (mainw->is_processing && cfile && cfile->is_loaded) || !mainw->cliplist) return;
6912 
6913       mainw->blend_palette = WEED_PALETTE_END;
6914 
6915       if (type == 2 || (mainw->active_sa_clips == SCREEN_AREA_BACKGROUND && mainw->playing_file > 0 && type != 1
6916                         && !(mainw->blend_file != -1 && !IS_NORMAL_CLIP(mainw->blend_file) && mainw->blend_file != mainw->playing_file))) {
6917         if (mainw->num_tr_applied < 1 || newclip == mainw->blend_file) return;
6918 
6919         // switch bg clip
6920         if (IS_VALID_CLIP(mainw->blend_file) && mainw->blend_file != mainw->playing_file
6921             && mainw->files[mainw->blend_file]->clip_type == CLIP_TYPE_GENERATOR) {
6922           if (mainw->blend_layer) check_layer_ready(mainw->blend_layer);
6923           weed_plant_t *inst = mainw->files[mainw->blend_file]->ext_src;
6924           if (inst) {
6925             mainw->osc_block = TRUE;
6926             if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) {
6927               int key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
6928               rte_key_on_off(key + 1, FALSE);
6929             }
6930             mainw->osc_block = FALSE;
6931           }
6932         }
6933 
6934         //chill_decoder_plugin(mainw->blend_file);
6935         mainw->blend_file = newclip;
6936         mainw->whentostop = NEVER_STOP;
6937         if (mainw->ce_thumbs && mainw->active_sa_clips == SCREEN_AREA_BACKGROUND) {
6938           ce_thumbs_highlight_current_clip();
6939         }
6940         mainw->blend_palette = WEED_PALETTE_END;
6941         return;
6942       }
6943 
6944       // switch fg clip
6945 
6946       if (!force && (newclip == mainw->current_file && (!LIVES_IS_PLAYING || mainw->playing_file == newclip))) return;
6947       if (cfile && !cfile->is_loaded) mainw->cancelled = CANCEL_NO_PROPOGATE;
6948 
6949       if (LIVES_IS_PLAYING) {
6950         mainw->new_clip = newclip;
6951         mainw->blend_palette = WEED_PALETTE_END;
6952       } else {
6953         if (!cfile || (force && newclip == mainw->current_file)) mainw->current_file = -1;
6954         switch_to_file(mainw->current_file, newclip);
6955       }
6956     }
6957 
6958 
6959     void switch_clip_activate(LiVESMenuItem * menuitem, livespointer user_data) {
6960       // switch clips from the clips menu
6961 
6962       register int i;
6963       if (mainw->current_file < 1 || mainw->preview || (mainw->is_processing && cfile->is_loaded) || !mainw->cliplist) return;
6964 
6965       for (i = 1; i < MAX_FILES; i++) {
6966         if (mainw->files[i]) {
6967           if (LIVES_MENU_ITEM(menuitem) == LIVES_MENU_ITEM(mainw->files[i]->menuentry) &&
6968               lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(mainw->files[i]->menuentry))) {
6969             switch_clip(0, i, FALSE);
6970             return;
6971 	// *INDENT-OFF*
6972       }}}
6973   // *INDENT-ON*
6974     }
6975 
6976 
6977     void on_about_activate(LiVESMenuItem * menuitem, livespointer user_data) {
6978 
6979 #ifdef GUI_GTK
6980 #if GTK_CHECK_VERSION(2, 14, 0)
6981       char *license = (_(
6982                          "This program is free software; you can redistribute it and/or modify\n"
6983                          "it under the terms of the GNU General Public License as published by\n"
6984                          "the Free Software Foundation; either version 3 of the License, or\n"
6985                          "(at your option) any later version.\n"
6986                          "\n"
6987                          "This program is distributed in the hope that it will be useful,\n"
6988                          "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
6989                          "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
6990                          "GNU General Public License for more details.\n"
6991                          "\n"
6992                          "You should have received a copy of the GNU General Public License\n"
6993                          "along with this program; if not, write to the Free Software\n"
6994                          "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA.\n"));
6995 
6996       char *comments = (_("A video editor and VJ program."));
6997       char *title = (_("About LiVES"));
6998 
6999       char *translator_credits = (_("translator_credits"));
7000 
7001 #if GTK_CHECK_VERSION(3, 0, 0)
7002       char *authors[2] = {LIVES_AUTHOR_EMAIL, NULL};
7003 #else
7004       gtk_about_dialog_set_url_hook(activate_url, NULL, NULL);
7005       gtk_about_dialog_set_email_hook(activate_url, NULL, NULL);
7006 #endif
7007 
7008       gtk_show_about_dialog(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET),
7009                             "logo", NULL,
7010                             "name", PACKAGE_NAME,
7011                             "version", LiVES_VERSION,
7012                             "comments", comments,
7013                             "copyright", "(C) "LIVES_COPYRIGHT_YEARS" salsaman <"LIVES_AUTHOR_EMAIL"> and others",
7014                             "website", LIVES_WEBSITE,
7015                             "license", license,
7016                             "title", title,
7017                             "translator_credits", translator_credits,
7018 #if GTK_CHECK_VERSION(3, 0, 0)
7019                             "authors", authors,
7020                             "license-type", GTK_LICENSE_GPL_3_0,
7021 #endif
7022                             NULL);
7023 
7024       lives_free(translator_credits);
7025       lives_free(comments);
7026       lives_free(title);
7027       lives_free(license);
7028       return;
7029 #endif
7030 #endif
7031       widget_opts.non_modal = TRUE;
7032       do_error_dialogf(_("LiVES Version %s\n"
7033                          "(c) G. Finch (salsaman) %s\n\n"
7034                          "Released under the GPL 3 or later (http://www.gnu.org/licenses/gpl.txt)\n"
7035                          "LiVES is distributed WITHOUT WARRANTY\n\n"
7036                          "Contact the author at:\n%s\n"
7037                          "Homepage: %s"),
7038                        LiVES_VERSION, LIVES_COPYRIGHT_YEARS, LIVES_AUTHOR_EMAIL, LIVES_WEBSITE);
7039       widget_opts.non_modal = FALSE;
7040     }
7041 
7042 
7043     void show_manual_activate(LiVESMenuItem * menuitem, livespointer user_data) {
7044       show_manual_section(NULL, NULL);
7045     }
7046 
7047 
7048     void email_author_activate(LiVESMenuItem * menuitem, livespointer user_data) {
7049       activate_url_inner("mailto:"LIVES_AUTHOR_EMAIL);
7050     }
7051 
7052 
7053     void report_bug_activate(LiVESMenuItem * menuitem, livespointer user_data) {
7054       activate_url_inner(LIVES_BUG_URL);
7055     }
7056 
7057 
7058     void suggest_feature_activate(LiVESMenuItem * menuitem, livespointer user_data) {
7059       activate_url_inner(LIVES_FEATURE_URL);
7060     }
7061 
7062 
7063     void help_translate_activate(LiVESMenuItem * menuitem, livespointer user_data) {
7064       activate_url_inner(LIVES_TRANSLATE_URL);
7065     }
7066 
7067 
7068     void donate_activate(LiVESMenuItem * menuitem, livespointer user_data) {
7069       const char *link = lives_strdup_printf("%s%s", LIVES_DONATE_URL, !user_data ? "" : (char *)user_data);
7070       activate_url_inner(link);
7071     }
7072 
7073 
7074     static char *fsp_ltext;
7075     static char *fsp_info_file;
7076     static char *file_open_params;
7077 
7078     static boolean fs_preview_idle(void *data) {
7079       LiVESWidget *widget = (LiVESWidget *)data;
7080       FILE *ifile;
7081 
7082       if (!(ifile = fopen(fsp_info_file, "r")) && mainw->in_fs_preview && mainw->fc_buttonresponse
7083           == LIVES_RESPONSE_NONE) {
7084         return TRUE;
7085       }
7086 
7087       widget_opts.expand = LIVES_EXPAND_NONE;
7088 
7089       if (LIVES_IS_BUTTON(widget))
7090         lives_button_set_label(LIVES_BUTTON(widget), fsp_ltext);
7091       widget_opts.expand = LIVES_EXPAND_DEFAULT;
7092       lives_free(fsp_ltext);
7093 
7094       if (ifile) {
7095         fclose(ifile);
7096       }
7097 
7098       end_fs_preview();
7099       lives_free(fsp_info_file);
7100 
7101       lives_freep((void **)&file_open_params);
7102       if (LIVES_IS_WIDGET(widget))
7103         lives_widget_set_sensitive(widget, TRUE);
7104       return FALSE;
7105     }
7106 
7107 
7108     void on_fs_preview_clicked(LiVESWidget * widget, livespointer user_data) {
7109       // file selector preview
7110       double start_time = 0.;
7111 
7112       uint64_t xwin = 0;
7113 
7114       char **array;
7115 
7116       int preview_frames = 0;
7117       int preview_type = LIVES_POINTER_TO_INT(user_data);
7118       int height = 0, width = 0;
7119       int fwidth = -1, fheight = -1;
7120       int owidth, oheight, npieces, border = 0;
7121       boolean with_audio = mainw->save_with_sound;
7122 
7123       char *thm_dir = NULL;
7124       char *tmp, *tmp2;
7125       char *com;
7126       char *type;
7127 
7128       file_open_params = NULL;
7129 
7130       if (mainw->in_fs_preview) {
7131         end_fs_preview();
7132         return;
7133       }
7134       fsp_ltext = NULL;
7135 
7136       if (!check_for_executable(&capable->has_mktemp, EXEC_MKTEMP)) {
7137         do_program_not_found_error(EXEC_MKTEMP);
7138         lives_widget_set_sensitive(widget, TRUE);
7139         return;
7140       }
7141 
7142       if (preview_type == LIVES_PREVIEW_TYPE_RANGE) {
7143         // open selection
7144         start_time = mainw->fx1_val;
7145         preview_frames = (int)mainw->fx2_val;
7146       } else {
7147         // open file
7148         lives_snprintf(file_name, PATH_MAX, "%s",
7149                        (tmp = lives_filename_to_utf8((tmp2
7150                               = lives_file_chooser_get_filename(LIVES_FILE_CHOOSER(lives_widget_get_toplevel(widget)))),
7151                               -1, NULL, NULL, NULL)));
7152         lives_free(tmp);
7153         lives_free(tmp2);
7154       }
7155 
7156       // get file detaisl
7157       if (!read_file_details_generic(file_name)) return;
7158 
7159       npieces = get_token_count(mainw->msg, '|');
7160       if (npieces < 4) {
7161         end_fs_preview();
7162         return;
7163       }
7164       array = lives_strsplit(mainw->msg, "|", npieces);
7165       type = lives_strdup(array[3]);
7166 
7167       if (npieces > 5) {
7168         width = atoi(array[4]);
7169         height = atoi(array[5]);
7170       }
7171       lives_strfreev(array);
7172 
7173       if (!strcmp(type, "image") || !strcmp(type, prefs->image_type)) {
7174         if (preview_type == LIVES_PREVIEW_TYPE_VIDEO_AUDIO || preview_type == LIVES_PREVIEW_TYPE_IMAGE_ONLY) {
7175           clear_mainw_msg();
7176 
7177           thm_dir = get_worktmp("_thm");
7178 
7179           if (thm_dir && capable->has_identify) {
7180             mainw->error = FALSE;
7181 
7182             fwidth = lives_widget_get_allocation_width(mainw->fs_playalign) - 20;
7183             fheight = lives_widget_get_allocation_height(mainw->fs_playalign) - 20;
7184 
7185             // make thumb from any image file
7186 
7187             com = lives_strdup_printf("%s make_thumb %s %d %d \"%s\" \"%s\"", prefs->backend_sync, thm_dir, fwidth,
7188                                       fheight, prefs->image_ext, (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)));
7189             lives_free(tmp);
7190 
7191             lives_popen(com, TRUE, mainw->msg, MAINW_MSG_SIZE);
7192             lives_free(com);
7193 
7194             npieces = get_token_count(mainw->msg, '|');
7195             if (npieces < 3) {
7196               THREADVAR(com_failed) = FALSE;
7197               end_fs_preview();
7198               return;
7199             }
7200             if (!THREADVAR(com_failed)) {
7201               array = lives_strsplit(mainw->msg, "|", 3);
7202               width = atoi(array[1]);
7203               height = atoi(array[2]);
7204               lives_strfreev(array);
7205             } else height = width = 0;
7206             THREADVAR(com_failed) = FALSE;
7207           } else {
7208             height = width = 0;
7209           }
7210 
7211           if (height > 0 && width > 0) {
7212             // draw image
7213             LiVESXWindow *xwin;
7214             LiVESError *error = NULL;
7215             char *thumbfile = lives_strdup_printf("%08d.%s", 1, prefs->image_ext);
7216             char *thumb = lives_build_filename(prefs->workdir, thm_dir, thumbfile, NULL);
7217             LiVESPixbuf *pixbuf = lives_pixbuf_new_from_file((tmp = lives_filename_from_utf8(thumb, -1, NULL, NULL, NULL)), &error);
7218             lives_free(thumbfile);
7219             lives_free(thumb);
7220             lives_free(tmp);
7221             if (!error) {
7222               lives_widget_object_set_data(LIVES_WIDGET_OBJECT(mainw->fs_playimg), "pixbuf", pixbuf);
7223               owidth = width;
7224               oheight = height;
7225 
7226               calc_maxspect(fwidth, fheight, &width, &height);
7227 
7228               width = (width >> 1) << 1;
7229               height = (height >> 1) << 1;
7230 
7231               if (width > owidth || height > oheight) {
7232                 width = owidth;
7233                 height = oheight;
7234               }
7235 
7236               if (width < 4 || height < 4) {
7237                 end_fs_preview();
7238                 lives_widget_set_sensitive(widget, TRUE);
7239                 return;
7240               }
7241               lives_widget_set_size_request(mainw->fs_playarea, width, height);
7242               lives_alignment_set(mainw->fs_playalign, 0.5, 0.5, (float)width / (float)fwidth,
7243                                   (float)height / (float)fheight);
7244               border = MIN(fwidth - width, fheight - height);
7245               lives_container_set_border_width(LIVES_CONTAINER(mainw->fs_playarea), border >> 1);
7246               lives_widget_show(mainw->fs_playimg);
7247               lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
7248               xwin = lives_widget_get_xwindow(mainw->fs_playimg);
7249               if (LIVES_IS_XWINDOW(xwin)) {
7250                 if (mainw->fsp_surface) lives_painter_surface_destroy(mainw->fsp_surface);
7251                 mainw->fsp_surface =
7252                   lives_xwindow_create_similar_surface(xwin, LIVES_PAINTER_CONTENT_COLOR,
7253                                                        width, height);
7254                 if (mainw->fsp_func == 0)
7255                   mainw->fsp_func = lives_signal_sync_connect(LIVES_GUI_OBJECT(mainw->fs_playimg), LIVES_WIDGET_EXPOSE_EVENT,
7256                                     LIVES_GUI_CALLBACK(all_expose), &mainw->fsp_surface);
7257               }
7258               set_drawing_area_from_pixbuf(mainw->fs_playimg, pixbuf, mainw->fsp_surface);
7259             } else {
7260               lives_error_free(error);
7261             }
7262           }
7263 
7264           if (thm_dir) {
7265             lives_rmdir(thm_dir, TRUE);
7266             lives_free(thm_dir);
7267           }
7268           if (height > 0 || width > 0 || preview_type == LIVES_PREVIEW_TYPE_IMAGE_ONLY) {
7269             lives_widget_set_sensitive(widget, TRUE);
7270             return;
7271           }
7272         }
7273         return;
7274       }
7275 
7276       check_for_executable(&capable->has_mplayer, EXEC_MPLAYER);
7277       check_for_executable(&capable->has_mplayer2, EXEC_MPLAYER2);
7278       check_for_executable(&capable->has_mpv, EXEC_MPV);
7279 
7280       if (!HAS_EXTERNAL_PLAYER) {
7281         char *msg;
7282         if (!check_for_executable(&capable->has_identify, EXEC_IDENTIFY)) {
7283           msg = (_("\n\nYou need to install mplayer, mplayer2 or mpv to be able to preview this file.\n"));
7284         } else {
7285           msg = (_("\n\nYou need to install mplayer, mplayer2, mpv or imageMagick to be able to preview this file.\n"));
7286         }
7287         do_error_dialog(msg);
7288         lives_free(msg);
7289         lives_widget_set_sensitive(widget, TRUE);
7290         return;
7291       }
7292 
7293       mainw->fsp_tmpdir = get_worktmp("_fsp");
7294 
7295       if (!mainw->fsp_tmpdir) {
7296         workdir_warning();
7297         lives_widget_set_sensitive(widget, TRUE);
7298         return;
7299       }
7300 
7301       fsp_info_file = lives_build_filename(mainw->fsp_tmpdir, LIVES_STATUS_FILE_NAME, NULL);
7302 
7303       mainw->in_fs_preview = TRUE;
7304 
7305       if (preview_type != LIVES_PREVIEW_TYPE_AUDIO_ONLY) {
7306         if (!strcmp(type, "Audio")) {
7307           preview_frames = -1;
7308         } else {
7309           if (height == 0 || width == 0) {
7310             width = DEF_FRAME_HSIZE / 2;
7311             height = DEF_FRAME_VSIZE / 2;
7312           }
7313 
7314           owidth = width;
7315           oheight = height;
7316 
7317           fwidth = lives_widget_get_allocation_width(mainw->fs_playframe) - 20;
7318           fheight = lives_widget_get_allocation_height(mainw->fs_playframe) - 20;
7319 
7320           calc_maxspect(fwidth, fheight, &width, &height);
7321 
7322           width = (width >> 1) << 1;
7323           height = (height >> 1) << 1;
7324 
7325           if (width > owidth || height > oheight) {
7326             width = owidth;
7327             height = oheight;
7328           }
7329 
7330           if (width < 4 || height < 4) {
7331             end_fs_preview();
7332             lives_widget_set_sensitive(widget, TRUE);
7333             return;
7334           }
7335           lives_widget_set_bg_color(mainw->fs_playframe, LIVES_WIDGET_STATE_NORMAL, &palette->black);
7336           lives_widget_set_size_request(mainw->fs_playarea, width, height);
7337           lives_alignment_set(mainw->fs_playalign, 0.5, 0.5, (float)width / (float)fwidth,
7338                               (float)height / (float)fheight);
7339           border = MIN(fwidth - width, fheight - height);
7340           lives_container_set_border_width(LIVES_CONTAINER(mainw->fs_playarea), border >> 1);
7341           lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
7342         }
7343       }  else preview_frames = -1;
7344 
7345       if (!USE_MPV) {
7346         if (with_audio && prefs->audio_player == AUD_PLAYER_JACK) {
7347           file_open_params = lives_strdup_printf("%s %s -ao jack", mainw->file_open_params != NULL ?
7348                                                  mainw->file_open_params : "", get_deinterlace_string());
7349         } else if (with_audio && prefs->audio_player == AUD_PLAYER_PULSE) {
7350           file_open_params = lives_strdup_printf("%s %s -ao pulse", mainw->file_open_params != NULL ?
7351                                                  mainw->file_open_params : "", get_deinterlace_string());
7352         } else {
7353           file_open_params = lives_strdup_printf("%s %s -ao null", mainw->file_open_params != NULL ?
7354                                                  mainw->file_open_params : "", get_deinterlace_string());
7355         }
7356       } else {
7357         if (with_audio && prefs->audio_player == AUD_PLAYER_JACK) {
7358           file_open_params = lives_strdup_printf("%s %s --ao=jack", mainw->file_open_params != NULL ?
7359                                                  mainw->file_open_params : "", get_deinterlace_string());
7360         } else if (with_audio && prefs->audio_player == AUD_PLAYER_PULSE) {
7361           file_open_params = lives_strdup_printf("%s %s --ao=pulse", mainw->file_open_params != NULL ?
7362                                                  mainw->file_open_params : "", get_deinterlace_string());
7363         } else {
7364           file_open_params = lives_strdup_printf("%s %s --ao=null", mainw->file_open_params != NULL ?
7365                                                  mainw->file_open_params : "", get_deinterlace_string());
7366         }
7367       }
7368 
7369       if (preview_type == LIVES_PREVIEW_TYPE_VIDEO_AUDIO || preview_type == LIVES_PREVIEW_TYPE_RANGE) {
7370         xwin = lives_widget_get_xwinid(mainw->fs_playarea, "Unsupported display type for preview.");
7371         if (xwin == -1) {
7372           end_fs_preview();
7373           lives_free(fsp_info_file);
7374           lives_widget_set_sensitive(widget, TRUE);
7375           return;
7376         }
7377       }
7378 
7379       if (file_open_params) {
7380         com = lives_strdup_printf("%s fs_preview %s %"PRIu64" %d %d %.2f %d %d \"%s\" \"%s\"", prefs->backend, mainw->fsp_tmpdir,
7381                                   xwin, width, height, start_time, preview_frames, (int)(prefs->volume * 100.),
7382                                   (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)), file_open_params);
7383 
7384       } else {
7385         com = lives_strdup_printf("%s fs_preview %s %"PRIu64" %d %d %.2f %d %d \"%s\"", prefs->backend, mainw->fsp_tmpdir,
7386                                   xwin, width, height, start_time, preview_frames, (int)(prefs->volume * 100.),
7387                                   (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)));
7388       }
7389 
7390       lives_free(tmp);
7391       mainw->in_fs_preview = TRUE;
7392       lives_rm(fsp_info_file);
7393       lives_system(com, FALSE);
7394       lives_free(com);
7395 
7396       if (THREADVAR(com_failed)) {
7397         THREADVAR(com_failed) = FALSE;
7398         end_fs_preview();
7399         lives_free(fsp_info_file);
7400         lives_widget_set_sensitive(widget, TRUE);
7401         return;
7402       }
7403 
7404       tmp = (_("\nStop Preview\n"));
7405       fsp_ltext = lives_strdup(lives_button_get_label(LIVES_BUTTON(widget)));
7406       widget_opts.expand = LIVES_EXPAND_NONE;
7407       lives_button_set_label(LIVES_BUTTON(widget), tmp);
7408       if (preview_type == LIVES_PREVIEW_TYPE_RANGE) {
7409         widget_opts.expand = LIVES_EXPAND_DEFAULT;
7410       }
7411       lives_free(tmp);
7412 
7413       // loop here until preview has finished, or the user presses OK or Cancel
7414       lives_idle_add_simple(fs_preview_idle, (livespointer)widget);
7415     }
7416 
7417 
7418     void on_open_activate(LiVESMenuItem * menuitem, livespointer user_data) {
7419       // OPEN A FILE (single or multiple)
7420       LiVESWidget *chooser;
7421       LiVESResponseType resp;
7422       mainw->mt_needs_idlefunc = FALSE;
7423 
7424       if (mainw->multitrack) {
7425         if (mainw->multitrack->idlefunc > 0) {
7426           lives_source_remove(mainw->multitrack->idlefunc);
7427           mainw->multitrack->idlefunc = 0;
7428           mainw->mt_needs_idlefunc = TRUE;
7429         }
7430         mt_desensitise(mainw->multitrack);
7431         lives_widget_set_sensitive(mainw->multitrack->playall, TRUE);
7432         lives_widget_set_sensitive(mainw->m_playbutton, TRUE);
7433       }
7434 
7435       chooser = choose_file_with_preview((*mainw->vid_load_dir) ? mainw->vid_load_dir : NULL, NULL, NULL,
7436                                          LIVES_FILE_SELECTION_VIDEO_AUDIO_MULTI);
7437 
7438       resp = lives_dialog_run(LIVES_DIALOG(chooser));
7439 
7440       end_fs_preview();
7441 
7442       if (resp == LIVES_RESPONSE_ACCEPT) on_ok_file_open_clicked(LIVES_FILE_CHOOSER(chooser), NULL);
7443       else on_filechooser_cancel_clicked(chooser);
7444     }
7445 
7446 
7447     void on_ok_file_open_clicked(LiVESFileChooser * chooser, LiVESSList * fnames) {
7448       // this is also called from drag target
7449 
7450       LiVESSList *ofnames;
7451 
7452       if (chooser) {
7453         fnames = lives_file_chooser_get_filenames(chooser);
7454 
7455         lives_widget_destroy(LIVES_WIDGET(chooser));
7456 
7457         if (!fnames) return;
7458 
7459         if (!fnames->data) {
7460           lives_list_free_all((LiVESList **)&fnames);
7461           return;
7462         }
7463 
7464         lives_snprintf(mainw->vid_load_dir, PATH_MAX, "%s", (char *)fnames->data);
7465         get_dirname(mainw->vid_load_dir);
7466 
7467         lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
7468 
7469         if (prefs->save_directories) {
7470           set_utf8_pref(PREF_VID_LOAD_DIR, mainw->vid_load_dir);
7471         }
7472 
7473         mainw->cancelled = CANCEL_NONE;
7474       }
7475 
7476       ofnames = fnames;
7477       mainw->img_concat_clip = -1;
7478 
7479       while (fnames && mainw->cancelled == CANCEL_NONE) {
7480         lives_snprintf(file_name, PATH_MAX, "%s", (char *)fnames->data);
7481         lives_free((livespointer)fnames->data);
7482         open_file(file_name);
7483         fnames = fnames->next;
7484       }
7485 
7486       lives_slist_free(ofnames);
7487 
7488       mainw->opening_multi = FALSE;
7489       mainw->img_concat_clip = -1;
7490 
7491       if (mainw->multitrack) {
7492         polymorph(mainw->multitrack, POLY_NONE);
7493         polymorph(mainw->multitrack, POLY_CLIPS);
7494         mt_sensitise(mainw->multitrack);
7495         maybe_add_mt_idlefunc();
7496       }
7497     }
7498 
7499 
7500 #ifdef GUI_GTK
7501     // TODO
7502     // files dragged onto target from outside - try to open them
7503     void drag_from_outside(LiVESWidget * widget, GdkDragContext * dcon, int x, int y,
7504                            GtkSelectionData * data, uint32_t info, uint32_t time, livespointer user_data) {
7505       GSList *fnames = NULL;
7506 #if GTK_CHECK_VERSION(3, 0, 0)
7507       char *filelist = (char *)gtk_selection_data_get_data(data);
7508 #else
7509       char *filelist = (char *)data->data;
7510 #endif
7511       char *nfilelist, **array;
7512       int nfiles, i;
7513 
7514       if (!filelist) {
7515         gtk_drag_finish(dcon, FALSE, FALSE, time);
7516         return;
7517       }
7518 
7519       if ((mainw->multitrack && !lives_widget_is_sensitive(mainw->multitrack->open_menu)) ||
7520           (!mainw->multitrack && !lives_widget_is_sensitive(mainw->open))) {
7521         gtk_drag_finish(dcon, FALSE, FALSE, time);
7522         return;
7523       }
7524 
7525       nfilelist = subst(filelist, "file://", "");
7526 
7527       nfiles = get_token_count(nfilelist, '\n');
7528       array = lives_strsplit(nfilelist, "\n", nfiles);
7529       lives_free(nfilelist);
7530 
7531       for (i = 0; i < nfiles; i++) {
7532         fnames = lives_slist_append(fnames, array[i]);
7533       }
7534 
7535       on_ok_file_open_clicked(NULL, fnames);
7536 
7537       // fn will free array elements and fnames
7538 
7539       lives_free(array);
7540 
7541       gtk_drag_finish(dcon, TRUE, FALSE, time);
7542     }
7543 #endif
7544 
7545 
7546     void on_opensel_range_ok_clicked(LiVESButton * button, livespointer user_data) {
7547       // open file selection
7548       boolean needs_idlefunc;
7549 
7550       end_fs_preview();
7551 
7552       needs_idlefunc = mainw->mt_needs_idlefunc;
7553       mainw->mt_needs_idlefunc = FALSE;
7554       lives_general_button_clicked(button, NULL);
7555       mainw->mt_needs_idlefunc = needs_idlefunc;
7556 
7557       mainw->img_concat_clip = -1;
7558       open_file_sel(file_name, mainw->fx1_val, (int)mainw->fx2_val);
7559 
7560       if (mainw->multitrack) {
7561         polymorph(mainw->multitrack, POLY_NONE);
7562         polymorph(mainw->multitrack, POLY_CLIPS);
7563         mt_sensitise(mainw->multitrack);
7564         maybe_add_mt_idlefunc();
7565       }
7566     }
7567 
7568 
7569     void end_fs_preview(void) {
7570       // clean up if we were playing a preview - should be called from all callbacks
7571       // where there is a possibility of fs preview still playing
7572       static boolean singleton = FALSE;
7573       char *com;
7574 
7575       if (singleton) return;
7576       singleton = TRUE;
7577 
7578       lives_widget_set_bg_color(mainw->fs_playframe, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
7579 
7580       if (mainw->fs_playarea) {
7581         LiVESPixbuf *pixbuf = NULL;
7582         pixbuf = (LiVESPixbuf *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mainw->fs_playarea), "pixbuf");
7583         if (pixbuf) lives_widget_object_unref(pixbuf);
7584         lives_widget_object_set_data(LIVES_WIDGET_OBJECT(mainw->fs_playarea), "pixbuf", NULL);
7585         if (mainw->fsp_func != 0) {
7586           lives_signal_handler_disconnect(mainw->fs_playimg, mainw->fsp_func);
7587           mainw->fsp_func = 0;
7588         }
7589         if (mainw->fsp_surface) {
7590           lives_painter_surface_destroy(mainw->fsp_surface);
7591           mainw->fsp_surface = NULL;
7592         }
7593         lives_widget_hide(mainw->fs_playimg);
7594       }
7595 
7596       if (mainw->in_fs_preview) {
7597         if (mainw->fsp_tmpdir) {
7598           char *permitname = lives_build_filename(prefs->workdir, mainw->fsp_tmpdir,
7599                                                   TEMPFILE_MARKER "." LIVES_FILE_EXT_TMP, NULL);
7600           lives_kill_subprocesses(mainw->fsp_tmpdir, TRUE);
7601           lives_touch(permitname);
7602           lives_free(permitname);
7603           com = lives_strdup_printf("%s close \"%s\"", prefs->backend, mainw->fsp_tmpdir);
7604           lives_freep((void **)&mainw->fsp_tmpdir);
7605           lives_system(com, TRUE);
7606           lives_free(com);
7607         }
7608         mainw->in_fs_preview = FALSE;
7609       }
7610       if (mainw->fs_playframe) lives_widget_queue_draw(mainw->fs_playframe);
7611       singleton = FALSE;
7612     }
7613 
7614 
7615     void on_save_textview_clicked(LiVESButton * button, livespointer user_data) {
7616       LiVESTextView *textview = (LiVESTextView *)user_data;
7617       char *filt[] = {"*." LIVES_FILE_EXT_TEXT, NULL};
7618       int fd;
7619       char *btext;
7620       char *save_file;
7621       boolean needs_idlefunc;
7622 
7623       lives_widget_hide(lives_widget_get_toplevel(LIVES_WIDGET(button)));
7624       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
7625 
7626       save_file = choose_file(NULL, NULL, filt, LIVES_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
7627 
7628       if (!save_file) {
7629         lives_widget_show(lives_widget_get_toplevel(LIVES_WIDGET(button)));
7630         return;
7631       }
7632 
7633 #ifndef IS_MINGW
7634       if ((fd = creat(save_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) == -1) {
7635 #else
7636       if ((fd = creat(save_file, S_IRUSR | S_IWUSR)) == -1) {
7637 #endif
7638         lives_widget_show(lives_widget_get_toplevel(LIVES_WIDGET(button)));
7639         do_write_failed_error_s(save_file, lives_strerror(errno));
7640         lives_free(save_file);
7641         return;
7642       }
7643 
7644       btext = lives_text_view_get_text(textview);
7645 
7646       needs_idlefunc = mainw->mt_needs_idlefunc;
7647       mainw->mt_needs_idlefunc = FALSE;
7648       lives_general_button_clicked(button, NULL);
7649       mainw->mt_needs_idlefunc = needs_idlefunc;
7650 
7651       THREADVAR(write_failed) = FALSE;
7652       lives_write(fd, btext, strlen(btext), FALSE);
7653       lives_free(btext);
7654 
7655       close(fd);
7656 
7657       if (THREADVAR(write_failed)) {
7658         do_write_failed_error_s(save_file, lives_strerror(errno));
7659       } else {
7660         char *msg = lives_strdup_printf(_("Text was saved as\n%s\n"), save_file);
7661         do_error_dialog(msg);
7662         lives_free(msg);
7663       }
7664 
7665       lives_free(save_file);
7666 #if 0
7667     }
7668 #endif
7669   }
7670 
7671 
7672   void on_filechooser_cancel_clicked(LiVESWidget * widget) {
7673     lives_widget_destroy(widget);
7674 
7675     if (mainw->multitrack) {
7676       mt_sensitise(mainw->multitrack);
7677       maybe_add_mt_idlefunc();
7678     } else if (!CURRENT_CLIP_IS_CLIPBOARD && CURRENT_CLIP_IS_VALID && !cfile->opening) {
7679       get_play_times();
7680     }
7681   }
7682 
7683 
7684   void on_cancel_opensel_clicked(LiVESButton * button, livespointer user_data) {
7685     boolean needs_idlefunc;
7686 
7687     end_fs_preview();
7688     needs_idlefunc = mainw->mt_needs_idlefunc;
7689     mainw->mt_needs_idlefunc = FALSE;
7690     lives_general_button_clicked(button, NULL);
7691     mainw->mt_needs_idlefunc = needs_idlefunc;
7692 
7693     if (mainw->multitrack) {
7694       mt_sensitise(mainw->multitrack);
7695       maybe_add_mt_idlefunc();
7696     }
7697     lives_menu_item_activate(LIVES_MENU_ITEM(mainw->open_sel)); // returm to the fileselector
7698   }
7699 
7700 
7701   void on_cancel_keep_button_clicked(LiVESButton * button, livespointer user_data) {
7702     // Cancel/Keep from progress dialog
7703     char *com = NULL;
7704 
7705     uint32_t keep_frames = 0;
7706 
7707     boolean killprocs = FALSE;
7708 
7709     if (CURRENT_CLIP_IS_VALID && cfile->opening && mainw->effects_paused) {
7710       on_stop_clicked(NULL, NULL);
7711       return;
7712     }
7713 
7714     clear_mainw_msg();
7715 
7716     if (CURRENT_CLIP_IS_VALID && mainw->proc_ptr) {
7717       lives_widget_set_sensitive(mainw->proc_ptr->cancel_button, FALSE);
7718       lives_widget_set_sensitive(mainw->proc_ptr->pause_button, FALSE);
7719       if (mainw->proc_ptr->stop_button)
7720         lives_widget_set_sensitive(mainw->proc_ptr->stop_button, FALSE);
7721       lives_widget_set_sensitive(mainw->proc_ptr->preview_button, FALSE);
7722     }
7723     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
7724 
7725     if ((!mainw->effects_paused || cfile->nokeep) && (!mainw->multitrack ||
7726         (mainw->multitrack && (!mainw->multitrack->is_rendering ||
7727                                !mainw->preview)))) {
7728       // Cancel
7729       if (mainw->cancel_type == CANCEL_SOFT) {
7730         // cancel in record audio
7731         mainw->cancelled = CANCEL_USER;
7732         d_print_cancelled();
7733         return;
7734       } else if (mainw->cancel_type == CANCEL_KILL) {
7735         // kill processes and subprocesses working on cfile
7736         killprocs = TRUE;
7737       }
7738 
7739       if (CURRENT_CLIP_IS_VALID && !cfile->opening && !mainw->internal_messaging) {
7740         // if we are opening, this is 'stop' in the preview, so don't cancel
7741         // otherwise, come here
7742 
7743         // kill off the background process
7744         if (killprocs) {
7745           lives_kill_subprocesses(cfile->handle, TRUE);
7746         }
7747 
7748         // resume for next time
7749         if (mainw->effects_paused) {
7750           lives_freep((void **)&com);
7751           com = lives_strdup_printf("%s resume \"%s\"", prefs->backend_sync, cfile->handle);
7752           lives_system(com, FALSE);
7753         }
7754       }
7755 
7756       mainw->cancelled = CANCEL_USER;
7757 
7758       if (mainw->is_rendering) {
7759         if (CURRENT_CLIP_IS_VALID) cfile->frames = 0;
7760         d_print_cancelled();
7761       } else {
7762         // see if there was a message from backend
7763 
7764         if (mainw->cancel_type != CANCEL_SOFT) {
7765           lives_fread_string(mainw->msg, MAINW_MSG_SIZE, cfile->info_file);
7766           if (lives_strncmp(mainw->msg, "completed", 9)) {
7767             d_print_cancelled();
7768           } else {
7769             // processing finished before we could cancel
7770             mainw->cancelled = CANCEL_NONE;
7771           }
7772         } else d_print_cancelled();
7773       }
7774     } else {
7775       // Keep
7776       if (mainw->cancel_type == CANCEL_SOFT) {
7777         mainw->cancelled = CANCEL_KEEP;
7778         return;
7779       }
7780       if (!mainw->is_rendering) {
7781         keep_frames = mainw->proc_ptr->frames_done - cfile->progress_start
7782                       + cfile->start - 1 + mainw->internal_messaging * 2;
7783         if (mainw->internal_messaging && atoi(mainw->msg) > mainw->proc_ptr->frames_done)
7784           keep_frames = atoi(mainw->msg) - cfile->progress_start + cfile->start - 1 + 2;
7785       } else keep_frames = cfile->frames + 1;
7786       if (keep_frames > mainw->internal_messaging) {
7787         d_print_enough(keep_frames - cfile->start);
7788         lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
7789         if (!mainw->internal_messaging) {
7790           lives_kill_subprocesses(cfile->handle, TRUE);
7791           com = lives_strdup_printf("%s resume \"%s\"", prefs->backend_sync, cfile->handle);
7792           lives_system(com, FALSE);
7793           lives_free(com);
7794           if (!mainw->keep_pre) com = lives_strdup_printf("%s mv_mgk \"%s\" %d %d \"%s\"", prefs->backend, cfile->handle,
7795                                         cfile->start, keep_frames - 1, get_image_ext_for_type(cfile->img_type));
7796           else {
7797             com = lives_strdup_printf("%s mv_pre \"%s\" %d %d \"%s\" &", prefs->backend_sync, cfile->handle,
7798                                       cfile->start, keep_frames - 1, get_image_ext_for_type(cfile->img_type));
7799             mainw->keep_pre = FALSE;
7800           }
7801         } else {
7802           mainw->internal_messaging = FALSE;
7803           if (!mainw->keep_pre) com = lives_strdup_printf("%s mv_mgk \"%s\" %d %d \"%s\"", prefs->backend, cfile->handle,
7804                                         cfile->start, keep_frames - 1, get_image_ext_for_type(cfile->img_type));
7805           else {
7806             com = lives_strdup_printf("%s mv_pre \"%s\" %d %d \"%s\" &", prefs->backend_sync, cfile->handle,
7807                                       cfile->start, keep_frames - 1, get_image_ext_for_type(cfile->img_type));
7808             mainw->keep_pre = FALSE;
7809           }
7810         }
7811         if (!mainw->is_rendering || mainw->multitrack) {
7812           lives_rm(cfile->info_file);
7813           lives_system(com, FALSE);
7814           cfile->undo_end = keep_frames - 1;
7815         } else mainw->cancelled = CANCEL_KEEP;
7816         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
7817       } else {
7818         // no frames there, nothing to keep
7819         d_print_cancelled();
7820 
7821         if (!mainw->internal_messaging && !mainw->is_rendering) {
7822           lives_kill_subprocesses(cfile->handle, TRUE);
7823           com = lives_strdup_printf("%s resume \"%s\"", prefs->backend_sync, cfile->handle);
7824           lives_system(com, FALSE);
7825         }
7826         mainw->cancelled = CANCEL_USER;
7827       }
7828     }
7829 
7830     lives_freep((void **)&com);
7831   }
7832 
7833 
7834   void on_details_button_clicked(void) {
7835     text_window *textwindow;
7836     widget_opts.expand = LIVES_EXPAND_EXTRA;
7837     textwindow = create_text_window(_("Encoder Debug Output"),
7838                                     lives_text_view_get_text(mainw->optextview), NULL, TRUE);
7839     widget_opts.expand = LIVES_EXPAND_DEFAULT;
7840     lives_widget_show_all(textwindow->dialog);
7841   }
7842 
7843 
7844   void on_full_screen_pressed(LiVESButton * button, livespointer user_data) {
7845     // toolbar button (full screen)
7846     // ignore if audio only clip
7847     if (CURRENT_CLIP_IS_VALID && !CURRENT_CLIP_HAS_VIDEO && !mainw->multitrack) return;
7848     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->full_screen), !mainw->fs);
7849   }
7850 
7851 
7852   static void _on_full_screen_activate(LiVESMenuItem * menuitem, livespointer user_data) {
7853 
7854     // ignore if audio only clip
7855     if (CURRENT_CLIP_IS_VALID && !CURRENT_CLIP_HAS_VIDEO && LIVES_IS_PLAYING && !mainw->multitrack) return;
7856 
7857     if (!user_data) {
7858       // toggle can be overridden by setting user_data non-NULL
7859       mainw->fs = !mainw->fs;
7860     }
7861     mainw->blend_palette = WEED_PALETTE_END;
7862 
7863     if (!mainw->fs) {
7864       lives_widget_set_tooltip_text(mainw->t_fullscreen, _("Fullscreen playback (f)"));
7865       lives_widget_set_opacity(mainw->t_fullscreen, .75);
7866     } else {
7867       lives_widget_set_tooltip_text(mainw->t_fullscreen, _("Fullscreen playback off (f)"));
7868       lives_widget_set_opacity(mainw->t_fullscreen, 1.);
7869     }
7870 
7871     if (LIVES_IS_PLAYING) {
7872       if (mainw->fs) {
7873         // switch TO full screen during pb
7874         if (!mainw->multitrack && !mainw->sep_win) {
7875           fade_background();
7876           fullscreen_internal();
7877         }
7878         if (mainw->sep_win) {
7879           resize_play_window();
7880           lives_window_set_decorated(LIVES_WINDOW(mainw->play_window), FALSE);
7881         }
7882         if (cfile->frames == 1 || cfile->play_paused) {
7883           lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
7884         }
7885 
7886         if (mainw->ext_playback && mainw->vpp->fheight > -1 && mainw->vpp->fwidth > -1) {
7887           // fixed o/p size for stream
7888           if (mainw->vpp->fwidth == 0 || mainw->vpp->fheight == 0) {
7889             mainw->vpp->fwidth = cfile->hsize;
7890             mainw->vpp->fheight = cfile->vsize;
7891           }
7892           mainw->pwidth = mainw->vpp->fwidth;
7893           mainw->pheight = mainw->vpp->fheight;
7894         }
7895       } else {
7896         // switch from fullscreen during pb
7897         if (mainw->sep_win) {
7898           // separate window
7899           mainw->ignore_screen_size = TRUE;
7900           if (prefs->show_desktop_panel && (capable->wm_caps.pan_annoy & ANNOY_DISPLAY)
7901               && (capable->wm_caps.pan_annoy & ANNOY_FS) && (capable->wm_caps.pan_res & RES_HIDE) &&
7902               capable->wm_caps.pan_res & RESTYPE_ACTION) {
7903             show_desktop_panel();
7904           }
7905           if (mainw->ext_playback) {
7906 #ifndef IS_MINGW
7907             vid_playback_plugin_exit();
7908             lives_window_unfullscreen(LIVES_WINDOW(mainw->play_window));
7909 #else
7910             lives_window_unfullscreen(LIVES_WINDOW(mainw->play_window));
7911             vid_playback_plugin_exit();
7912 #endif
7913           } else {
7914             // multi monitors don't like this it seems, breaks the window
7915             lives_window_unfullscreen(LIVES_WINDOW(mainw->play_window));
7916           }
7917 
7918           if (!mainw->faded) unfade_background();
7919           resize_play_window();
7920 
7921           if (!mainw->multitrack && mainw->opwx > -1) {
7922             //opwx and opwy were stored when we first switched to full screen
7923             lives_window_move(LIVES_WINDOW(mainw->play_window), mainw->opwx, mainw->opwy);
7924             mainw->opwx = mainw->opwy = -1;
7925           } else {
7926             if (mainw->play_window) {
7927               lives_window_center(LIVES_WINDOW(mainw->play_window));
7928               hide_cursor(lives_widget_get_xwindow(mainw->play_window));
7929               lives_widget_set_app_paintable(mainw->play_window, TRUE);
7930             }
7931           }
7932           if (!mainw->multitrack) {
7933             lives_window_set_decorated(LIVES_WINDOW(mainw->play_window), TRUE);
7934             if (cfile->frames == 1 || cfile->play_paused) {
7935               lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
7936             }
7937           }
7938         } else {
7939           // switch FROM fullscreen during pb
7940           // in frame window
7941           if (!mainw->multitrack) {
7942             if (!mainw->faded) {
7943               if (mainw->double_size) {
7944                 lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), FALSE);
7945                 lives_widget_hide(mainw->sep_image);
7946                 if (prefs->show_msg_area) lives_widget_hide(mainw->message_box);
7947               } else lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), TRUE);
7948               unfade_background();
7949             } else {
7950               lives_widget_hide(mainw->frame1);
7951               lives_widget_hide(mainw->frame2);
7952               lives_widget_show(mainw->eventbox3);
7953               lives_widget_show(mainw->eventbox4);
7954               fade_background();
7955               if (mainw->double_size) {
7956                 lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), FALSE);
7957                 resize(2.);
7958               } else {
7959                 lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), TRUE);
7960                 resize(1.);
7961               }
7962             }
7963 
7964             lives_widget_set_sensitive(mainw->fade, TRUE);
7965             lives_widget_set_sensitive(mainw->dsize, TRUE);
7966 	    // *INDENT-OFF*
7967 	}}
7968       if (!mainw->multitrack && !mainw->faded) {
7969 	if (CURRENT_CLIP_IS_VALID) {
7970 	  redraw_timeline(mainw->current_file);
7971 	  show_playbar_labels(mainw->current_file);
7972 	}}}
7973     // *INDENT-ON*
7974       mainw->force_show = TRUE;
7975     } else {
7976       // not playing
7977       if (!mainw->multitrack) {
7978         if (mainw->fs) {
7979           lives_widget_set_sensitive(mainw->fade, FALSE);
7980           lives_widget_set_sensitive(mainw->dsize, FALSE);
7981         } else {
7982           lives_widget_set_sensitive(mainw->fade, TRUE);
7983           lives_widget_set_sensitive(mainw->dsize, TRUE);
7984 	// *INDENT-OFF*
7985       }}}
7986   // *INDENT-ON*
7987   }
7988 
7989 
7990   void on_full_screen_activate(LiVESMenuItem * menuitem, livespointer user_data) {
7991     main_thread_execute((lives_funcptr_t)_on_full_screen_activate, 0, NULL, "vv", menuitem, user_data);
7992   }
7993 
7994   void on_double_size_pressed(LiVESButton * button, livespointer user_data) {
7995     // toolbar button (separate window)
7996     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->dsize), !mainw->double_size);
7997   }
7998 
7999 
8000   void on_double_size_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8001 
8002     if (mainw->multitrack || (CURRENT_CLIP_IS_VALID && !CURRENT_CLIP_HAS_VIDEO && !user_data)) return;
8003 
8004     if (!user_data) {
8005       mainw->double_size = !mainw->double_size;
8006     }
8007 
8008     if (!CURRENT_CLIP_IS_VALID) return;
8009     mainw->blend_palette = WEED_PALETTE_END;
8010 
8011     mainw->opwx = mainw->opwy = -1;
8012 
8013     if ((LIVES_IS_PLAYING && !mainw->fs) || (!LIVES_IS_PLAYING && mainw->play_window)) {
8014       if (mainw->play_window) {
8015         resize_play_window();
8016         sched_yield();
8017         if (!mainw->double_size) lives_window_center(LIVES_WINDOW(mainw->play_window));
8018       } else {
8019         // in-frame
8020         if (mainw->double_size) {
8021           if (!mainw->faded) {
8022             if (palette->style & STYLE_1) {
8023               lives_widget_hide(mainw->sep_image);
8024             }
8025             if (prefs->show_msg_area) lives_widget_hide(mainw->message_box);
8026           }
8027           lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), FALSE);
8028           resize(2.);
8029         } else {
8030           lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), TRUE);
8031           resize(1.);
8032           if (!mainw->faded) {
8033             if (palette->style & STYLE_1) {
8034               lives_widget_show_all(mainw->sep_image);
8035             }
8036             if (prefs->show_msg_area) lives_widget_show_all(mainw->message_box);
8037 	  // *INDENT-OFF*
8038         }}}}
8039   // *INDENT-ON*
8040     if (LIVES_IS_PLAYING && !mainw->fs) mainw->force_show = TRUE;
8041   }
8042 
8043 
8044   void on_sepwin_pressed(LiVESButton * button, livespointer user_data) {
8045     if (mainw->go_away) return;
8046 
8047     // toolbar button (separate window)
8048     if (mainw->multitrack) {
8049       lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->multitrack->sepwin), !mainw->sep_win);
8050       lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->sepwin), mainw->sep_win);
8051     } else lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->sepwin), !mainw->sep_win);
8052   }
8053 
8054 
8055   void on_sepwin_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8056 
8057     if (mainw->go_away) return;
8058 
8059     mainw->sep_win = !mainw->sep_win;
8060     mainw->blend_palette = WEED_PALETTE_END;
8061 
8062     if (mainw->multitrack) {
8063       if (!LIVES_IS_PLAYING) return;
8064       unpaint_lines(mainw->multitrack);
8065       mainw->multitrack->redraw_block = TRUE; // stop pb cursor from updating
8066       mt_show_current_frame(mainw->multitrack, FALSE);
8067       mainw->multitrack->redraw_block = FALSE;
8068     }
8069 
8070     if (mainw->sep_win) {
8071       lives_widget_set_tooltip_text(mainw->m_sepwinbutton, _("Hide the play window (s)"));
8072       lives_widget_set_tooltip_text(mainw->t_sepwin, _("Hide the play window (s)"));
8073       lives_widget_set_opacity(mainw->m_sepwinbutton, 1.);
8074       lives_widget_set_opacity(mainw->t_sepwin, 1.);
8075     } else {
8076       lives_widget_set_tooltip_text(mainw->m_sepwinbutton, _("Show the play window (s)"));
8077       lives_widget_set_tooltip_text(mainw->t_sepwin, _("Play in separate window (s)"));
8078       lives_widget_set_opacity(mainw->m_sepwinbutton, .75);
8079       lives_widget_set_opacity(mainw->t_sepwin, .75);
8080     }
8081 
8082     if (prefs->sepwin_type == SEPWIN_TYPE_STICKY && !LIVES_IS_PLAYING) {
8083       if (mainw->sep_win) make_play_window();
8084       else kill_play_window();
8085       /* if (mainw->multitrack && !LIVES_IS_PLAYING) { */
8086       /*   activate_mt_preview(mainw->multitrack); // show frame preview */
8087       /* } */
8088     } else {
8089       if (LIVES_IS_PLAYING) {
8090         if (mainw->sep_win) {
8091           // switch to separate window during pb
8092           if (!mainw->multitrack) {
8093             lives_widget_set_opacity(mainw->playframe, 0.);
8094             if (!prefs->hide_framebar && !mainw->faded && ((!mainw->preview && (CURRENT_CLIP_HAS_VIDEO || mainw->foreign)) ||
8095                 (CURRENT_CLIP_IS_VALID &&
8096                  cfile->opening))) {
8097               lives_widget_show(mainw->framebar);
8098             }
8099             if ((!mainw->faded && mainw->fs && ((prefs->play_monitor != widget_opts.monitor && prefs->play_monitor > 0 &&
8100                                                  capable->nmonitors > 1))) ||
8101                 (mainw->fs && mainw->vpp &&
8102                  !(mainw->vpp->capabilities & VPP_LOCAL_DISPLAY))) {
8103               unfade_background();
8104               showclipimgs();
8105             }
8106             if (mainw->fs && !mainw->faded) {
8107               lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), TRUE);
8108               resize(1.);
8109             } else {
8110               if (mainw->faded) {
8111                 lives_widget_set_opacity(mainw->playframe, 0.);
8112                 //lives_widget_hide(mainw->playframe);
8113                 lives_widget_hide(mainw->frame1);
8114                 lives_widget_hide(mainw->frame2);
8115                 lives_widget_show(mainw->eventbox3);
8116                 lives_widget_show(mainw->eventbox4);
8117               } else {
8118                 if (mainw->double_size) {
8119                   // switch back to single size as we are scooping the player out
8120                   lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), TRUE);
8121                   resize(1.);
8122                   if (!mainw->faded) {
8123                     if (palette->style & STYLE_1) {
8124                       lives_widget_show(mainw->sep_image);
8125                     }
8126                     if (prefs->show_msg_area) lives_widget_show_all(mainw->message_box);
8127 		// *INDENT-OFF*
8128                 }}}}}
8129 	// *INDENT-ON*
8130           else {
8131             set_drawing_area_from_pixbuf(mainw->play_image, NULL, mainw->play_surface);
8132           }
8133           make_play_window();
8134 
8135           mainw->pw_scroll_func = lives_signal_connect(LIVES_GUI_OBJECT(mainw->play_window), LIVES_WIDGET_SCROLL_EVENT,
8136                                   LIVES_GUI_CALLBACK(on_mouse_scroll), NULL);
8137 
8138           if (mainw->ext_playback && mainw->vpp->fheight > -1 && mainw->vpp->fwidth > -1) {
8139             // fixed o/p size for stream
8140             if ((mainw->vpp->fwidth == 0 || mainw->vpp->fheight == 0) && CURRENT_CLIP_IS_VALID) {
8141               mainw->vpp->fwidth = cfile->hsize;
8142               mainw->vpp->fheight = cfile->vsize;
8143             }
8144             mainw->pwidth = mainw->vpp->fwidth;
8145             mainw->pheight = mainw->vpp->fheight;
8146 
8147             if (!(mainw->vpp->capabilities & VPP_LOCAL_DISPLAY)) {
8148               unfade_background();
8149             }
8150             resize(1.);
8151             resize_play_window();
8152           }
8153 
8154           if (mainw->play_window && LIVES_IS_XWINDOW(lives_widget_get_xwindow(mainw->play_window))) {
8155             hide_cursor(lives_widget_get_xwindow(mainw->play_window));
8156             lives_widget_set_app_paintable(mainw->play_window, TRUE);
8157           }
8158         } else {
8159           // switch from separate window during playback
8160           if (mainw->ext_playback) {
8161 #ifndef IS_MINGW
8162             vid_playback_plugin_exit();
8163             lives_window_unfullscreen(LIVES_WINDOW(mainw->play_window));
8164 #else
8165             lives_window_unfullscreen(LIVES_WINDOW(mainw->play_window));
8166             vid_playback_plugin_exit();
8167 #endif
8168           }
8169           if (mainw->fs) {
8170             mainw->ignore_screen_size = TRUE;
8171             if (prefs->show_desktop_panel) {
8172               show_desktop_panel();
8173             }
8174           }
8175 
8176           kill_play_window();
8177 
8178           if (!mainw->multitrack) {
8179             lives_widget_show_all(mainw->playframe);
8180             if (!mainw->fs) {
8181               if (!mainw->double_size) {
8182                 lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), TRUE);
8183                 resize(1.);
8184               } else {
8185                 if (palette->style & STYLE_1) {
8186                   lives_widget_hide(mainw->sep_image);
8187                 }
8188                 if (prefs->show_msg_area) lives_widget_hide(mainw->message_box);
8189                 lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), FALSE);
8190                 resize(2.);
8191               }
8192             } else {
8193               // fullscreen
8194               if (!mainw->multitrack && CURRENT_CLIP_HAS_VIDEO) {
8195                 lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), FALSE);
8196                 fade_background();
8197                 fullscreen_internal();
8198 	      // *INDENT-OFF*
8199 	    }}
8200 	  lives_widget_set_opacity(mainw->playframe, 1.);
8201 	  hide_cursor(lives_widget_get_xwindow(mainw->playarea));
8202 	}}}}
8203   // *INDENT-ON*
8204     if (LIVES_IS_PLAYING) mainw->force_show = TRUE;
8205   }
8206 
8207 
8208   void on_showfct_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8209     prefs->hide_framebar = !prefs->hide_framebar;
8210     if (!mainw->fs || (prefs->play_monitor != widget_opts.monitor && mainw->play_window && capable->nmonitors > 1)) {
8211       if (!prefs->hide_framebar) {
8212         lives_widget_show(mainw->framebar);
8213       } else {
8214         if (prefs->hide_framebar) {
8215           lives_widget_hide(mainw->framebar);
8216 	// *INDENT-OFF*
8217       }}}
8218   // *INDENT-ON*
8219   }
8220 
8221 
8222   void on_sticky_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8223     // type is SEPWIN_TYPE_STICKY (shown even when not playing)
8224     // or SEPWIN_TYPE_NON_STICKY (shown only when playing)
8225     boolean make_perm = (prefs->sepwin_type == future_prefs->sepwin_type);
8226     if (prefs->sepwin_type == SEPWIN_TYPE_NON_STICKY) {
8227       pref_factory_int(PREF_SEPWIN_TYPE, SEPWIN_TYPE_STICKY, make_perm);
8228     } else {
8229       pref_factory_int(PREF_SEPWIN_TYPE, SEPWIN_TYPE_NON_STICKY, make_perm);
8230     }
8231   }
8232 
8233 
8234   void on_fade_pressed(LiVESButton * button, livespointer user_data) {
8235     // toolbar button (unblank background)
8236     if (mainw->fs && (!mainw->play_window || (prefs->play_monitor == widget_opts.monitor || capable->nmonitors == 1)))
8237       return;
8238     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->fade), !mainw->faded);
8239   }
8240 
8241 
8242   void on_fade_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8243     mainw->faded = !mainw->faded;
8244     if (LIVES_IS_PLAYING && (!mainw->fs || (prefs->play_monitor != widget_opts.monitor && mainw->play_window &&
8245                                             capable->nmonitors > 1))) {
8246       if (mainw->faded) {
8247         lives_widget_hide(mainw->framebar);
8248         fade_background();
8249       } else {
8250         unfade_background();
8251         lives_widget_show(mainw->frame1);
8252         lives_widget_show(mainw->frame2);
8253         lives_widget_show(mainw->eventbox3);
8254         lives_widget_show(mainw->eventbox4);
8255         if (!prefs->hide_framebar && !(prefs->hfbwnp && !LIVES_IS_PLAYING)) {
8256           lives_widget_show(mainw->framebar);
8257         }
8258         if (!mainw->multitrack && CURRENT_CLIP_IS_VALID) {
8259           redraw_timeline(mainw->current_file);
8260           show_playbar_labels(mainw->current_file);
8261 	// *INDENT-OFF*
8262       }}}
8263   // *INDENT-ON*
8264   }
8265 
8266 
8267   void on_showsubs_toggled(LiVESWidgetObject * obj, livespointer user_data) {
8268     prefs->show_subtitles = !prefs->show_subtitles;
8269     if (mainw->current_file > 0 && !mainw->multitrack) {
8270       if (mainw->play_window) {
8271         load_preview_image(FALSE);
8272       }
8273       showclipimgs();
8274     }
8275   }
8276 
8277 
8278   void on_boolean_toggled(LiVESWidgetObject * obj, livespointer user_data) {
8279     boolean *ppref = (boolean *)user_data;
8280     *ppref = !(*ppref);
8281   }
8282 
8283 
8284   void on_audio_toggled(LiVESWidget * tbutton, LiVESWidget * label) {
8285     boolean state;
8286     if (!LIVES_IS_INTERACTIVE) return;
8287 
8288     state = lives_toggle_tool_button_get_active(LIVES_TOGGLE_TOOL_BUTTON(tbutton));
8289     lives_widget_set_sensitive(tbutton, !state);
8290 
8291     if (tbutton == mainw->ext_audio_checkbutton) {
8292       pref_factory_bool(PREF_REC_EXT_AUDIO, state, TRUE);
8293     } else {
8294       pref_factory_bool(PREF_REC_EXT_AUDIO, !state, TRUE);
8295     }
8296   }
8297 
8298   void on_loop_video_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8299     if (mainw->current_file == 0) return;
8300     mainw->loop = !mainw->loop;
8301     lives_widget_set_sensitive(mainw->playclip, !LIVES_IS_PLAYING && clipboard);
8302     if (mainw->current_file > -1) find_when_to_stop();
8303   }
8304 
8305 
8306   void on_loop_button_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8307     if (mainw->multitrack) {
8308       lives_signal_handler_block(mainw->multitrack->loop_continue, mainw->multitrack->loop_cont_func);
8309       lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->multitrack->loop_continue), !mainw->loop_cont);
8310       lives_signal_handler_unblock(mainw->multitrack->loop_continue, mainw->multitrack->loop_cont_func);
8311     }
8312     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->loop_continue), !mainw->loop_cont);
8313   }
8314 
8315 
8316   void on_loop_cont_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8317 
8318     mainw->loop_cont = !mainw->loop_cont;
8319 
8320     if (mainw->loop_cont) {
8321       lives_widget_set_tooltip_text(mainw->m_loopbutton, _("Switch continuous looping off (o)"));
8322       lives_widget_set_opacity(mainw->m_loopbutton, 1.);
8323     } else {
8324       lives_widget_set_tooltip_text(mainw->m_loopbutton, _("Switch continuous looping on (o)"));
8325       lives_widget_set_opacity(mainw->m_loopbutton, .75);
8326     }
8327 
8328     lives_widget_set_sensitive(mainw->playclip, clipboard != NULL);
8329     if (mainw->current_file > -1) find_when_to_stop();
8330     else mainw->whentostop = NEVER_STOP;
8331 
8332     if (mainw->preview_box) {
8333       if (mainw->loop_cont)
8334         lives_widget_set_opacity(mainw->p_loopbutton, 1.);
8335       else
8336         lives_widget_set_opacity(mainw->p_loopbutton, .75);
8337     }
8338 
8339 #ifdef ENABLE_JACK
8340     if (prefs->audio_player == AUD_PLAYER_JACK) {
8341       if (mainw->jackd && (mainw->loop_cont || mainw->whentostop == NEVER_STOP)) {
8342         if (mainw->ping_pong && prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS)
8343           mainw->jackd->loop = AUDIO_LOOP_PINGPONG;
8344         else mainw->jackd->loop = AUDIO_LOOP_FORWARD;
8345       } else if (mainw->jackd) mainw->jackd->loop = AUDIO_LOOP_NONE;
8346     }
8347 #endif
8348 #ifdef HAVE_PULSE_AUDIO
8349     if (prefs->audio_player == AUD_PLAYER_PULSE) {
8350       if (mainw->pulsed && (mainw->loop_cont || mainw->whentostop == NEVER_STOP)) {
8351         if (mainw->ping_pong && prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS)
8352           mainw->pulsed->loop = AUDIO_LOOP_PINGPONG;
8353         else mainw->pulsed->loop = AUDIO_LOOP_FORWARD;
8354       } else if (mainw->pulsed) mainw->pulsed->loop = AUDIO_LOOP_NONE;
8355     }
8356 #endif
8357   }
8358 
8359 
8360   void on_ping_pong_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8361     mainw->ping_pong = !mainw->ping_pong;
8362 #ifdef ENABLE_JACK
8363     if (prefs->audio_player == AUD_PLAYER_JACK && mainw->jackd && mainw->jackd->loop != AUDIO_LOOP_NONE) {
8364       if (mainw->ping_pong && prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS) mainw->jackd->loop = AUDIO_LOOP_PINGPONG;
8365       else mainw->jackd->loop = AUDIO_LOOP_FORWARD;
8366     }
8367 #endif
8368 #ifdef HAVE_PULSE_AUDIO
8369     if (prefs->audio_player == AUD_PLAYER_PULSE && mainw->pulsed && mainw->pulsed->loop != AUDIO_LOOP_NONE) {
8370       if (mainw->ping_pong && prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS) mainw->pulsed->loop = AUDIO_LOOP_PINGPONG;
8371       else mainw->pulsed->loop = AUDIO_LOOP_FORWARD;
8372     }
8373 #endif
8374   }
8375 
8376 
8377   void on_volume_slider_value_changed(LiVESScaleButton * sbutton, livespointer user_data) {
8378     pref_factory_float(PREF_MASTER_VOLUME, lives_scale_button_get_value(sbutton), TRUE);
8379   }
8380 
8381 
8382   void on_mute_button_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8383     if (mainw->multitrack) {
8384       lives_signal_handler_block(mainw->multitrack->mute_audio, mainw->multitrack->mute_audio_func);
8385       lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->multitrack->mute_audio), !mainw->mute);
8386       lives_signal_handler_unblock(mainw->multitrack->mute_audio, mainw->multitrack->mute_audio_func);
8387     }
8388     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->mute_audio), !mainw->mute);
8389   }
8390 
8391 
8392   boolean mute_audio_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
8393                               livespointer user_data) {
8394     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->mute_audio), !mainw->mute);
8395     return TRUE;
8396   }
8397 
8398 
8399   void on_mute_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8400 
8401     mainw->mute = !mainw->mute;
8402 
8403     // change the mute icon
8404 
8405     if (mainw->mute) {
8406       lives_widget_set_opacity(mainw->m_mutebutton, 1.);
8407       if (mainw->preview_box) {
8408         lives_widget_set_opacity(mainw->p_mutebutton, 1.);
8409       }
8410 #ifdef ENABLE_JACK
8411       if (mainw->jackd && prefs->audio_player == AUD_PLAYER_JACK) {
8412         if (LIVES_IS_PLAYING) {
8413           if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO)) {
8414             weed_plant_t *event = get_last_frame_event(mainw->event_list);
8415             insert_audio_event_at(event, -1, mainw->jackd->playing_file, 0., 0.); // audio switch off
8416           }
8417           mainw->jackd->mute = TRUE;
8418           mainw->jackd->in_use = TRUE;
8419         }
8420       }
8421 #endif
8422 #ifdef HAVE_PULSE_AUDIO
8423       if (mainw->pulsed && prefs->audio_player == AUD_PLAYER_PULSE) {
8424         if (LIVES_IS_PLAYING) {
8425           if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO)) {
8426             weed_plant_t *event = get_last_frame_event(mainw->event_list);
8427             insert_audio_event_at(event, -1, mainw->pulsed->playing_file, 0., 0.); // audio switch off
8428           }
8429           mainw->pulsed->mute = TRUE;
8430           mainw->pulsed->in_use = TRUE;
8431         }
8432       }
8433 #endif
8434       lives_widget_set_tooltip_text(mainw->m_mutebutton, _("Unmute the audio (z)"));
8435       if (mainw->preview_box) lives_widget_set_tooltip_text(mainw->p_mutebutton, _("Unmute the audio (z)"));
8436     } else {
8437 #ifdef ENABLE_JACK
8438       if (mainw->jackd && prefs->audio_player == AUD_PLAYER_JACK) {
8439         if (LIVES_IS_PLAYING) {
8440           if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO)) {
8441             jack_get_rec_avals(mainw->jackd);
8442           }
8443           mainw->jackd->mute = FALSE;
8444           mainw->jackd->in_use = TRUE;
8445         }
8446       }
8447 #endif
8448 #ifdef HAVE_PULSE_AUDIO
8449       if (mainw->pulsed && prefs->audio_player == AUD_PLAYER_PULSE) {
8450         if (LIVES_IS_PLAYING) {
8451           if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO)) {
8452             pulse_get_rec_avals(mainw->pulsed);
8453           }
8454           mainw->pulsed->mute = FALSE;
8455           mainw->pulsed->in_use = TRUE;
8456         }
8457       }
8458 #endif
8459       lives_widget_set_opacity(mainw->m_mutebutton, .75);
8460       if (mainw->preview_box) {
8461         lives_widget_set_opacity(mainw->p_mutebutton, .75);
8462       }
8463 
8464       lives_widget_set_tooltip_text(mainw->m_mutebutton, _("Mute the audio (z)"));
8465       if (mainw->preview_box) lives_widget_set_tooltip_text(mainw->p_mutebutton, _("Mute the audio (z)"));
8466 
8467 #ifdef ENABLE_JACK
8468       if (prefs->audio_player == AUD_PLAYER_JACK && LIVES_IS_PLAYING && mainw->jackd) {
8469         mainw->jackd->mute = mainw->mute;
8470       }
8471 #endif
8472 #ifdef HAVE_PULSE_AUDIO
8473       if (prefs->audio_player == AUD_PLAYER_PULSE && LIVES_IS_PLAYING && mainw->pulsed) {
8474         mainw->pulsed->mute = mainw->mute;
8475       }
8476 #endif
8477     }
8478   }
8479 
8480 #define GEN_SPB_LINK(n, bit) case n: mainw->fx##n##_##bit = \
8481     lives_spin_button_get_value(LIVES_SPIN_BUTTON(spinbutton)); break
8482 #define GEN_SPB_LINK_I(n, bit) case n: mainw->fx##n##_##bit = \
8483     lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton)); break
8484 
8485   void on_spin_value_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
8486     // TODO - use array
8487     switch (LIVES_POINTER_TO_INT(user_data)) {
8488       GEN_SPB_LINK(1, val); GEN_SPB_LINK(2, val);
8489       GEN_SPB_LINK(3, val); GEN_SPB_LINK(4, val);
8490     default: break;
8491     }
8492   }
8493 
8494 
8495   void on_spin_start_value_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
8496     // generic
8497     // TODO - use array
8498     switch (LIVES_POINTER_TO_INT(user_data)) {
8499       GEN_SPB_LINK_I(1, start); GEN_SPB_LINK_I(2, start);
8500       GEN_SPB_LINK_I(3, start); GEN_SPB_LINK_I(4, start);
8501     default: break;
8502     }
8503   }
8504 
8505 
8506   void on_spin_step_value_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
8507     // generic
8508     // TODO - use array
8509     switch (LIVES_POINTER_TO_INT(user_data)) {
8510       GEN_SPB_LINK_I(1, step); GEN_SPB_LINK_I(2, step);
8511       GEN_SPB_LINK_I(3, step); GEN_SPB_LINK_I(4, step);
8512     default: break;
8513     }
8514   }
8515 
8516 
8517   void on_spin_end_value_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
8518     // generic
8519     // TODO - use array
8520     switch (LIVES_POINTER_TO_INT(user_data)) {
8521       GEN_SPB_LINK_I(1, end); GEN_SPB_LINK_I(2, end);
8522       GEN_SPB_LINK_I(3, end); GEN_SPB_LINK_I(4, end);
8523     default: break;
8524     }
8525   }
8526 
8527 
8528   void on_rev_clipboard_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8529     // reverse the clipboard
8530     char *com;
8531     int current_file = mainw->current_file;
8532     mainw->current_file = 0;
8533 
8534     if (!check_if_non_virtual(0, 1, cfile->frames)) {
8535       lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
8536       char *msg = (_("Pulling frames from clipboard..."));
8537       if (!(cdata->seek_flag & LIVES_SEEK_FAST)) {
8538         if (realize_all_frames(0, msg, FALSE) <= 0) {
8539           mainw->current_file = current_file;
8540           lives_free(msg);
8541           sensitize();
8542           return;
8543         }
8544         lives_free(msg);
8545       }
8546     }
8547 
8548     d_print(_("Reversing clipboard..."));
8549     com = lives_strdup_printf("%s reverse \"%s\" %d %d \"%s\"", prefs->backend, clipboard->handle, 1, clipboard->frames,
8550                               get_image_ext_for_type(cfile->img_type));
8551 
8552     lives_rm(cfile->info_file);
8553     lives_system(com, FALSE);
8554     lives_free(com);
8555 
8556     if (!THREADVAR(com_failed)) {
8557       cfile->progress_start = 1;
8558       cfile->progress_end = cfile->frames;
8559 
8560       // show a progress dialog, not cancellable
8561       do_progress_dialog(TRUE, FALSE, _("Reversing clipboard"));
8562     }
8563 
8564     if (THREADVAR(com_failed) || mainw->error) d_print_failed();
8565     else {
8566       if (clipboard->frame_index) reverse_frame_index(0);
8567       d_print_done();
8568     }
8569     clipboard->arate = -clipboard->arate;
8570     clipboard->aseek_pos = clipboard->afilesize;
8571     mainw->current_file = current_file;
8572     sensitize();
8573   }
8574 
8575 
8576   void on_load_subs_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8577     char *subfile;
8578     char *filt[] = LIVES_SUBS_FILTER;
8579     char filename[512];
8580     char *subfname, *isubfname;
8581     lives_subtitle_type_t subtype = SUBTITLE_TYPE_NONE;
8582     char *lfile_name;
8583     char *ttl;
8584 
8585     if (!CURRENT_CLIP_IS_VALID) return;
8586 
8587     if (cfile->subt) if (!do_existing_subs_warning()) return;
8588 
8589     // try to repaint the screen, as it may take a few seconds to get a directory listing
8590     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
8591 
8592     ttl = (_("Load Subtitles"));
8593 
8594     if (*mainw->vid_load_dir) {
8595       subfile = choose_file(mainw->vid_load_dir, NULL, filt, LIVES_FILE_CHOOSER_ACTION_OPEN, ttl, NULL);
8596     } else subfile = choose_file(NULL, NULL, filt, LIVES_FILE_CHOOSER_ACTION_OPEN, ttl, NULL);
8597     lives_free(ttl);
8598 
8599     if (!subfile) return; // cancelled
8600 
8601     lives_snprintf(filename, 512, "%s", subfile);
8602     lives_free(subfile);
8603 
8604     get_filename(filename, FALSE); // strip extension
8605     isubfname = lives_strdup_printf("%s.%s", filename, LIVES_FILE_EXT_SRT);
8606     lfile_name = lives_filename_from_utf8(isubfname, -1, NULL, NULL, NULL);
8607 
8608     if (lives_file_test(lfile_name, LIVES_FILE_TEST_EXISTS)) {
8609       subfname = lives_build_filename(prefs->workdir, cfile->handle, SUBS_FILENAME "." LIVES_FILE_EXT_SRT, NULL);
8610       subtype = SUBTITLE_TYPE_SRT;
8611     } else {
8612       lives_free(isubfname);
8613       lives_free(lfile_name);
8614       isubfname = lives_strdup_printf("%s.%s", filename, LIVES_FILE_EXT_SUB);
8615       lfile_name = lives_filename_from_utf8(isubfname, -1, NULL, NULL, NULL);
8616 
8617       if (lives_file_test(isubfname, LIVES_FILE_TEST_EXISTS)) {
8618         subfname = lives_build_filename(prefs->workdir, cfile->handle, SUBS_FILENAME "." LIVES_FILE_EXT_SUB, NULL);
8619         subtype = SUBTITLE_TYPE_SUB;
8620       } else {
8621         lives_free(isubfname);
8622         do_invalid_subs_error();
8623         lives_free(lfile_name);
8624         return;
8625       }
8626     }
8627 
8628     if (cfile->subt) {
8629       // erase any existing subs
8630       on_erase_subs_activate(NULL, NULL);
8631       subtitles_free(cfile);
8632     }
8633 
8634     lives_cp(lfile_name, subfname);
8635 
8636     if (THREADVAR(com_failed)) {
8637       lives_free(subfname);
8638       lives_free(isubfname);
8639       lives_free(lfile_name);
8640       return;
8641     }
8642 
8643     subtitles_init(cfile, subfname, subtype);
8644     lives_free(subfname);
8645 
8646     if (!mainw->multitrack) {
8647       // force update
8648       switch_to_file(0, mainw->current_file);
8649     }
8650 
8651     d_print(_("Loaded subtitle file: %s\n"), isubfname);
8652 
8653     lives_free(isubfname);
8654     lives_free(lfile_name);
8655   }
8656 
8657 
8658   void on_save_subs_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8659     char *subfile;
8660     char xfname[512];
8661     char xfname2[512];
8662 
8663     // try to repaint the screen, as it may take a few seconds to get a directory listing
8664     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
8665 
8666     lives_snprintf(xfname, 512, "%s", mainw->subt_save_file);
8667     get_dirname(xfname);
8668 
8669     lives_snprintf(xfname2, 512, "%s", mainw->subt_save_file);
8670     get_basename(xfname2);
8671 
8672     subfile = choose_file(xfname, xfname2, NULL, LIVES_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
8673 
8674     if (!subfile) return; // cancelled
8675 
8676     lives_free(subfile);
8677   }
8678 
8679 
8680   void on_erase_subs_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8681     char *sfname;
8682 
8683     if (!CURRENT_CLIP_IS_VALID || !cfile->subt) return;
8684 
8685     if (menuitem)
8686       if (!do_erase_subs_warning()) return;
8687 
8688     switch (cfile->subt->type) {
8689     case SUBTITLE_TYPE_SRT:
8690       sfname = lives_build_filename(prefs->workdir, cfile->handle, SUBS_FILENAME "." LIVES_FILE_EXT_SRT, NULL);
8691       break;
8692 
8693     case SUBTITLE_TYPE_SUB:
8694       sfname = lives_build_filename(prefs->workdir, cfile->handle, SUBS_FILENAME "." LIVES_FILE_EXT_SUB, NULL);
8695       break;
8696 
8697     default:
8698       return;
8699     }
8700 
8701     subtitles_free(cfile);
8702 
8703     lives_rm(sfname);
8704     lives_free(sfname);
8705 
8706     if (menuitem) {
8707       // force update
8708       if (!mainw->multitrack) {
8709         switch_to_file(0, mainw->current_file);
8710       }
8711       d_print(_("Subtitles were erased.\n"));
8712     }
8713   }
8714 
8715 
8716   void on_load_audio_activate(LiVESMenuItem * menuitem, livespointer user_data) {
8717     LiVESWidget *chooser;
8718     char *filt[] = LIVES_AUDIO_LOAD_FILTER;
8719     LiVESResponseType resp;
8720     mainw->mt_needs_idlefunc = FALSE;
8721 
8722     if (mainw->multitrack) {
8723       if (mainw->multitrack->idlefunc > 0) {
8724         lives_source_remove(mainw->multitrack->idlefunc);
8725         mainw->multitrack->idlefunc = 0;
8726         mainw->mt_needs_idlefunc = TRUE;
8727       }
8728       mt_desensitise(mainw->multitrack);
8729       lives_widget_set_sensitive(mainw->multitrack->playall, TRUE);
8730       lives_widget_set_sensitive(mainw->m_playbutton, TRUE);
8731     }
8732 
8733     chooser = choose_file_with_preview((*mainw->audio_dir) ? mainw->audio_dir : NULL, _("Select Audio File"), filt,
8734                                        LIVES_FILE_SELECTION_AUDIO_ONLY);
8735 
8736     resp = lives_dialog_run(LIVES_DIALOG(chooser));
8737 
8738     end_fs_preview();
8739 
8740     if (resp != LIVES_RESPONSE_ACCEPT) on_filechooser_cancel_clicked(chooser);
8741     else on_open_new_audio_clicked(LIVES_FILE_CHOOSER(chooser), NULL);
8742   }
8743 
8744 
8745   void on_open_new_audio_clicked(LiVESFileChooser * chooser, livespointer user_data) {
8746     // open audio file
8747     // also called from osc.c
8748 
8749     char *a_type;
8750     char *com, *tmp;
8751     char **array;
8752 
8753     uint32_t chk_mask = 0;
8754 
8755     int oundo_start;
8756     int oundo_end;
8757     int israw = 1;
8758     int asigned, aendian;
8759 
8760     boolean bad_header = FALSE;
8761     boolean preparse = FALSE;
8762     boolean gotit = FALSE;
8763 
8764     register int i;
8765 
8766     if (!CURRENT_CLIP_IS_VALID) return;
8767 
8768     tmp = (_("Loading new audio"));
8769     chk_mask = WARN_MASK_LAYOUT_DELETE_AUDIO | WARN_MASK_LAYOUT_ALTER_AUDIO;
8770     if (!check_for_layout_errors(tmp, mainw->current_file, 1, 0, &chk_mask)) {
8771       lives_free(tmp);
8772       if (mainw->multitrack) {
8773         mt_sensitise(mainw->multitrack);
8774         maybe_add_mt_idlefunc();
8775       }
8776       return;
8777     }
8778 
8779     cfile->undo_arate = cfile->arate;
8780     cfile->undo_achans = cfile->achans;
8781     cfile->undo_asampsize = cfile->asampsize;
8782     cfile->undo_signed_endian = cfile->signed_endian;
8783     cfile->undo_arps = cfile->arps;
8784 
8785     oundo_start = cfile->undo_start;
8786     oundo_end = cfile->undo_end;
8787 
8788     if (!user_data) {
8789       char *filename = lives_file_chooser_get_filename(chooser);
8790       lives_snprintf(file_name, PATH_MAX, "%s", (tmp = lives_filename_to_utf8(filename, -1, NULL, NULL, NULL)));
8791       lives_free(filename);
8792       lives_free(tmp);
8793     } else lives_snprintf(file_name, PATH_MAX, "%s", (char *)user_data);
8794 
8795     lives_snprintf(mainw->audio_dir, PATH_MAX, "%s", file_name);
8796     get_dirname(mainw->audio_dir);
8797     end_fs_preview();
8798     lives_widget_destroy(LIVES_WIDGET(chooser));
8799     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
8800 
8801     a_type = get_extension(file_name);
8802 
8803     if (strlen(a_type)) {
8804       char *filt[] = LIVES_AUDIO_LOAD_FILTER;
8805       for (i = 0; filt[i]; i++) {
8806         if (!lives_ascii_strcasecmp(a_type, filt[i] + 2)) gotit = TRUE; // skip past "*." in filt
8807       }
8808     }
8809 
8810     if (gotit) {
8811       com = lives_strdup_printf("%s audioopen \"%s\" \"%s\"", prefs->backend, cfile->handle,
8812                                 (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)));
8813       lives_free(tmp);
8814     } else {
8815       lives_free(a_type);
8816       do_audio_import_error();
8817 
8818       if (mainw->multitrack) {
8819         mt_sensitise(mainw->multitrack);
8820         maybe_add_mt_idlefunc();
8821       }
8822       unbuffer_lmap_errors(FALSE);
8823       return;
8824     }
8825 
8826     if (!lives_ascii_strncasecmp(a_type, LIVES_FILE_EXT_WAV, 3)) israw = 0;
8827 
8828     if (HAS_EXTERNAL_PLAYER) {
8829       if (read_file_details(file_name, TRUE, FALSE)) {
8830         if (get_token_count(mainw->msg, '|') >= 14) {
8831           array = lives_strsplit(mainw->msg, "|", -1);
8832           cfile->arate = atoi(array[9]);
8833           cfile->achans = atoi(array[10]);
8834           cfile->asampsize = atoi(array[11]);
8835           cfile->signed_endian = get_signed_endian(atoi(array[12]), atoi(array[13]));
8836           lives_strfreev(array);
8837           preparse = TRUE;
8838         }
8839       }
8840     }
8841 
8842     if (!preparse) {
8843       // TODO !!! - need some way to identify audio without invoking mplayer
8844       cfile->arate = cfile->arps = DEFAULT_AUDIO_RATE;
8845       cfile->achans = DEFAULT_AUDIO_CHANS;
8846       cfile->asampsize = DEFAULT_AUDIO_SAMPS;
8847       cfile->signed_endian = mainw->endian;
8848     }
8849 
8850     if (cfile->undo_arate > 0) cfile->arps = cfile->undo_arps / cfile->undo_arate * cfile->arate;
8851     else cfile->arps = cfile->arate;
8852 
8853     d_print(""); // force switchtext
8854     d_print(_("Opening audio %s, type %s..."), file_name, a_type);
8855     lives_free(a_type);
8856 
8857     lives_rm(cfile->info_file);
8858     lives_system(com, FALSE);
8859     lives_free(com);
8860 
8861     if (THREADVAR(com_failed)) {
8862       cfile->arate = cfile->undo_arate;
8863       cfile->achans = cfile->undo_achans;
8864       cfile->asampsize = cfile->undo_asampsize;
8865       cfile->signed_endian = cfile->undo_signed_endian;
8866       cfile->arps = cfile->undo_arps;
8867       cfile->undo_start = oundo_start;
8868       cfile->undo_end = oundo_end;
8869       sensitize();
8870       if (mainw->multitrack) {
8871         mt_sensitise(mainw->multitrack);
8872         maybe_add_mt_idlefunc();
8873       }
8874       reget_afilesize(mainw->current_file);
8875       unbuffer_lmap_errors(FALSE);
8876       return;
8877     }
8878 
8879     cfile->opening = cfile->opening_audio = cfile->opening_only_audio = TRUE;
8880 
8881     cfile->undo_start = 1;
8882     cfile->undo_end = cfile->frames;
8883 
8884     // show audio [opening...] in main window
8885     get_play_times();
8886 
8887     if (!(do_progress_dialog(TRUE, TRUE, _("Opening audio")))) {
8888       lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
8889 
8890       mainw->cancelled = CANCEL_NONE;
8891       mainw->error = FALSE;
8892       lives_rm(cfile->info_file);
8893       com = lives_strdup_printf("%s cancel_audio \"%s\"", prefs->backend, cfile->handle);
8894       lives_system(com, FALSE);
8895       do_auto_dialog(_("Cancelling"), 0);
8896       lives_free(com);
8897       cfile->opening_audio = cfile->opening = cfile->opening_only_audio = FALSE;
8898       cfile->arate = cfile->undo_arate;
8899       cfile->achans = cfile->undo_achans;
8900       cfile->asampsize = cfile->undo_asampsize;
8901       cfile->signed_endian = cfile->undo_signed_endian;
8902       cfile->arps = cfile->undo_arps;
8903       cfile->undo_start = oundo_start;
8904       cfile->undo_end = oundo_end;
8905       sensitize();
8906       if (mainw->multitrack) {
8907         mt_sensitise(mainw->multitrack);
8908         maybe_add_mt_idlefunc();
8909       }
8910       reget_afilesize(mainw->current_file);
8911       if (mainw->error) d_print_failed();
8912       unbuffer_lmap_errors(FALSE);
8913       return;
8914     }
8915 
8916     cfile->opening_audio = cfile->opening = cfile->opening_only_audio = FALSE;
8917 
8918     cfile->afilesize = 0;
8919 
8920     if (get_token_count(mainw->msg, '|') > 6) {
8921       array = lives_strsplit(mainw->msg, "|", 7);
8922       cfile->arate = atoi(array[1]);
8923       cfile->achans = atoi(array[2]);
8924       cfile->asampsize = atoi(array[3]);
8925       cfile->signed_endian = get_signed_endian(atoi(array[4]), atoi(array[5]));
8926       cfile->afilesize = atol(array[6]);
8927       lives_strfreev(array);
8928 
8929       if (cfile->undo_arate > 0) cfile->arps = cfile->undo_arps / cfile->undo_arate * cfile->arate;
8930       else cfile->arps = cfile->arate;
8931     }
8932 
8933     /// not sure why, but this messes up mainw->msg...
8934     lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
8935 
8936     if (cfile->afilesize == 0) {
8937       d_print_failed();
8938 
8939       mainw->cancelled = CANCEL_NONE;
8940       mainw->error = FALSE;
8941       lives_rm(cfile->info_file);
8942 
8943       com = lives_strdup_printf("%s cancel_audio \"%s\"", prefs->backend, cfile->handle);
8944 
8945       lives_system(com, FALSE);
8946       lives_free(com);
8947 
8948       if (!THREADVAR(com_failed)) do_auto_dialog(_("Cancelling"), 0);
8949 
8950       cfile->arate = cfile->undo_arate;
8951       cfile->achans = cfile->undo_achans;
8952       cfile->asampsize = cfile->undo_asampsize;
8953       cfile->signed_endian = cfile->undo_signed_endian;
8954       cfile->arps = cfile->undo_arps;
8955       cfile->undo_start = oundo_start;
8956       cfile->undo_end = oundo_end;
8957       sensitize();
8958       if (mainw->multitrack) {
8959         mt_sensitise(mainw->multitrack);
8960         maybe_add_mt_idlefunc();
8961       }
8962       reget_afilesize(mainw->current_file);
8963       unbuffer_lmap_errors(FALSE);
8964       return;
8965     }
8966 
8967     cfile->changed = TRUE;
8968     d_print_done();
8969 
8970     d_print(P_("New audio: %d Hz %d channel %d bps\n", "New audio: %d Hz %d channels %d bps\n",
8971                cfile->achans),
8972             cfile->arate, cfile->achans, cfile->asampsize);
8973 
8974     mainw->cancelled = CANCEL_NONE;
8975     mainw->error = FALSE;
8976     lives_rm(cfile->info_file);
8977 
8978     com = lives_strdup_printf("%s commit_audio \"%s\" %d", prefs->backend, cfile->handle, israw);
8979     lives_system(com, FALSE);
8980     lives_free(com);
8981 
8982     if (THREADVAR(com_failed)) {
8983       cfile->arate = cfile->undo_arate;
8984       cfile->achans = cfile->undo_achans;
8985       cfile->asampsize = cfile->undo_asampsize;
8986       cfile->signed_endian = cfile->undo_signed_endian;
8987       cfile->arps = cfile->undo_arps;
8988       cfile->undo_start = oundo_start;
8989       cfile->undo_end = oundo_end;
8990       sensitize();
8991       if (mainw->multitrack) {
8992         mt_sensitise(mainw->multitrack);
8993         maybe_add_mt_idlefunc();
8994       }
8995       reget_afilesize(mainw->current_file);
8996       unbuffer_lmap_errors(FALSE);
8997       return;
8998     }
8999 
9000     if (!do_auto_dialog(_("Committing audio"), 0)) {
9001       //cfile->may_be_damaged=TRUE;
9002       d_print_failed();
9003       if (mainw->multitrack) {
9004         mt_sensitise(mainw->multitrack);
9005         maybe_add_mt_idlefunc();
9006       }
9007       unbuffer_lmap_errors(FALSE);
9008       return;
9009     }
9010 
9011     if (prefs->save_directories) {
9012       set_utf8_pref(PREF_AUDIO_DIR, mainw->audio_dir);
9013     }
9014     if (!prefs->conserve_space) {
9015       cfile->undo_action = UNDO_NEW_AUDIO;
9016       set_undoable(_("New Audio"), TRUE);
9017     }
9018 
9019     asigned = !(cfile->signed_endian & AFORM_UNSIGNED);
9020     aendian = cfile->signed_endian & AFORM_BIG_ENDIAN;
9021 
9022     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ARATE, &cfile->arps)) bad_header = TRUE;
9023     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_ARATE, &cfile->arate)) bad_header = TRUE;
9024     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ACHANS, &cfile->achans)) bad_header = TRUE;
9025     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASAMPS, &cfile->asampsize)) bad_header = TRUE;
9026     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_AENDIAN, &aendian)) bad_header = TRUE;
9027     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASIGNED, &asigned)) bad_header = TRUE;
9028 
9029     if (bad_header) do_header_write_error(mainw->current_file);
9030 
9031     if (!mainw->multitrack) {
9032       switch_to_file(mainw->current_file, mainw->current_file);
9033     }
9034 
9035     if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
9036 
9037     if (mainw->multitrack) {
9038       mt_sensitise(mainw->multitrack);
9039       maybe_add_mt_idlefunc();
9040     }
9041   }
9042 
9043 
9044   void on_load_cdtrack_activate(LiVESMenuItem * menuitem, livespointer user_data) {
9045     LiVESWidget *cdtrack_dialog;
9046 
9047     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
9048 
9049     if (!strlen(prefs->cdplay_device)) {
9050       do_cd_error_dialog();
9051       return;
9052     }
9053 
9054     mainw->fx1_val = 1;
9055     cdtrack_dialog = create_cdtrack_dialog(LIVES_DEVICE_CD, NULL);
9056     lives_widget_show_all(cdtrack_dialog);
9057   }
9058 
9059 
9060   void on_eject_cd_activate(LiVESMenuItem * menuitem, livespointer user_data) {
9061     char *com;
9062 
9063     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
9064 
9065     if (!strlen(prefs->cdplay_device)) {
9066       do_cd_error_dialog();
9067       return;
9068     }
9069 
9070     if (strlen(capable->eject_cmd)) {
9071       com = lives_strdup_printf("%s \"%s\"", capable->eject_cmd, prefs->cdplay_device);
9072 
9073       lives_system(com, TRUE);
9074       lives_free(com);
9075     }
9076   }
9077 
9078 
9079   void on_load_cdtrack_ok_clicked(LiVESButton * button, livespointer user_data) {
9080     char *com;
9081     char **array;
9082 
9083     boolean was_new = FALSE;
9084 
9085     uint32_t chk_mask = 0;
9086 
9087     int new_file = mainw->first_free_file;
9088     int asigned, endian;
9089 
9090     boolean bad_header = FALSE;
9091     boolean needs_idlefunc = mainw->mt_needs_idlefunc;
9092     mainw->mt_needs_idlefunc = FALSE;
9093     lives_general_button_clicked(button, NULL);
9094     mainw->mt_needs_idlefunc = needs_idlefunc;
9095 
9096     if (CURRENT_CLIP_IS_VALID) {
9097       char *tmp = (_("Loading new audio"));
9098       chk_mask = WARN_MASK_LAYOUT_DELETE_AUDIO | WARN_MASK_LAYOUT_ALTER_AUDIO;
9099       if (!check_for_layout_errors(tmp, mainw->current_file, 1, 0, &chk_mask)) {
9100         lives_free(tmp);
9101         return;
9102       }
9103       lives_free(tmp);
9104     }
9105 
9106     d_print(_("Opening CD track %d from %s..."), (int)mainw->fx1_val, prefs->cdplay_device);
9107 
9108     if (!CURRENT_CLIP_IS_VALID) {
9109       if (!get_new_handle(new_file, lives_strdup_printf(_("CD track %d"), (int)mainw->fx1_val))) {
9110         unbuffer_lmap_errors(FALSE);
9111         return;
9112       }
9113 
9114       mainw->current_file = new_file;
9115       lives_snprintf(cfile->type, 40, "CD track %d on %s", (int)mainw->fx1_val, prefs->cdplay_device);
9116       update_play_times();
9117       add_to_clipmenu();
9118       was_new = TRUE;
9119       cfile->opening = cfile->opening_audio = cfile->opening_only_audio = TRUE;
9120       cfile->hsize = DEF_FRAME_HSIZE;
9121       cfile->vsize = DEF_FRAME_VSIZE;
9122     } else {
9123       cfile->undo_arate = cfile->arate;
9124       cfile->undo_achans = cfile->achans;
9125       cfile->undo_asampsize = cfile->asampsize;
9126       cfile->undo_signed_endian = cfile->signed_endian;
9127       cfile->undo_arps = cfile->arps;
9128     }
9129 
9130     com = lives_strdup_printf("%s cdopen \"%s\" %d", prefs->backend, cfile->handle, (int)mainw->fx1_val);
9131 
9132     lives_rm(cfile->info_file);
9133     lives_system(com, FALSE);
9134     lives_free(com);
9135 
9136     if (THREADVAR(com_failed)) {
9137       cfile->arate = cfile->undo_arate;
9138       cfile->achans = cfile->undo_achans;
9139       cfile->asampsize = cfile->undo_asampsize;
9140       cfile->signed_endian = cfile->undo_signed_endian;
9141       cfile->arps = cfile->undo_arps;
9142 
9143       sensitize();
9144       reget_afilesize(mainw->current_file);
9145       if (was_new) close_current_file(0);
9146       unbuffer_lmap_errors(FALSE);
9147       return;
9148     }
9149 
9150     if (!(do_progress_dialog(TRUE, TRUE, _("Opening CD track...")))) {
9151       lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
9152 
9153       if (!was_new) {
9154         mainw->cancelled = CANCEL_NONE;
9155         mainw->error = FALSE;
9156         lives_rm(cfile->info_file);
9157 
9158         com = lives_strdup_printf("%s cancel_audio \"%s\"", prefs->backend, cfile->handle);
9159         lives_system(com, FALSE);
9160         lives_free(com);
9161 
9162         if (!THREADVAR(com_failed)) do_auto_dialog(_("Cancelling"), 0);
9163 
9164         cfile->arate = cfile->undo_arate;
9165         cfile->achans = cfile->undo_achans;
9166         cfile->asampsize = cfile->undo_asampsize;
9167         cfile->signed_endian = cfile->undo_signed_endian;
9168         cfile->arps = cfile->undo_arps;
9169 
9170         sensitize();
9171         reget_afilesize(mainw->current_file);
9172       }
9173 
9174       if (was_new) close_current_file(0);
9175 
9176       if (mainw->error) {
9177         d_print_failed();
9178       }
9179 
9180       unbuffer_lmap_errors(FALSE);
9181       return;
9182     }
9183 
9184     lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
9185 
9186     if (mainw->error) {
9187       d_print(_("Error loading CD track\n"));
9188 
9189       lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
9190 
9191       if (!was_new) {
9192         com = lives_strdup_printf("%s cancel_audio \"%s\"", prefs->backend, cfile->handle);
9193         mainw->cancelled = CANCEL_NONE;
9194         mainw->error = FALSE;
9195         lives_rm(cfile->info_file);
9196         lives_system(com, FALSE);
9197         lives_free(com);
9198 
9199         if (!THREADVAR(com_failed)) do_auto_dialog(_("Cancelling"), 0);
9200 
9201         cfile->arate = cfile->undo_arate;
9202         cfile->achans = cfile->undo_achans;
9203         cfile->asampsize = cfile->undo_asampsize;
9204         cfile->signed_endian = cfile->undo_signed_endian;
9205         cfile->arps = cfile->undo_arps;
9206 
9207         sensitize();
9208         reget_afilesize(mainw->current_file);
9209       }
9210 
9211       if (was_new) close_current_file(0);
9212       unbuffer_lmap_errors(FALSE);
9213       return;
9214     }
9215 
9216     array = lives_strsplit(mainw->msg, "|", 5);
9217     cfile->arate = atoi(array[1]);
9218     cfile->achans = atoi(array[2]);
9219     cfile->asampsize = atoi(array[3]);
9220     cfile->afilesize = strtol(array[4], NULL, 10);
9221     lives_strfreev(array);
9222 
9223     if (!was_new && cfile->undo_arate > 0) cfile->arps = cfile->undo_arps / cfile->undo_arate * cfile->arate;
9224     else cfile->arps = cfile->arate;
9225 
9226     asigned = !(cfile->signed_endian & AFORM_UNSIGNED);
9227     endian = cfile->signed_endian & AFORM_BIG_ENDIAN;
9228 
9229     if (cfile->afilesize == 0l) {
9230       d_print(_("Error loading CD track\n"));
9231 
9232       if (!was_new) {
9233         com = lives_strdup_printf("%s cancel_audio \"%s\"", prefs->backend, cfile->handle);
9234         mainw->cancelled = CANCEL_NONE;
9235         mainw->error = FALSE;
9236         lives_rm(cfile->info_file);
9237         lives_system(com, FALSE);
9238         lives_free(com);
9239 
9240         if (!THREADVAR(com_failed)) do_auto_dialog(_("Cancelling"), 0);
9241 
9242         cfile->achans = cfile->undo_achans;
9243         cfile->arate = cfile->undo_arate;
9244         cfile->arps = cfile->undo_arps;
9245         cfile->asampsize = cfile->undo_asampsize;
9246         cfile->signed_endian = cfile->undo_signed_endian;
9247 
9248         reget_afilesize(mainw->current_file);
9249       }
9250 
9251       if (was_new) close_current_file(0);
9252       unbuffer_lmap_errors(FALSE);
9253       return;
9254     }
9255 
9256     cfile->opening = cfile->opening_audio = cfile->opening_only_audio = FALSE;
9257 
9258     mainw->cancelled = CANCEL_NONE;
9259     mainw->error = FALSE;
9260     lives_rm(cfile->info_file);
9261 
9262     com = lives_strdup_printf("%s commit_audio \"%s\"", prefs->backend, cfile->handle);
9263     lives_system(com, FALSE);
9264     lives_free(com);
9265 
9266     if (THREADVAR(com_failed)) {
9267       d_print_failed();
9268       cfile->achans = cfile->undo_achans;
9269       cfile->arate = cfile->undo_arate;
9270       cfile->arps = cfile->undo_arps;
9271       cfile->asampsize = cfile->undo_asampsize;
9272       cfile->signed_endian = cfile->undo_signed_endian;
9273 
9274       reget_afilesize(mainw->current_file);
9275 
9276       if (was_new) close_current_file(0);
9277       unbuffer_lmap_errors(FALSE);
9278       return;
9279     }
9280 
9281     if (!do_auto_dialog(_("Committing audio"), 0)) {
9282       d_print_failed();
9283       unbuffer_lmap_errors(FALSE);
9284       return;
9285     }
9286 
9287     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ARATE, &cfile->arps)) bad_header = TRUE;
9288     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_ARATE, &cfile->arate)) bad_header = TRUE;
9289     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ACHANS, &cfile->achans)) bad_header = TRUE;
9290     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASIGNED, &asigned)) bad_header = TRUE;
9291     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_AENDIAN, &endian)) bad_header = TRUE;
9292     if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASAMPS, &cfile->asampsize)) bad_header = TRUE;
9293 
9294     if (bad_header) do_header_write_error(mainw->current_file);
9295 
9296     reget_afilesize(mainw->current_file);
9297     cfile->changed = TRUE;
9298     d_print_done();
9299     d_print(P_("New audio: %d Hz %d channel %d bps\n", "New audio: %d Hz %d channels %d bps\n", cfile->achans),
9300             cfile->arate, cfile->achans, cfile->asampsize);
9301 
9302     if (!was_new) {
9303       if (!prefs->conserve_space) {
9304         cfile->undo_action = UNDO_NEW_AUDIO;
9305         set_undoable(_("New Audio"), TRUE);
9306       }
9307     }
9308 
9309     lives_widget_set_sensitive(mainw->loop_video, TRUE);
9310 
9311     lives_notify(LIVES_OSC_NOTIFY_CLIP_OPENED, "");
9312 
9313     if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
9314   }
9315 
9316 
9317   void on_load_vcd_ok_clicked(LiVESButton * button, livespointer user_data) {
9318     boolean needs_idlefunc = mainw->mt_needs_idlefunc;
9319     mainw->mt_needs_idlefunc = FALSE;
9320     lives_general_button_clicked(button, NULL);
9321     mainw->mt_needs_idlefunc = needs_idlefunc;
9322 
9323     if (LIVES_POINTER_TO_INT(user_data) == LIVES_DEVICE_DVD) {
9324       lives_snprintf(file_name, PATH_MAX, "dvd://%d", (int)mainw->fx1_val);
9325       lives_freep((void **)&mainw->file_open_params);
9326       if (USE_MPV) mainw->file_open_params = lives_strdup_printf("--chapter=%d --aid=%d", (int)mainw->fx2_val, (int)mainw->fx3_val);
9327       else mainw->file_open_params = lives_strdup_printf("-chapter %d -aid %d", (int)mainw->fx2_val, (int)mainw->fx3_val);
9328     } else {
9329       lives_snprintf(file_name, PATH_MAX, "vcd://%d", (int)mainw->fx1_val);
9330     }
9331     open_sel_range_activate(0, 0.);
9332   }
9333 
9334 
9335   void popup_lmap_errors(LiVESMenuItem * menuitem, livespointer user_data) {
9336     // popup layout map errors dialog
9337     LiVESWidget *vbox;
9338     LiVESWidget *button;
9339     text_window *textwindow;
9340 
9341     uint32_t chk_mask = 0;
9342 
9343     unbuffer_lmap_errors(TRUE);
9344 
9345     if (!menuitem && user_data) {
9346       if (prefs->warning_mask & WARN_MASK_LAYOUT_POPUP) return;
9347       chk_mask = (uint32_t)LIVES_POINTER_TO_INT(user_data);
9348       if (((chk_mask ^ prefs->warning_mask) & chk_mask) == 0) return;
9349     }
9350 
9351     widget_opts.expand = LIVES_EXPAND_EXTRA_WIDTH | LIVES_EXPAND_DEFAULT_HEIGHT;
9352     textwindow = create_text_window(_("Layout Errors"), NULL, mainw->layout_textbuffer, FALSE);
9353     widget_opts.expand = LIVES_EXPAND_DEFAULT;
9354 
9355     vbox = lives_dialog_get_content_area(LIVES_DIALOG(textwindow->dialog));
9356 
9357     add_warn_check(LIVES_BOX(vbox), WARN_MASK_LAYOUT_POPUP);
9358 
9359     button = lives_dialog_add_button_from_stock(LIVES_DIALOG(textwindow->dialog), LIVES_STOCK_CLOSE, _("_Close Window"),
9360              LIVES_RESPONSE_OK);
9361 
9362     lives_signal_sync_connect(LIVES_GUI_OBJECT(button), LIVES_WIDGET_CLICKED_SIGNAL,
9363                               LIVES_GUI_CALLBACK(lives_general_button_clicked),
9364                               textwindow);
9365 
9366     lives_container_set_border_width(LIVES_CONTAINER(button), widget_opts.border_width);
9367 
9368     textwindow->clear_button = lives_dialog_add_button_from_stock(LIVES_DIALOG(textwindow->dialog), LIVES_STOCK_CLEAR,
9369                                _("Clear _Errors"),
9370                                LIVES_RESPONSE_CANCEL);
9371 
9372     lives_signal_sync_connect(LIVES_GUI_OBJECT(textwindow->clear_button), LIVES_WIDGET_CLICKED_SIGNAL,
9373                               LIVES_GUI_CALLBACK(on_lerrors_clear_clicked),
9374                               LIVES_INT_TO_POINTER(FALSE));
9375 
9376     lives_container_set_border_width(LIVES_CONTAINER(textwindow->clear_button), widget_opts.border_width);
9377 
9378     textwindow->delete_button = lives_dialog_add_button_from_stock(LIVES_DIALOG(textwindow->dialog), LIVES_STOCK_DELETE,
9379                                 _("_Delete affected layouts"), LIVES_RESPONSE_CANCEL);
9380 
9381     lives_button_box_set_layout(LIVES_BUTTON_BOX(lives_widget_get_parent(textwindow->delete_button)), LIVES_BUTTONBOX_SPREAD);
9382 
9383     lives_container_set_border_width(LIVES_CONTAINER(textwindow->delete_button), widget_opts.border_width);
9384 
9385     lives_signal_sync_connect(LIVES_GUI_OBJECT(textwindow->delete_button), LIVES_WIDGET_CLICKED_SIGNAL,
9386                               LIVES_GUI_CALLBACK(on_lerrors_delete_clicked), NULL);
9387 
9388     lives_widget_show_all(textwindow->dialog);
9389   }
9390 
9391 
9392   void on_rename_activate(LiVESMenuItem * menuitem, livespointer user_data) {
9393     renamew = create_rename_dialog(1);
9394     lives_widget_show_all(renamew->dialog);
9395   }
9396 
9397 
9398   void autolives_toggle(LiVESMenuItem * menuitem, livespointer user_data) {
9399     // TODO: allow mapping of change types to random ranges in the backend
9400     // TODO: allow user selection of all ports
9401 #ifdef ENABLE_OSC
9402     autolives_window *alwindow = NULL;
9403     int trigtime;
9404     char *apb;
9405     char *mute;
9406     char *trigopt;
9407     char *debug;
9408     char string[PATH_MAX];
9409     char *com = NULL;
9410     boolean cancelled = FALSE;
9411 
9412     if (mainw->alives_pgid > 0) {
9413       // already running, kill the old process
9414       lives_killpg(mainw->alives_pgid, LIVES_SIGHUP);
9415       mainw->alives_pgid = 0;
9416 
9417       // restore pre-playback rte state
9418       rte_keymodes_restore(prefs->rte_keys_virtual);
9419       goto autolives_fail;
9420     }
9421 
9422     if (!lives_check_menu_item_get_active(LIVES_CHECK_MENU_ITEM(mainw->autolives))) return;
9423 
9424     if (!CURRENT_CLIP_IS_VALID) {
9425       do_autolives_needs_clips_error();
9426       goto autolives_fail;
9427     }
9428 
9429     if (cfile->event_list || cfile->opening || mainw->multitrack || mainw->is_processing || mainw->preview) {
9430       // ignore if doing something more important
9431       goto autolives_fail;
9432     }
9433 
9434     // search for autolives.pl
9435     if (!capable->has_autolives) {
9436       get_location(EXEC_AUTOLIVES_PL, string, PATH_MAX);
9437       if (strlen(string)) capable->has_autolives = TRUE;
9438       else {
9439         do_no_autolives_error();
9440         goto autolives_fail;
9441       }
9442     }
9443 
9444     alwindow = autolives_pre_dialog();
9445     if (!alwindow) {
9446       goto autolives_fail;
9447     }
9448     if (lives_dialog_run(LIVES_DIALOG(alwindow->dialog)) == LIVES_RESPONSE_CANCEL) {
9449       // user cancelled
9450       cancelled = TRUE;
9451       goto autolives_fail;
9452     }
9453 
9454     // check if osc is started; if not ask permission
9455     if (!prefs->osc_udp_started) {
9456       char typep[32];
9457       char *inst = (char *)typep;
9458       lives_snprintf(typep, 32, "%d", LIVES_PERM_OSC_PORTS);
9459       if (!lives_ask_permission((char **)&inst, 1, 0)) {
9460         // permission not given
9461         goto autolives_fail;
9462       }
9463 
9464       // try: start up osc
9465       prefs->osc_udp_started = lives_osc_init(prefs->osc_udp_port);
9466       if (!prefs->osc_udp_started) {
9467         goto autolives_fail;
9468       }
9469     }
9470 
9471     // build the command to run
9472     trigtime = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(alwindow->atrigger_spin));
9473     if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(alwindow->apb_button))) apb = lives_strdup(" -waitforplay");
9474     else apb = lives_strdup("");
9475     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(alwindow->mute_button))) mute = lives_strdup(" -mute");
9476     else mute = lives_strdup("");
9477     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(alwindow->atrigger_button))) {
9478       // timed
9479       trigopt = lives_strdup_printf(" -time %d", trigtime);
9480     } else {
9481       // omc
9482       trigopt = lives_strdup_printf(" -omc %d", prefs->osc_udp_port - 2);
9483     }
9484     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(alwindow->debug_button))) {
9485       debug = lives_strdup(" -debug");
9486     } else {
9487       debug = lives_strdup("");
9488     }
9489     lives_widget_destroy(alwindow->dialog);
9490     lives_free(alwindow);
9491 
9492     // store the current key/mode state
9493     rte_keymodes_backup(prefs->rte_keys_virtual);
9494 
9495     com = lives_strdup_printf("/usr/bin/%s localhost %d %d%s%s%s%s", EXEC_AUTOLIVES_PL, prefs->osc_udp_port,
9496                               prefs->osc_udp_port - 1, apb,
9497                               trigopt, mute,
9498                               debug);
9499 
9500     mainw->alives_pgid = lives_fork(com);
9501 
9502     lives_free(debug);
9503     lives_free(trigopt);
9504     lives_free(apb);
9505     lives_free(mute);
9506     if (com) lives_free(com);
9507     return;
9508 
9509 autolives_fail:
9510     if (alwindow) {
9511       if (!cancelled) lives_widget_destroy(alwindow->dialog);
9512       lives_free(alwindow);
9513     }
9514 
9515     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->autolives), FALSE);
9516 
9517 #endif
9518   }
9519 
9520 
9521   void on_rename_clip_name(LiVESButton * button, livespointer user_data) {
9522     char title[256];
9523     boolean bad_header = FALSE;
9524 
9525     if (!user_data) {
9526       lives_snprintf(title, 256, "%s", lives_entry_get_text(LIVES_ENTRY(renamew->entry)));
9527       lives_widget_destroy(renamew->dialog);
9528       lives_free(renamew);
9529     } else lives_snprintf(title, 256, "%s", (char *)user_data);
9530 
9531     if (!(strlen(title))) return;
9532 
9533     if (!user_data) {
9534       set_main_title(title, 0);
9535     }
9536 
9537     if (CURRENT_CLIP_IS_VALID) {
9538       lives_menu_item_set_text(cfile->menuentry, title, FALSE);
9539 
9540       lives_snprintf(cfile->name, 256, "%s", title);
9541 
9542       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_CLIPNAME, cfile->name)) bad_header = TRUE;
9543 
9544       if (bad_header) do_header_write_error(mainw->current_file);
9545       cfile->was_renamed = TRUE;
9546     }
9547   }
9548 
9549 
9550   void on_toy_activate(LiVESMenuItem * menuitem, livespointer user_data) {
9551     if (menuitem && mainw->toy_type == LIVES_POINTER_TO_INT(user_data)) {
9552       // switch is off
9553       user_data = LIVES_INT_TO_POINTER(LIVES_TOY_NONE);
9554     }
9555 
9556     switch (mainw->toy_type) {
9557     // old status
9558     case LIVES_TOY_MAD_FRAMES:
9559       lives_signal_handler_block(mainw->toy_random_frames, mainw->toy_func_random_frames);
9560       lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->toy_random_frames), FALSE);
9561       lives_signal_handler_unblock(mainw->toy_random_frames, mainw->toy_func_random_frames);
9562       if (LIVES_IS_PLAYING) {
9563         if (mainw->faded) {
9564           lives_widget_hide(mainw->start_image);
9565           lives_widget_hide(mainw->end_image);
9566         }
9567         if (CURRENT_CLIP_IS_VALID) {
9568           showclipimgs();
9569         }
9570       }
9571       break;
9572     case LIVES_TOY_TV:
9573       lives_signal_handler_block(mainw->toy_tv, mainw->toy_func_lives_tv);
9574       lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->toy_tv), FALSE);
9575       lives_signal_handler_unblock(mainw->toy_tv, mainw->toy_func_lives_tv);
9576       break;
9577     default:
9578       lives_signal_handler_block(mainw->toy_none, mainw->toy_func_none);
9579       lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->toy_none), FALSE);
9580       lives_signal_handler_unblock(mainw->toy_none, mainw->toy_func_none);
9581       break;
9582     }
9583 
9584     mainw->toy_type = (lives_toy_t)LIVES_POINTER_TO_INT(user_data);
9585 
9586     switch (mainw->toy_type) {
9587     case LIVES_TOY_NONE:
9588       lives_signal_handler_block(mainw->toy_none, mainw->toy_func_none);
9589       lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->toy_none), TRUE);
9590       lives_signal_handler_unblock(mainw->toy_none, mainw->toy_func_none);
9591       return;
9592     case LIVES_TOY_MAD_FRAMES:
9593       break;
9594     case LIVES_TOY_TV:
9595       // load in the lives TV clip
9596       deduce_file(LIVES_TV_CHANNEL1, 0., 0);
9597 
9598       // if we choose to discard it, discard it....otherwise keep it
9599       if (prefs->discard_tv) {
9600         close_current_file(0);
9601       } else {
9602         // keep it
9603         int current_file = mainw->current_file;
9604         char *com = lives_strdup_printf("%s commit_audio \"%s\"", prefs->backend, cfile->handle);
9605         cfile->start = 1;
9606         cfile->frames = get_frame_count(mainw->current_file, 1);
9607         cfile->end = cfile->frames;
9608         cfile->opening = cfile->opening_loc = cfile->opening_audio = cfile->opening_only_audio = FALSE;
9609         cfile->is_loaded = TRUE;
9610         lives_system(com, FALSE);
9611         save_clip_values(current_file);
9612         if (prefs->crash_recovery) add_to_recovery_file(cfile->handle);
9613         if (!mainw->multitrack) {
9614           switch_to_file((mainw->current_file = 0), current_file);
9615           sensitize();
9616         }
9617       }
9618       break;
9619     default:
9620       if (mainw->faded && !mainw->foreign) {
9621         lives_widget_show(mainw->start_image);
9622         lives_widget_show(mainw->end_image);
9623       }
9624     }
9625   }
9626 
9627 
9628   void on_preview_spinbutton_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
9629     // update the play window preview
9630     int preview_frame;
9631     static volatile boolean updated = FALSE;
9632 
9633     if (updated) return;
9634 
9635     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) {
9636       return;
9637     }
9638     if ((preview_frame = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton))) == mainw->preview_frame) {
9639       return;
9640     }
9641     // prevent multiple updates from interfering
9642     updated = TRUE;
9643     lives_signal_handler_block(mainw->preview_spinbutton, mainw->preview_spin_func);
9644     //lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
9645     mainw->preview_frame = preview_frame;
9646     load_preview_image(TRUE);
9647     lives_signal_handler_unblock(mainw->preview_spinbutton, mainw->preview_spin_func);
9648     updated = FALSE;
9649   }
9650 
9651 
9652   void on_prv_link_toggled(LiVESToggleButton * togglebutton, livespointer user_data) {
9653     if (!lives_toggle_button_get_active(togglebutton)) return;
9654     mainw->prv_link = LIVES_POINTER_TO_INT(user_data);
9655     if (mainw->is_processing && (mainw->prv_link == PRV_START || mainw->prv_link == PRV_END)) {
9656       // block spinbutton in play window
9657       lives_widget_set_sensitive(mainw->preview_spinbutton, FALSE);
9658     } else {
9659       lives_widget_set_sensitive(mainw->preview_spinbutton, TRUE);
9660     }
9661     load_preview_image(FALSE);
9662     lives_widget_grab_focus(mainw->preview_spinbutton);
9663   }
9664 
9665 
9666   void update_sel_menu(void) {
9667     if (mainw->multitrack || mainw->selwidth_locked) return;
9668 
9669     if (!CURRENT_CLIP_HAS_VIDEO || LIVES_IS_PLAYING || CURRENT_CLIP_IS_CLIPBOARD || mainw->is_processing) {
9670       lives_widget_set_sensitive(mainw->select_invert, FALSE);
9671       lives_widget_set_sensitive(mainw->select_all, FALSE);
9672       lives_widget_set_sensitive(mainw->sa_button, FALSE);
9673       lives_widget_set_sensitive(mainw->select_start_only, FALSE);
9674       lives_widget_set_sensitive(mainw->select_end_only, FALSE);
9675       lives_widget_set_sensitive(mainw->select_from_start, FALSE);
9676       lives_widget_set_sensitive(mainw->select_to_end, FALSE);
9677       lives_widget_set_sensitive(mainw->select_to_aend, FALSE);
9678       return;
9679     }
9680 
9681     lives_widget_set_sensitive(mainw->select_new, cfile->insert_start > 0);
9682     lives_widget_set_sensitive(mainw->select_last, cfile->undo_start > 0);
9683 
9684     if (cfile->end > cfile->start) {
9685       lives_widget_set_sensitive(mainw->select_start_only, TRUE);
9686       lives_widget_set_sensitive(mainw->select_end_only, TRUE);
9687     } else {
9688       lives_widget_set_sensitive(mainw->select_start_only, FALSE);
9689       lives_widget_set_sensitive(mainw->select_end_only, FALSE);
9690     }
9691     if (cfile->start == 1 && cfile->end == cfile->frames) {
9692       lives_widget_set_sensitive(mainw->select_invert, FALSE);
9693       lives_widget_set_sensitive(mainw->select_all, FALSE);
9694       lives_widget_set_sensitive(mainw->sa_button, FALSE);
9695     } else {
9696       if (cfile->start == 1 || cfile->end == cfile->frames)
9697         lives_widget_set_sensitive(mainw->select_invert, TRUE);
9698       else
9699         lives_widget_set_sensitive(mainw->select_invert, FALSE);
9700 
9701       lives_widget_set_sensitive(mainw->select_all, TRUE);
9702       lives_widget_set_sensitive(mainw->sa_button, TRUE);
9703     }
9704 
9705     if (cfile->start == 1) lives_widget_set_sensitive(mainw->select_from_start, FALSE);
9706     else lives_widget_set_sensitive(mainw->select_from_start, TRUE);
9707 
9708     if (cfile->end < cfile->frames) {
9709       lives_widget_set_sensitive(mainw->select_to_end, TRUE);
9710     } else {
9711       lives_widget_set_sensitive(mainw->select_to_end, FALSE);
9712       lives_widget_set_sensitive(mainw->select_to_aend, FALSE);
9713     }
9714     if (cfile->achans > 0) {
9715       int audframe = calc_frame_from_time4(mainw->current_file, cfile->laudio_time);
9716       if (audframe <= cfile->frames && audframe >= cfile->start && audframe != cfile->end)
9717         lives_widget_set_sensitive(mainw->select_to_aend, TRUE);
9718       else lives_widget_set_sensitive(mainw->select_to_aend, FALSE);
9719     } else lives_widget_set_sensitive(mainw->select_to_aend, FALSE);
9720   }
9721 
9722 
9723   void on_spinbutton_start_value_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
9724     int start, ostart;
9725     boolean rdrw_bars = TRUE;
9726     static volatile boolean updated = FALSE;
9727 
9728     if (updated) return;
9729 
9730     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) {
9731       return;
9732     }
9733     if ((start = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton))) == cfile->start) {
9734       return;
9735     }
9736 
9737     // prevent multiple updates from interfering
9738     updated = TRUE;
9739     lives_signal_handler_block(mainw->spinbutton_start, mainw->spin_start_func);
9740     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
9741 
9742     ostart = cfile->start;
9743     cfile->start = start;
9744 
9745     if (mainw->selwidth_locked) {
9746       /// must not update cfile->end directly, otherwise the selection colour won'r be updates
9747       int new_end = cfile->end + cfile->start - ostart;
9748       mainw->selwidth_locked = FALSE;
9749       if (new_end > cfile->frames) {
9750         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->start - cfile->end + cfile->frames);
9751         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->frames);
9752         lives_spin_button_update(LIVES_SPIN_BUTTON(mainw->spinbutton_end));
9753       } else {
9754         if (cfile->start < ostart && cfile->fps > 0.) {
9755           redraw_timer_bars((double)(cfile->start - 1.) / cfile->fps, (double)ostart / cfile->fps, 0);
9756           rdrw_bars = FALSE;
9757         }
9758         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), new_end);
9759         lives_spin_button_update(LIVES_SPIN_BUTTON(mainw->spinbutton_end));
9760       }
9761       mainw->selwidth_locked = TRUE;
9762     }
9763     update_sel_menu();
9764 
9765     if (!LIVES_IS_PLAYING && mainw->play_window && cfile->is_loaded) {
9766       /// load this first in case of caching - it is likely to be larger and higher quality
9767       if (mainw->prv_link == PRV_START && mainw->preview_frame != cfile->start)
9768         load_preview_image(FALSE);
9769     }
9770 
9771     if (!LIVES_IS_PLAYING) load_start_image(cfile->start);
9772 
9773     if (cfile->start > cfile->end) {
9774       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->start);
9775     }
9776     set_sel_label(mainw->sel_label);
9777     if (rdrw_bars && cfile->fps > 0.) {
9778       if (cfile->start < ostart)
9779         redraw_timer_bars((double)(cfile->start - 1.) / cfile->fps, (double)ostart / cfile->fps, 0);
9780       else
9781         redraw_timer_bars((double)(ostart - 1.) / cfile->fps, (double)cfile->start / cfile->fps, 0);
9782     }
9783 
9784     lives_signal_handler_unblock(mainw->spinbutton_start, mainw->spin_start_func);
9785     updated = FALSE;
9786   }
9787 
9788 
9789   void on_spinbutton_end_value_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
9790     int end, oend;
9791     boolean rdrw_bars = TRUE;
9792     static volatile boolean updated = FALSE;
9793 
9794     if (updated) return;
9795 
9796     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) {
9797       return;
9798     }
9799     if ((end = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton))) == cfile->end) {
9800       return;
9801     }
9802 
9803     // prevent multiple updates from interfering
9804     updated = TRUE;
9805 
9806     lives_signal_handler_block(mainw->spinbutton_end, mainw->spin_end_func);
9807     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
9808 
9809     oend = cfile->end;
9810     cfile->end = end;
9811 
9812     if (mainw->selwidth_locked) {
9813       int new_start = cfile->start + cfile->end - oend;
9814       mainw->selwidth_locked = FALSE;
9815       if (new_start < 1) {
9816         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->end - cfile->start + 1);
9817         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), 1);
9818         lives_spin_button_update(LIVES_SPIN_BUTTON(mainw->spinbutton_start));
9819       } else {
9820         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), new_start);
9821         if (cfile->end > oend && cfile->fps > 0.) {
9822           redraw_timer_bars((double)oend / cfile->fps, (double)(cfile->end + 1) / cfile->fps, 0);
9823           rdrw_bars = FALSE;
9824         }
9825         lives_spin_button_update(LIVES_SPIN_BUTTON(mainw->spinbutton_start));
9826       }
9827       mainw->selwidth_locked = TRUE;
9828     }
9829     update_sel_menu();
9830 
9831     if (!LIVES_IS_PLAYING && mainw->play_window && cfile->is_loaded) {
9832       /// load this first in case of caching - it is likely to be larger and higher quality
9833       if (mainw->prv_link == PRV_END && mainw->preview_frame != cfile->end)
9834         load_preview_image(FALSE);
9835     }
9836 
9837     if (!LIVES_IS_PLAYING) load_end_image(cfile->end);
9838 
9839     if (cfile->end < cfile->start) {
9840       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->end);
9841     }
9842 
9843     set_sel_label(mainw->sel_label);
9844     if (rdrw_bars && cfile->fps > 0.) {
9845       if (cfile->end > oend)
9846         redraw_timer_bars((double)oend / cfile->fps, (double)(cfile->end + 1) / cfile->fps, 0);
9847       else
9848         redraw_timer_bars((double)cfile->end / cfile->fps, (double)(oend + 1) / cfile->fps, 0);
9849     }
9850 
9851     lives_signal_handler_unblock(mainw->spinbutton_end, mainw->spin_end_func);
9852     updated = FALSE;
9853   }
9854 
9855 
9856   boolean all_expose(LiVESWidget * widget, lives_painter_t *cr, livespointer psurf) {
9857     lives_painter_surface_t **surf = (lives_painter_surface_t **)psurf;
9858     if (surf) {
9859       if (*surf) {
9860         lives_painter_set_source_surface(cr, *surf, 0., 0.);
9861         lives_painter_paint(cr);
9862       } else {
9863         return TRUE;
9864       }
9865     }
9866     return TRUE;
9867   }
9868 
9869 
9870   boolean all_expose_overlay(LiVESWidget * widget, lives_painter_t *creb, livespointer psurf) {
9871     /// quick and dirty copy / paste
9872     if (mainw->go_away) return FALSE;
9873     if (LIVES_IS_PLAYING && mainw->faded) return FALSE;
9874     if (!CURRENT_CLIP_IS_VALID) return FALSE;
9875     else {
9876       int bar_height;
9877       int allocy;
9878       double allocwidth = (double)lives_widget_get_allocation_width(mainw->video_draw), allocheight;
9879       double offset;
9880       double ptrtime = mainw->ptrtime;
9881       frames_t frame;
9882       int which = 0;
9883 
9884       offset = ptrtime / CURRENT_CLIP_TOTAL_TIME * allocwidth;
9885 
9886       lives_painter_set_line_width(creb, 1.);
9887 
9888       if (palette->style & STYLE_LIGHT) {
9889         lives_painter_set_source_rgb_from_lives_widget_color(creb, &palette->black);
9890       } else {
9891         lives_painter_set_source_rgb_from_lives_widget_color(creb, &palette->white);
9892       }
9893 
9894       if (!(frame = calc_frame_from_time(mainw->current_file, ptrtime)))
9895         frame = cfile->frames;
9896 
9897       if (cfile->frames > 0 && (which == 0 || which == 1)) {
9898         if (mainw->video_drawable) {
9899           bar_height = CE_VIDBAR_HEIGHT;
9900 
9901           allocheight = (double)lives_widget_get_allocation_height(mainw->vidbar) + bar_height + widget_opts.packing_height * 2.5;
9902           allocy = lives_widget_get_allocation_y(mainw->vidbar) - widget_opts.packing_height;
9903           lives_painter_move_to(creb, offset, allocy);
9904           lives_painter_line_to(creb, offset, allocy + allocheight);
9905           lives_painter_stroke(creb);
9906         }
9907       }
9908 
9909       if (LIVES_IS_PLAYING) {
9910         if (which == 0) lives_ruler_set_value(LIVES_RULER(mainw->hruler), ptrtime);
9911         if (cfile->achans > 0 && cfile->is_loaded && prefs->audio_src != AUDIO_SRC_EXT) {
9912           if (is_realtime_aplayer(prefs->audio_player) && (!mainw->event_list || !mainw->preview)) {
9913 #ifdef ENABLE_JACK
9914             if (mainw->jackd && prefs->audio_player == AUD_PLAYER_JACK) {
9915               offset = allocwidth * ((double)mainw->jackd->seek_pos / cfile->arate / cfile->achans /
9916                                      cfile->asampsize * 8) / CURRENT_CLIP_TOTAL_TIME;
9917             }
9918 #endif
9919 #ifdef HAVE_PULSE_AUDIO
9920             if (mainw->pulsed && prefs->audio_player == AUD_PLAYER_PULSE) {
9921               offset = allocwidth * ((double)mainw->pulsed->seek_pos / cfile->arate / cfile->achans /
9922                                      cfile->asampsize * 8) / CURRENT_CLIP_TOTAL_TIME;
9923             }
9924 #endif
9925           } else offset = allocwidth * (mainw->aframeno - .5) / cfile->fps / CURRENT_CLIP_TOTAL_TIME;
9926         }
9927       } else {
9928         offset = cfile->real_pointer_time / CURRENT_CLIP_TOTAL_TIME * allocwidth;
9929       }
9930 
9931       if (cfile->achans > 0) {
9932         bar_height = CE_AUDBAR_HEIGHT;
9933         if (mainw->laudio_drawable && (which == 0 || which == 2)) {
9934           allocheight = (double)lives_widget_get_allocation_height(mainw->laudbar) + bar_height
9935                         + widget_opts.packing_height * 2.5;
9936           allocy = lives_widget_get_allocation_y(mainw->laudbar) - widget_opts.packing_height;
9937           lives_painter_move_to(creb, offset, allocy);
9938           lives_painter_line_to(creb, offset, allocy + allocheight);
9939         }
9940 
9941         if (cfile->achans > 1 && (which == 0 || which == 3)) {
9942           if (mainw->raudio_drawable) {
9943             allocheight = (double)lives_widget_get_allocation_height(mainw->raudbar)
9944                           + bar_height + widget_opts.packing_height * 2.5;
9945             allocy = lives_widget_get_allocation_y(mainw->raudbar) - widget_opts.packing_height;
9946             lives_painter_move_to(creb, offset, allocy);
9947             lives_painter_line_to(creb, offset, allocy + allocheight);
9948           }
9949         }
9950       }
9951 
9952       lives_painter_stroke(creb);
9953       return TRUE;
9954     }
9955   }
9956 
9957 
9958   boolean all_expose_pb(LiVESWidget * widget, lives_painter_t *cr, livespointer psurf) {
9959     if (LIVES_IS_PLAYING) all_expose(widget, cr, psurf);
9960     return TRUE;
9961   }
9962 
9963   boolean all_expose_nopb(LiVESWidget * widget, lives_painter_t *cr, livespointer psurf) {
9964     if (!LIVES_IS_PLAYING) all_expose(widget, cr, psurf);
9965     return TRUE;
9966   }
9967 
9968   boolean expose_vid_draw(LiVESWidget * widget, lives_painter_t *cr, livespointer psurf) {
9969     if (mainw->video_drawable) {
9970       lives_painter_set_source_surface(cr, mainw->video_drawable, 0., 0.);
9971       lives_painter_paint(cr);
9972     }
9973     return TRUE;
9974   }
9975 
9976   boolean config_vid_draw(LiVESWidget * widget, LiVESXEventConfigure * event, livespointer user_data) {
9977     if (mainw->video_drawable) lives_painter_surface_destroy(mainw->video_drawable);
9978     mainw->video_drawable = lives_widget_create_painter_surface(widget);
9979     clear_widget_bg(widget, mainw->video_drawable);
9980     update_timer_bars(0, 0, 0, 0, 1);
9981     return TRUE;
9982   }
9983 
9984   boolean expose_laud_draw(LiVESWidget * widget, lives_painter_t *cr, livespointer psurf) {
9985     if (mainw->laudio_drawable) {
9986       lives_painter_set_source_surface(cr, mainw->laudio_drawable, 0., 0.);
9987       lives_painter_paint(cr);
9988     }
9989     return TRUE;
9990   }
9991 
9992   boolean config_laud_draw(LiVESWidget * widget, LiVESXEventConfigure * event, livespointer user_data) {
9993     if (IS_VALID_CLIP(mainw->drawsrc)) {
9994       lives_painter_surface_t *surf = lives_widget_create_painter_surface(widget);
9995       lives_painter_surface_t *laudio_drawable;
9996 
9997       clear_widget_bg(widget, surf);
9998       laudio_drawable = mainw->laudio_drawable;
9999       mainw->laudio_drawable = surf;
10000 
10001       if (laudio_drawable) {
10002         lives_painter_surface_destroy(laudio_drawable);
10003       }
10004       mainw->files[mainw->drawsrc]->laudio_drawable = mainw->laudio_drawable;
10005     }
10006     return TRUE;
10007   }
10008 
10009 
10010   boolean expose_raud_draw(LiVESWidget * widget, lives_painter_t *cr, livespointer psurf) {
10011     if (mainw->raudio_drawable) {
10012       lives_painter_set_source_surface(cr, mainw->raudio_drawable, 0., 0.);
10013       lives_painter_paint(cr);
10014     }
10015     return TRUE;
10016   }
10017 
10018   boolean config_raud_draw(LiVESWidget * widget, LiVESXEventConfigure * event, livespointer user_data) {
10019     if (IS_VALID_CLIP(mainw->drawsrc)) {
10020       lives_painter_surface_t *surf = lives_widget_create_painter_surface(widget);
10021       lives_painter_surface_t *raudio_drawable;
10022 
10023       clear_widget_bg(widget, surf);
10024       raudio_drawable = mainw->raudio_drawable;
10025       mainw->raudio_drawable = surf;
10026 
10027       if (raudio_drawable) {
10028         lives_painter_surface_destroy(raudio_drawable);
10029       }
10030       mainw->files[mainw->drawsrc]->raudio_drawable = mainw->raudio_drawable;
10031     }
10032     return TRUE;
10033   }
10034 
10035   boolean config_event2(LiVESWidget * widget, LiVESXEventConfigure * event, livespointer user_data) {
10036     mainw->msg_area_configed = TRUE;
10037     return TRUE;
10038   }
10039 
10040 
10041   /// genric func. to create surfaces
10042   boolean all_config(LiVESWidget * widget, LiVESXEventConfigure * event, livespointer ppsurf) {
10043     lives_painter_surface_t **psurf = (lives_painter_surface_t **)ppsurf;
10044     if (mainw->no_configs) return TRUE;
10045 
10046     if (!psurf) return FALSE;
10047     if (*psurf) lives_painter_surface_destroy(*psurf);
10048     *psurf = lives_widget_create_painter_surface(widget);
10049 
10050 #ifdef USE_SPECIAL_BUTTONS
10051     if (LIVES_IS_DRAWING_AREA(widget)) {
10052       LiVESWidget *parent = lives_widget_get_parent(widget);
10053       if (parent && LIVES_IS_BUTTON(parent) && is_standard_widget(parent)) {
10054         sbutt_render(parent, 0, NULL);
10055         return FALSE;
10056       }
10057     }
10058 #endif
10059 
10060     clear_widget_bg(widget, *psurf);
10061 
10062     if (widget == mainw->start_image)
10063       load_start_image(CURRENT_CLIP_IS_VALID ? cfile->start : 0);
10064     else if (widget == mainw->end_image)
10065       load_end_image(CURRENT_CLIP_IS_VALID ? cfile->end : 0);
10066     else if (widget == mainw->preview_image)
10067       load_preview_image(FALSE);
10068     else if (widget == mainw->msg_area && !mainw->multitrack)
10069       msg_area_config(widget);
10070     else if (widget == mainw->dsu_widget)
10071       draw_dsu_widget(widget);
10072     else if (mainw->multitrack) {
10073       if (widget == mainw->multitrack->timeline_reg)
10074         draw_region(mainw->multitrack);
10075       else if (widget == mainw->multitrack->in_image || widget == mainw->multitrack->out_image) {
10076         show_in_out_images(mainw->multitrack);
10077       } else if (widget == mainw->play_image) {
10078         lives_idle_add_simple(mt_idle_show_current_frame, (livespointer)mainw->multitrack);
10079         //lives_widget_queue_draw(mainw->multitrack->preview_frame);
10080         //set_mt_play_sizes_cfg(mainw->multitrack);
10081         //mt_show_current_frame(mainw->multitrack, FALSE);
10082       } else if (widget == mainw->multitrack->msg_area) {
10083         msg_area_config(widget);
10084       }
10085     }
10086     return FALSE;
10087   }
10088 
10089 
10090   boolean config_event(LiVESWidget * widget, LiVESXEventConfigure * event, livespointer user_data) {
10091     if (!mainw->configured) {
10092       mainw->configured = TRUE;
10093       return FALSE;
10094     }
10095     if (widget == LIVES_MAIN_WINDOW_WIDGET) {
10096       int scr_width, scr_height;
10097       scr_width = GUI_SCREEN_PHYS_WIDTH;
10098       scr_height = GUI_SCREEN_PHYS_HEIGHT;
10099       if (event->width != scr_width || event->height != scr_height) {
10100         get_monitors(FALSE);
10101         if (scr_width != GUI_SCREEN_PHYS_WIDTH || scr_height != GUI_SCREEN_PHYS_HEIGHT) {
10102           if (!mainw->ignore_screen_size) {
10103             if (prefs->show_dev_opts) {
10104               g_printerr("VALLS %d %d   %d %d   %d %d\n", event->width, event->height, scr_width, scr_height,
10105                          GUI_SCREEN_PHYS_WIDTH, GUI_SCREEN_PHYS_HEIGHT);
10106             }
10107             resize_widgets_for_monitor(FALSE);
10108             if (!CURRENT_CLIP_IS_VALID) {
10109               lives_ce_update_timeline(0, 0.);
10110 	    // *INDENT-OFF*
10111 	  }}}
10112 	else mainw->ignore_screen_size = FALSE;
10113       }}
10114   // *INDENT-ON*
10115 
10116     return FALSE;
10117   }
10118 
10119 
10120   // these two really belong with the processing widget
10121 
10122   void on_effects_paused(LiVESButton * button, livespointer user_data) {
10123     char *com = NULL;
10124     const char *stockim;
10125     ticks_t xticks;
10126 
10127     if (mainw->iochan || cfile->opening) {
10128       // pause during encoding (if we start using mainw->iochan for other things, this will
10129       // need changing...)
10130       if (!mainw->effects_paused) {
10131         lives_suspend_resume_process(cfile->handle, TRUE);
10132 
10133         if (!cfile->opening) {
10134           lives_button_set_label(LIVES_BUTTON(button), _("Resume"));
10135           lives_label_set_text(LIVES_LABEL(mainw->proc_ptr->label2), _("\nPaused\n(click Resume to continue processing)"));
10136           d_print(_("paused..."));
10137         }
10138       } else {
10139         lives_suspend_resume_process(cfile->handle, FALSE);
10140 
10141         if (!cfile->opening) {
10142           lives_button_set_label(LIVES_BUTTON(button), _("Paus_e"));
10143           lives_label_set_text(LIVES_LABEL(mainw->proc_ptr->label2), _("\nPlease Wait"));
10144           d_print(_("resumed..."));
10145         }
10146       }
10147     }
10148 
10149     if (!mainw->iochan) {
10150       // pause during effects processing or opening
10151       xticks = lives_get_relative_ticks(mainw->origsecs, mainw->orignsecs);
10152       if (!mainw->effects_paused) {
10153         mainw->timeout_ticks -= xticks;
10154         com = lives_strdup_printf("%s pause \"%s\"", prefs->backend_sync, cfile->handle);
10155         if (!mainw->preview) {
10156           lives_button_set_label(LIVES_BUTTON(button), _("Resume"));
10157           if (!cfile->nokeep) {
10158             char *tmp, *ltext;
10159 
10160             if (!cfile->opening) {
10161               ltext = (_("Keep"));
10162             } else {
10163               ltext = (_("Enough"));
10164             }
10165             stockim = LIVES_STOCK_KEEP;
10166             lives_button_set_image_from_stock(LIVES_BUTTON(mainw->proc_ptr->cancel_button), stockim);
10167             lives_button_set_label(LIVES_BUTTON(mainw->proc_ptr->cancel_button), ltext);
10168             lives_label_set_text(LIVES_LABEL(mainw->proc_ptr->label2),
10169                                  (tmp = lives_strdup_printf
10170                                         (_("\nPaused\n(click %s to keep what you have and stop)\n(click "
10171                                            "Resume to continue processing)"), ltext)));
10172             lives_free(tmp);
10173             lives_free(ltext);
10174           }
10175           if ((!mainw->multitrack && mainw->is_rendering && prefs->render_audio) || (mainw->multitrack &&
10176               mainw->multitrack->opts.render_audp)) {
10177             // render audio up to current tc
10178             mainw->flush_audio_tc = q_gint64((double)cfile->undo_end / cfile->fps * TICKS_PER_SECOND_DBL, cfile->fps);
10179             render_events_cb(FALSE);
10180             mainw->flush_audio_tc = 0;
10181             cfile->afilesize = reget_afilesize_inner(mainw->current_file);
10182             cfile->laudio_time = (double)(cfile->afilesize / (cfile->asampsize >> 3) / cfile->achans) / (double)cfile->arate;
10183             if (cfile->achans > 1) {
10184               cfile->raudio_time = cfile->laudio_time;
10185             }
10186           }
10187           d_print(_("paused..."));
10188         }
10189 #ifdef ENABLE_JACK
10190         if (mainw->jackd && mainw->jackd_read && mainw->jackd_read->in_use)
10191           if (mainw->proc_ptr->stop_button)
10192             lives_widget_hide(mainw->proc_ptr->stop_button);
10193 #endif
10194 #ifdef HAVE_PULSE_AUDIO
10195         if (mainw->pulsed && mainw->pulsed_read && mainw->pulsed_read->in_use)
10196           if (mainw->proc_ptr->stop_button)
10197             lives_widget_hide(mainw->proc_ptr->stop_button);
10198 #endif
10199       } else {
10200         mainw->timeout_ticks += xticks;
10201         com = lives_strdup_printf("%s resume \"%s\"", prefs->backend_sync, cfile->handle);
10202         if (!mainw->preview) {
10203           if (cfile->opening || !cfile->nokeep) lives_button_set_label(LIVES_BUTTON(button), _("Pause/_Enough"));
10204           else lives_button_set_label(LIVES_BUTTON(button), _("Paus_e"));
10205           lives_button_set_label(LIVES_BUTTON(mainw->proc_ptr->cancel_button), _("Cancel"));
10206           stockim = LIVES_STOCK_CANCEL;
10207           lives_button_set_image_from_stock(LIVES_BUTTON(mainw->proc_ptr->cancel_button), stockim);
10208           lives_label_set_text(LIVES_LABEL(mainw->proc_ptr->label2), _("\nPlease Wait"));
10209           d_print(_("resumed..."));
10210         }
10211 #ifdef ENABLE_JACK
10212         if (mainw->jackd && mainw->jackd_read && mainw->jackd_read->in_use)
10213           if (mainw->proc_ptr->stop_button)
10214             lives_widget_show_all(mainw->proc_ptr->stop_button);
10215 #endif
10216 #ifdef HAVE_PULSE_AUDIO
10217         if (mainw->pulsed && mainw->pulsed_read && mainw->pulsed_read->in_use)
10218           if (mainw->proc_ptr->stop_button)
10219             lives_widget_show_all(mainw->proc_ptr->stop_button);
10220 #endif
10221       }
10222 
10223       if (!cfile->opening && !mainw->internal_messaging
10224           && !(
10225 #ifdef ENABLE_JACK
10226             (mainw->jackd && mainw->jackd_read && mainw->jackd_read->in_use)
10227 #else
10228             0
10229 #endif
10230             ||
10231 #ifdef HAVE_PULSE_AUDIO
10232             (mainw->pulsed && mainw->pulsed_read && mainw->pulsed->in_use)
10233 #else
10234             0
10235 #endif
10236           )) {
10237         lives_system(com, FALSE);
10238       }
10239     }
10240     lives_freep((void **)&com);
10241     mainw->effects_paused = !mainw->effects_paused;
10242   }
10243 
10244 
10245   void on_preview_clicked(LiVESButton * button, livespointer user_data) {
10246     // play an effect/tool preview
10247     // IMPORTANT: cfile->undo_start and cfile->undo_end determine which frames
10248     // should be played
10249 
10250     weed_plant_t *filter_map = mainw->filter_map; // back this up in case we are rendering
10251     weed_plant_t *afilter_map = mainw->afilter_map; // back this up in case we are rendering
10252     weed_plant_t *audio_event = mainw->audio_event;
10253 
10254     uint64_t old_rte; //TODO - block better
10255     ticks_t xticks;
10256 
10257     static volatile boolean in_preview_func = FALSE;
10258 
10259     boolean resume_after;
10260     boolean ointernal_messaging = mainw->internal_messaging;
10261 
10262     int ostart = cfile->start;
10263     int oend = cfile->end;
10264 
10265     int toy_type = mainw->toy_type;
10266 
10267     int current_file = mainw->current_file;
10268 
10269     if (in_preview_func) {
10270       // called a second time from playback loop
10271       // this is a special value of cancel - don't propogate it to "open"
10272       mainw->cancelled = CANCEL_NO_PROPOGATE;
10273       return;
10274     }
10275 
10276     if (mainw->noswitch) {
10277       mainw->preview_req = TRUE;
10278       return;
10279     }
10280 
10281     in_preview_func = TRUE;
10282 
10283     old_rte = mainw->rte;
10284     xticks = lives_get_current_ticks();
10285     mainw->timeout_ticks -= xticks;
10286 
10287     if (mainw->internal_messaging) {
10288       mainw->internal_messaging = FALSE;
10289       // for realtime fx previews, we will switch all effects off and restore old
10290       // value after
10291       mainw->rte = EFFECT_NONE;
10292     }
10293 
10294     if (!LIVES_IS_PLAYING) {
10295       if (cfile->opening) {
10296         if (!cfile->opening_only_audio) {
10297           mainw->toy_type = LIVES_TOY_NONE;
10298           lives_widget_set_sensitive(mainw->toys_menu, FALSE);
10299         }
10300         if (!mainw->multitrack && prefs->show_gui) lives_widget_show(LIVES_MAIN_WINDOW_WIDGET);
10301 
10302         if (!mainw->multitrack && !cfile->is_loaded) {
10303           if (mainw->play_window) {
10304             cfile->is_loaded = TRUE;
10305             resize_play_window();
10306             cfile->is_loaded = FALSE;
10307           }
10308         }
10309       }
10310 
10311       resume_after = FALSE;
10312 
10313       if (mainw->multitrack) {
10314         mt_prepare_for_playback(mainw->multitrack);
10315         if (cfile->opening) {
10316           lives_widget_set_sensitive(mainw->multitrack->playall, FALSE);
10317           lives_widget_set_sensitive(mainw->m_playbutton, FALSE);
10318         }
10319       }
10320 
10321       if (user_data) {
10322         // called from multitrack
10323         /* if (mainw->play_window) { */
10324         /*   resize_play_window(); */
10325         /* } */
10326         if (mainw->multitrack && mainw->multitrack->is_rendering) {
10327           mainw->play_start = 1;
10328           mainw->play_end = cfile->frames;
10329         } else {
10330           mainw->play_start = 1;
10331           mainw->play_end = INT_MAX;
10332         }
10333       } else {
10334         mainw->preview = TRUE;
10335         if (!mainw->is_processing && !mainw->is_rendering) {
10336           mainw->play_start = cfile->start = cfile->undo_start;
10337           mainw->play_end = cfile->end = cfile->undo_end;
10338         } else {
10339           if (mainw->is_processing && mainw->is_rendering && prefs->render_audio) {
10340             // render audio up to current tc
10341             mainw->flush_audio_tc = q_gint64((double)cfile->undo_end / cfile->fps * TICKS_PER_SECOND_DBL, cfile->fps);
10342             render_events_cb(FALSE);
10343             mainw->flush_audio_tc = 0;
10344             cfile->afilesize = reget_afilesize_inner(mainw->current_file);
10345             cfile->laudio_time = (double)(cfile->afilesize / (cfile->asampsize >> 3) / cfile->achans) / (double)cfile->arate;
10346             if (cfile->achans > 1) {
10347               cfile->raudio_time = cfile->laudio_time;
10348             }
10349           }
10350           mainw->play_start = calc_frame_from_time(mainw->current_file, event_list_get_start_secs(mainw->event_list));
10351           mainw->play_end = INT_MAX;
10352         }
10353       }
10354 
10355       if (cfile->opening) mainw->effects_paused = TRUE;
10356       else {
10357         // stop effects processing (if preferred)
10358         if (prefs->pause_effect_during_preview) {
10359           if (!(mainw->effects_paused)) {
10360             on_effects_paused(LIVES_BUTTON(mainw->proc_ptr->pause_button), NULL);
10361             resume_after = TRUE;
10362           }
10363         }
10364       }
10365 
10366       if (button) lives_button_set_label(LIVES_BUTTON(button), _("Stop"));
10367       if (mainw->proc_ptr) {
10368         lives_widget_set_sensitive(mainw->proc_ptr->pause_button, FALSE);
10369         lives_widget_set_sensitive(mainw->proc_ptr->cancel_button, FALSE);
10370         lives_widget_hide(mainw->proc_ptr->processing);
10371       }
10372       if (!cfile->opening) {
10373         lives_widget_set_sensitive(mainw->showfct, FALSE);
10374       }
10375 
10376       desensitize();
10377 
10378       if (cfile->opening || cfile->opening_only_audio) {
10379         lives_widget_hide(mainw->proc_ptr->processing);
10380         if (!mainw->multitrack && !cfile->opening_audio) {
10381           showclipimgs();
10382         }
10383         resize(1);
10384       }
10385 
10386       if (ointernal_messaging) {
10387         lives_sync(3);
10388       }
10389       current_file = mainw->current_file;
10390       resize(1);
10391 
10392       // play the clip
10393       on_playsel_activate(NULL, LIVES_INT_TO_POINTER(TRUE));
10394 
10395       if (current_file != mainw->current_file) {
10396         if (mainw->is_rendering) {
10397           mainw->files[current_file]->next_event = cfile->next_event;
10398           cfile->next_event = NULL;
10399           mainw->current_file = current_file;
10400         } else if (!mainw->multitrack) {
10401           switch_to_file((mainw->current_file = 0), current_file);
10402         }
10403       }
10404 
10405       if (mainw->is_rendering) {
10406         init_conversions(LIVES_INTENTION_RENDER);
10407         mainw->effort = -EFFORT_RANGE_MAX;
10408       }
10409 
10410       if (cfile->opening) mainw->effects_paused = FALSE;
10411       else {
10412         // restart effects processing (if necessary)
10413         if (resume_after) on_effects_paused(LIVES_BUTTON(mainw->proc_ptr->pause_button), NULL);
10414       }
10415       // user_data is non-NULL if called from multitrack. We want to preserve the value of cancelled.
10416       if (!user_data) mainw->cancelled = CANCEL_NONE;
10417 
10418       cfile->start = ostart;
10419       cfile->end = oend;
10420 
10421       mainw->toy_type = (lives_toy_t)toy_type;
10422       lives_widget_set_sensitive(mainw->toys_menu, TRUE);
10423 
10424       if (mainw->proc_ptr) {
10425         // proc_ptr can be NULL if we finished loading with a bg generator running
10426         lives_widget_show(mainw->proc_ptr->processing);
10427         if (button) lives_button_set_label(LIVES_BUTTON(button), _("Preview"));
10428         lives_widget_set_sensitive(mainw->proc_ptr->pause_button, TRUE);
10429         lives_widget_set_sensitive(mainw->proc_ptr->cancel_button, TRUE);
10430       }
10431       mainw->preview = FALSE;
10432       desensitize();
10433       procw_desensitize();
10434 
10435       if (!cfile->opening) {
10436         lives_widget_set_sensitive(mainw->showfct, TRUE);
10437       } else {
10438         /*      for (i=1;i<MAX_FILES;i++) {
10439           if (mainw->files[i]!=NULL) {
10440           if (mainw->files[i]->menuentry!=NULL) {
10441           lives_widget_set_sensitive (mainw->files[i]->menuentry, TRUE);
10442           }}}*/
10443         if (!mainw->multitrack && mainw->play_window) {
10444           resize_play_window();
10445         }
10446       }
10447     }
10448 
10449     if (mainw->preview_box) lives_widget_set_tooltip_text(mainw->p_playbutton, _("Preview"));
10450     lives_widget_set_tooltip_text(mainw->m_playbutton, _("Preview"));
10451 
10452     // redraw our bars for the clip
10453     if (!mainw->merge && !mainw->multitrack) {
10454       get_play_times();
10455     }
10456     if (ointernal_messaging) {
10457       mainw->internal_messaging = TRUE;
10458 
10459       // switch realtime fx back on
10460       mainw->rte = old_rte;
10461     }
10462 
10463     if (mainw->play_window && mainw->fs && !mainw->multitrack) {
10464       // this prevents a hang when the separate window is visible
10465       // it may be the first time we have shown it
10466       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
10467 
10468     }
10469     xticks = lives_get_current_ticks();
10470     mainw->timeout_ticks += xticks;
10471     mainw->filter_map = filter_map;
10472     mainw->afilter_map = afilter_map;
10473     mainw->audio_event = audio_event;
10474 
10475     if (mainw->multitrack) {
10476       current_file = mainw->current_file;
10477       mainw->current_file = mainw->multitrack->render_file;
10478       main_thread_execute((lives_funcptr_t)mt_post_playback, -1, NULL, "v", mainw->multitrack);
10479       mainw->current_file = current_file;
10480     }
10481 
10482     in_preview_func = FALSE;
10483   }
10484 
10485 
10486   void vj_mode_toggled(LiVESCheckMenuItem * menuitem, livespointer user_data) {
10487     if (lives_check_menu_item_get_active(menuitem) && (prefs->warning_mask & WARN_MASK_VJMODE_ENTER) == 0) {
10488       if (!(do_yesno_dialog_with_check(_("VJ Mode is specifically designed to make LiVES ready for realtime presentation.\n"
10489                                          "Enabling VJ restart will have the following effects:\n"
10490                                          "\n\n - On startup, audio source will be set to external. "
10491                                          "Clips willl reload without audio (although the audio files will remain on the disk).\n"
10492                                          "Additionally, when playing external audio, LiVES uses the system clock for frame timings "
10493                                          "(rather than the soundcard) which may allow for slightly smoother playback.\n"
10494                                          "\n - only the lightest of checks will be done when reloading clips (unless a problem is detected "
10495                                          "during the reload.)\n\n"
10496                                          "Startup  will be almost instantaneous, however in the rare occurence of corruption to "
10497                                          "a clip audio file, this will not be detected, as the file will not be loaded."
10498                                          "\nOn startup, LiVES will grab the keyboard and screen focus if it can,"
10499                                          "\n - Shutdown will be slightly more rapid as no cleanup of the working directory will be attempted"
10500                                          "\n - Rendered effects will not be loaded, which will further reduce the startup time. "
10501                                          "(Realtime effects will still be loaded as usual)\n"
10502                                          "\n - Any crash recovery files will be auto reloaded "
10503                                          "making it convenient to  terminate  LiVES using ctrl-c or simply shutting down the machine\n"
10504                                          "\n - Continuous looping of video will be enabled automatically on startup"),
10505                                        WARN_MASK_VJMODE_ENTER))) {
10506         lives_signal_handler_block(mainw->vj_mode, mainw->vj_mode_func);
10507         lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->vj_mode), FALSE);
10508         lives_signal_handler_unblock(mainw->vj_mode, mainw->vj_mode_func);
10509         return;
10510       }
10511     }
10512     pref_factory_bool(PREF_VJMODE, !future_prefs->vj_mode, TRUE);
10513   }
10514 
10515 
10516   /**
10517      This is a super important function : almost everything related to velocity direction
10518      changes during playback should ensurre this function is called.
10519      For example it user_data is non-NULL, will also set the audio player rate / direction.
10520      Even if the clip is frozen, we still update the freeze fps so that when it is unfrozen it starts with the correct velocity.
10521   */
10522   void changed_fps_during_pb(LiVESSpinButton * spinbutton, livespointer user_data) {
10523     /// user_data non-NULL to force audio rate update
10524     double new_fps;
10525 
10526     if (!LIVES_IS_PLAYING) return;
10527     if (!CURRENT_CLIP_IS_VALID) return;
10528 
10529     new_fps = lives_fix(lives_spin_button_get_value(LIVES_SPIN_BUTTON(spinbutton)), 3);
10530 
10531     if (!user_data && ((!cfile->play_paused && cfile->pb_fps == new_fps) || (cfile->play_paused && new_fps == 0.))) {
10532       mainw->period = TICKS_PER_SECOND_DBL / cfile->pb_fps;
10533       return;
10534     }
10535 
10536     mainw->currticks = lives_get_current_playback_ticks(mainw->origsecs, mainw->orignsecs, NULL);
10537 
10538     if (new_fps != cfile->pb_fps && fabs(new_fps) > .001 && !cfile->play_paused) {
10539       /// we must scale the frame delta, since e.g if we were a halfway through the frame and the fps increased,
10540       /// we could end up jumping several frames
10541       ticks_t delta_ticks;
10542       delta_ticks = (mainw->currticks - mainw->startticks);
10543       delta_ticks = (ticks_t)((double)delta_ticks + fabs(cfile->pb_fps / new_fps));
10544       /// the time we would shown the last frame at using the new fps
10545       mainw->startticks = mainw->currticks - delta_ticks;
10546     }
10547 
10548     cfile->pb_fps = new_fps;
10549     mainw->period = TICKS_PER_SECOND_DBL / cfile->pb_fps;
10550 
10551     if (prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS) {
10552       if (new_fps >= 0.) cfile->adirection = LIVES_DIRECTION_FORWARD;
10553       else cfile->adirection = LIVES_DIRECTION_REVERSE;
10554       // update our audio player
10555 #ifdef ENABLE_JACK
10556       if (prefs->audio_src == AUDIO_SRC_INT && prefs->audio_player == AUD_PLAYER_JACK && mainw->jackd) {
10557         if (mainw->jackd->playing_file == mainw->current_file) {
10558           mainw->jackd->sample_in_rate = cfile->arate * cfile->pb_fps / cfile->fps;
10559           if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO)
10560               && mainw->agen_key == 0 && !mainw->agen_needs_reinit) {
10561             jack_get_rec_avals(mainw->jackd);
10562 	  // *INDENT-OFF*
10563         }}}
10564     // *INDENT-ON*
10565 #endif
10566 
10567 #ifdef HAVE_PULSE_AUDIO
10568       if (prefs->audio_src == AUDIO_SRC_INT && prefs->audio_player == AUD_PLAYER_PULSE && mainw->pulsed) {
10569         if (mainw->pulsed->playing_file == mainw->current_file) {
10570           mainw->pulsed->in_arate = cfile->arate * cfile->pb_fps / cfile->fps;
10571           if (mainw->pulsed->fd >= 0) {
10572             if (mainw->pulsed->in_arate > 0.) {
10573               lives_buffered_rdonly_set_reversed(mainw->pulsed->fd, FALSE);
10574             } else {
10575               lives_buffered_rdonly_set_reversed(mainw->pulsed->fd, TRUE);
10576             }
10577           }
10578           if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO)
10579               && mainw->agen_key == 0 && !mainw->agen_needs_reinit) {
10580             pulse_get_rec_avals(mainw->pulsed);
10581 	  // *INDENT-OFF*
10582         }}}
10583     // *INDENT-ON*
10584 #endif
10585     }
10586 
10587     if (cfile->play_paused && new_fps != 0.) {
10588       cfile->freeze_fps = new_fps;
10589       // unfreeze the clip at the new (non-zero) fps rate
10590       freeze_callback(NULL, NULL, 0, (LiVESXModifierType)0, NULL);
10591       return;
10592     }
10593 
10594     if (cfile->pb_fps == 0. && !cfile->play_paused) {
10595       // freeze the clip
10596       freeze_callback(NULL, NULL, 0, (LiVESXModifierType)0, NULL);
10597       return;
10598     }
10599   }
10600 
10601 
10602   boolean on_mouse_scroll(LiVESWidget * widget, LiVESXEventScroll * event, livespointer user_data) {
10603     lives_mt *mt = (lives_mt *)user_data;
10604     LiVESXModifierType kstate = (LiVESXModifierType)event->state;
10605     uint32_t type = 1;
10606 
10607     if (!LIVES_IS_INTERACTIVE) return FALSE;
10608     if (mt) {
10609       // multitrack mode
10610       if ((kstate & LIVES_DEFAULT_MOD_MASK) == LIVES_CONTROL_MASK) {
10611         if (lives_get_scroll_direction(event) == LIVES_SCROLL_UP) mt_zoom_in(NULL, mt);
10612         else if (lives_get_scroll_direction(event) == LIVES_SCROLL_DOWN) mt_zoom_out(NULL, mt);
10613         return FALSE;
10614       }
10615 
10616       if (!prefs->mouse_scroll_clips) return FALSE;
10617 
10618       if (mt->poly_state == POLY_CLIPS) {
10619         // check if mouse pointer is over clip scroll tab
10620         LiVESXWindow *window = lives_display_get_window_at_pointer
10621                                ((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
10622                                 mt->display, NULL, NULL);
10623 
10624         if (widget == mt->clip_scroll || window == lives_widget_get_xwindow(mt->poly_box)) {
10625           // scroll fwd / back in clips
10626           if (lives_get_scroll_direction(event) == LIVES_SCROLL_UP) mt_prevclip(NULL, NULL, 0, (LiVESXModifierType)0, user_data);
10627           else if (lives_get_scroll_direction(event) == LIVES_SCROLL_DOWN) mt_nextclip(NULL, NULL, 0, (LiVESXModifierType)0, user_data);
10628         }
10629       }
10630       return FALSE;
10631     }
10632 
10633     // clip editor mode
10634 
10635     if (!prefs->mouse_scroll_clips) return FALSE;
10636 
10637     if ((kstate & LIVES_DEFAULT_MOD_MASK) == (LIVES_CONTROL_MASK | LIVES_SHIFT_MASK)) type = 2; // bg
10638     else if ((kstate & LIVES_DEFAULT_MOD_MASK) == LIVES_CONTROL_MASK) type = 0; // fg or bg
10639 
10640     if (lives_get_scroll_direction(event) == LIVES_SCROLL_UP) prevclip_callback(NULL, NULL, 0, (LiVESXModifierType)0,
10641           LIVES_INT_TO_POINTER(type));
10642     else if (lives_get_scroll_direction(event) == LIVES_SCROLL_DOWN) nextclip_callback(NULL, NULL, 0, (LiVESXModifierType)0,
10643           LIVES_INT_TO_POINTER(type));
10644 
10645     return TRUE;
10646   }
10647 
10648 
10649   // next few functions are for the timer bars
10650   boolean on_mouse_sel_update(LiVESWidget * widget, LiVESXEventMotion * event, livespointer user_data) {
10651     if (!LIVES_IS_INTERACTIVE) return FALSE;
10652 
10653     if (CURRENT_CLIP_IS_VALID && mainw->sel_start > 0) {
10654       int x, sel_current;
10655       double tpos;
10656 
10657       lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
10658                                LIVES_MAIN_WINDOW_WIDGET, &x, NULL);
10659       tpos = (double)x / (double)(lives_widget_get_allocation_width(mainw->video_draw) - 1) * CLIP_TOTAL_TIME(mainw->current_file);
10660       if (mainw->sel_move == SEL_MOVE_AUTO)
10661         sel_current = calc_frame_from_time3(mainw->current_file, tpos);
10662       else
10663         sel_current = calc_frame_from_time(mainw->current_file, tpos);
10664 
10665       if (mainw->sel_move == SEL_MOVE_SINGLE) {
10666         sel_current = calc_frame_from_time3(mainw->current_file, tpos);
10667         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), sel_current);
10668         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), sel_current);
10669       }
10670 
10671       if (mainw->sel_move == SEL_MOVE_START || (mainw->sel_move == SEL_MOVE_AUTO && sel_current < mainw->sel_start)) {
10672         sel_current = calc_frame_from_time(mainw->current_file, tpos);
10673         if (LIVES_IS_PLAYING && sel_current > cfile->end) sel_current = cfile->end;
10674         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), sel_current);
10675       } else if (mainw->sel_move == SEL_MOVE_END || (mainw->sel_move == SEL_MOVE_AUTO && sel_current > mainw->sel_start)) {
10676         sel_current = calc_frame_from_time2(mainw->current_file, tpos);
10677         if (LIVES_IS_PLAYING && sel_current <= cfile->start) sel_current = cfile->start + 1;
10678         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), sel_current - 1);
10679       }
10680     }
10681     return FALSE;
10682   }
10683 
10684 
10685   boolean on_mouse_sel_reset(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
10686     if (!LIVES_IS_INTERACTIVE) return FALSE;
10687 
10688     if (mainw->current_file <= 0) return FALSE;
10689     mainw->sel_start = 0;
10690     if (!mainw->mouse_blocked) {
10691       lives_signal_handler_block(mainw->eventbox2, mainw->mouse_fn1);
10692       mainw->mouse_blocked = TRUE;
10693     }
10694     return FALSE;
10695   }
10696 
10697 
10698   boolean on_mouse_sel_start(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
10699     int x;
10700 
10701     if (!LIVES_IS_INTERACTIVE) return FALSE;
10702 
10703     if (mainw->current_file <= 0) return FALSE;
10704 
10705     lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
10706                              LIVES_MAIN_WINDOW_WIDGET, &x, NULL);
10707 
10708     mainw->sel_start = calc_frame_from_time(mainw->current_file,
10709                                             (double)x / (double)(lives_widget_get_allocation_width(mainw->video_draw) - 1)
10710                                             * CLIP_TOTAL_TIME(mainw->current_file));
10711 
10712     if (event->button == 3 && !mainw->selwidth_locked) {
10713       mainw->sel_start = calc_frame_from_time3(mainw->current_file,
10714                          (double)x / (double)lives_widget_get_allocation_width(mainw->video_draw)
10715                          * CLIP_TOTAL_TIME(mainw->current_file));
10716       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), mainw->sel_start);
10717       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), mainw->sel_start);
10718       mainw->sel_move = SEL_MOVE_AUTO;
10719     }
10720 
10721     else {
10722       if (event->button == 2 && !mainw->selwidth_locked) {
10723         mainw->sel_start = calc_frame_from_time3(mainw->current_file,
10724                            (double)x / (double)lives_widget_get_allocation_width(mainw->video_draw)
10725                            * CLIP_TOTAL_TIME(mainw->current_file));
10726         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), mainw->sel_start);
10727         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), (int)mainw->sel_start);
10728         mainw->sel_move = SEL_MOVE_SINGLE;
10729       }
10730 
10731       else {
10732         if (!mainw->selwidth_locked) {
10733           if ((mainw->sel_start < cfile->end && ((mainw->sel_start - cfile->start) <= (cfile->end - mainw->sel_start))) ||
10734               mainw->sel_start < cfile->start) {
10735             if (LIVES_IS_PLAYING && mainw->sel_start >= cfile->end) {
10736               mainw->sel_start = cfile->end;
10737               lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), mainw->sel_start);
10738             }
10739             lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), mainw->sel_start);
10740             mainw->sel_move = SEL_MOVE_START;
10741           } else {
10742             mainw->sel_start = calc_frame_from_time2(mainw->current_file,
10743                                (double)x / (double)lives_widget_get_allocation_width(mainw->video_draw)
10744                                * CLIP_TOTAL_TIME(mainw->current_file));
10745             if (LIVES_IS_PLAYING && mainw->sel_start <= cfile->start) {
10746               mainw->sel_start = cfile->start;
10747               lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), mainw->sel_start);
10748             } else
10749               lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), mainw->sel_start - 1);
10750             mainw->sel_move = SEL_MOVE_END;
10751           }
10752         } else {
10753           // locked selection
10754           if (mainw->sel_start > cfile->end) {
10755             // past end
10756             if (cfile->end + cfile->end - cfile->start + 1 <= cfile->frames) {
10757               lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->end + cfile->end - cfile->start + 1);
10758               mainw->sel_move = SEL_MOVE_START;
10759             }
10760           } else {
10761             if (mainw->sel_start >= cfile->start) {
10762               if (mainw->sel_start > cfile->start + (cfile->end - cfile->start + 1) / 2) {
10763                 // nearer to end
10764                 lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), mainw->sel_start);
10765                 mainw->sel_move = SEL_MOVE_END;
10766               } else {
10767                 // nearer to start
10768                 lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), mainw->sel_start);
10769                 mainw->sel_move = SEL_MOVE_START;
10770               }
10771             } else {
10772               // before start
10773               if (cfile->start - cfile->end + cfile->start - 1 >= 1) {
10774                 lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->start - 1);
10775                 mainw->sel_move = SEL_MOVE_END;
10776 	      // *INDENT-OFF*
10777 	    }}}}}}
10778 	  // *INDENT-ON*
10779 
10780     if (mainw->mouse_blocked) {// stops a warning if the user clicks around a lot...
10781       lives_signal_handler_unblock(mainw->eventbox2, mainw->mouse_fn1);
10782       mainw->mouse_blocked = FALSE;
10783     }
10784     return FALSE;
10785   }
10786 
10787 
10788 #ifdef ENABLE_GIW_3
10789   void on_hrule_value_changed(LiVESWidget * widget, livespointer user_data) {
10790     if (!LIVES_IS_INTERACTIVE) return;
10791     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) return;
10792 
10793     if (LIVES_IS_PLAYING) {
10794       lives_clip_t *pfile = mainw->files[mainw->playing_file];
10795       if (pfile->frames > 0) {
10796         pfile->frameno = pfile->last_frameno = calc_frame_from_time(mainw->playing_file,
10797                                                giw_timeline_get_value(GIW_TIMELINE(widget)));
10798         mainw->scratch = SCRATCH_JUMP;
10799       }
10800       return;
10801     }
10802 
10803     cfile->pointer_time = lives_ce_update_timeline(0, giw_timeline_get_value(GIW_TIMELINE(widget)));
10804     if (cfile->frames > 0) cfile->frameno = cfile->last_frameno = calc_frame_from_time(mainw->current_file, cfile->pointer_time);
10805 
10806     if (cfile->pointer_time > 0.) {
10807       lives_widget_set_sensitive(mainw->rewind, TRUE);
10808       lives_widget_set_sensitive(mainw->trim_to_pstart, (cfile->achans * cfile->frames > 0));
10809       lives_widget_set_sensitive(mainw->m_rewindbutton, TRUE);
10810       if (mainw->preview_box) {
10811         lives_widget_set_sensitive(mainw->p_rewindbutton, TRUE);
10812       }
10813     } else {
10814       lives_widget_set_sensitive(mainw->rewind, FALSE);
10815       lives_widget_set_sensitive(mainw->trim_to_pstart, FALSE);
10816       lives_widget_set_sensitive(mainw->m_rewindbutton, FALSE);
10817       if (mainw->preview_box) {
10818         lives_widget_set_sensitive(mainw->p_rewindbutton, FALSE);
10819       }
10820     }
10821     mainw->ptrtime = cfile->pointer_time;
10822     lives_widget_queue_draw(mainw->eventbox2);
10823   }
10824 
10825 #else
10826 
10827   boolean on_hrule_update(LiVESWidget * widget, LiVESXEventMotion * event, livespointer user_data) {
10828     LiVESXModifierType modmask;
10829     LiVESXDevice *device;
10830     int x;
10831     if (LIVES_IS_PLAYING) return TRUE;
10832     if (!LIVES_IS_INTERACTIVE) return TRUE;
10833     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) return TRUE;
10834 
10835     device = (LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device;
10836     lives_widget_get_modmask(device, widget, &modmask);
10837 
10838     if (!(modmask & LIVES_BUTTON1_MASK)) return TRUE;
10839 
10840     lives_widget_get_pointer(device, widget, &x, NULL);
10841     cfile->pointer_time = lives_ce_update_timeline(0,
10842                           (double)x / (double()lives_widget_get_allocation_width(widget) - 1)
10843                           * CLIP_TOTAL_TIME(mainw->current_file));
10844     if (cfile->frames > 0) cfile->frameno = calc_frame_from_time(mainw->current_file, cfile->pointer_time);
10845     return TRUE;
10846   }
10847 
10848 
10849   boolean on_hrule_reset(LiVESWidget * widget, LiVESXEventButton  * event, livespointer user_data) {
10850     //button release
10851     int x;
10852 
10853     if (LIVES_IS_PLAYING) return FALSE;
10854     if (!LIVES_IS_INTERACTIVE) return FALSE;
10855     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) return FALSE;
10856 
10857     lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
10858                              widget, &x, NULL);
10859     cfile->pointer_time = lives_ce_update_timeline(0,
10860                           (double)x / (double)(lives_widget_get_allocation_width(widget) - 1)
10861                           * CLIP_TOTAL_TIME(mainw->current_file));
10862     if (cfile->frames > 0) {
10863       cfile->last_frameno = cfile->frameno = calc_frame_from_time(mainw->current_file, cfile->pointer_time);
10864     }
10865     if (cfile->pointer_time > 0.) {
10866       lives_widget_set_sensitive(mainw->rewind, TRUE);
10867       lives_widget_set_sensitive(mainw->trim_to_pstart, (cfile->achans * cfile->frames > 0));
10868       lives_widget_set_sensitive(mainw->m_rewindbutton, TRUE);
10869       if (mainw->preview_box) {
10870         lives_widget_set_sensitive(mainw->p_rewindbutton, TRUE);
10871       }
10872     } else {
10873       lives_widget_set_sensitive(mainw->rewind, FALSE);
10874       lives_widget_set_sensitive(mainw->trim_to_pstart, FALSE);
10875       lives_widget_set_sensitive(mainw->m_rewindbutton, FALSE);
10876       if (mainw->preview_box) {
10877         lives_widget_set_sensitive(mainw->p_rewindbutton, FALSE);
10878       }
10879     }
10880     return FALSE;
10881   }
10882 
10883 
10884   boolean on_hrule_set(LiVESWidget * widget, LiVESXEventButton * event, livespointer user_data) {
10885     // button press
10886     int x;
10887 
10888     if (!LIVES_IS_INTERACTIVE) return FALSE;
10889     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) return TRUE;
10890 
10891     lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
10892                              widget, &x, NULL);
10893 
10894     cfile->pointer_time = lives_ce_update_timeline(0,
10895                           (double)x / (double)(lives_widget_get_allocation_width(widget) - 1)
10896                           * CLIP_TOTAL_TIME(mainw->current_file));
10897     if (cfile->frames > 0) cfile->frameno = cfile->last_frameno = calc_frame_from_time(mainw->current_file, cfile->pointer_time);
10898     if (LIVES_IS_PLAYING) mainw->scratch = SCRATCH_JUMP;
10899 
10900     mainw->ptrtime = cfile->pointer_time;
10901     lives_widget_queue_draw(mainw->eventbox2);
10902     g_print("HRSET\n");
10903     return TRUE;
10904   }
10905 
10906 #endif
10907 
10908 
10909   boolean frame_context(LiVESWidget * widget, LiVESXEventButton * event, livespointer which) {
10910     //popup a context menu when we right click on a frame
10911 
10912     LiVESWidget *save_frame_as;
10913     LiVESWidget *menu;
10914 
10915     int frame = 0;
10916 
10917     if (!LIVES_IS_INTERACTIVE) return FALSE;
10918     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) return FALSE;
10919 
10920     if (mainw->multitrack && !mainw->multitrack->event_list) return FALSE;
10921 
10922     // only accept right mouse clicks
10923 
10924     if (event->button != 3) return FALSE;
10925 
10926     if (!mainw->multitrack) {
10927       switch (LIVES_POINTER_TO_INT(which)) {
10928       case 1:
10929         // start frame
10930         frame = cfile->start;
10931         break;
10932       case 2:
10933         // end frame
10934         frame = cfile->end;
10935         break;
10936       default:
10937         // preview frame
10938         frame = mainw->preview_frame;
10939         break;
10940       }
10941     }
10942 
10943     menu = lives_menu_new();
10944     lives_menu_set_title(LIVES_MENU(menu), _("Selected Frame"));
10945 
10946     if (palette->style & STYLE_1) {
10947       lives_widget_set_bg_color(menu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
10948       lives_widget_set_fg_color(menu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
10949     }
10950 
10951     if (cfile->frames > 0 || mainw->multitrack) {
10952       save_frame_as = lives_standard_menu_item_new_with_label(_("_Save Frame as..."));
10953       lives_signal_sync_connect(LIVES_GUI_OBJECT(save_frame_as), LIVES_WIDGET_ACTIVATE_SIGNAL,
10954                                 LIVES_GUI_CALLBACK(save_frame),
10955                                 LIVES_INT_TO_POINTER(frame));
10956 
10957       if (capable->has_convert && capable->has_composite)
10958         lives_container_add(LIVES_CONTAINER(menu), save_frame_as);
10959     }
10960 
10961     lives_widget_show_all(menu);
10962     lives_menu_popup(LIVES_MENU(menu), event);
10963 
10964     return FALSE;
10965   }
10966 
10967 
10968   void on_less_pressed(LiVESButton * button, livespointer user_data) {
10969     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) return;
10970 
10971     if (!LIVES_IS_PLAYING || mainw->internal_messaging || (mainw->is_processing && cfile->is_loaded)) return;
10972     if (cfile->next_event) return;
10973 
10974     if (mainw->rte_keys != -1) {
10975       mainw->blend_factor -= prefs->blendchange_amount * (double)KEY_RPT_INTERVAL / 1000.;
10976       weed_set_blend_factor(mainw->rte_keys);
10977     }
10978   }
10979 
10980 
10981   void on_slower_pressed(LiVESButton * button, livespointer user_data) {
10982     double change = 1., new_fps;
10983 
10984     int type = 0;
10985 
10986     lives_clip_t *sfile = cfile;
10987     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) return;
10988 
10989     if (!LIVES_IS_PLAYING || mainw->internal_messaging || (mainw->is_processing && cfile->is_loaded)) return;
10990     if (mainw->record && !(prefs->rec_opts & REC_FRAMES)) return;
10991     if (cfile->next_event) return;
10992 
10993     if (mainw->record && !mainw->record_paused && !(prefs->rec_opts & REC_FPS)) return;
10994 
10995     if (user_data) {
10996       type = LIVES_POINTER_TO_INT(user_data);
10997       if (type == SCREEN_AREA_BACKGROUND) {
10998         if (!IS_NORMAL_CLIP(mainw->blend_file)) return;
10999         sfile = mainw->files[mainw->blend_file];
11000       } else if (!IS_NORMAL_CLIP(mainw->current_file)) return;
11001     }
11002 
11003     if (sfile->next_event) return;
11004 
11005     change *= prefs->fpschange_amount / TICKS_PER_SECOND_DBL * (double)KEY_RPT_INTERVAL * sfile->pb_fps;
11006     if (button) change *= 4.;
11007     if (sfile->pb_fps == 0.) return;
11008     if (sfile->pb_fps > 0.) {
11009       if (sfile->pb_fps < 0.1 || sfile->pb_fps < change) sfile->pb_fps = change;
11010       new_fps = sfile->pb_fps - change;
11011       if (sfile == cfile) lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), new_fps);
11012       else sfile->pb_fps = new_fps;
11013     } else {
11014       if (sfile->pb_fps > change) sfile->pb_fps = change;
11015       if (sfile == cfile) lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), (sfile->pb_fps - change));
11016       else sfile->pb_fps -= change;
11017     }
11018   }
11019 
11020 
11021   void on_more_pressed(LiVESButton * button, livespointer user_data) {
11022     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) return;
11023 
11024     if (!LIVES_IS_PLAYING || mainw->internal_messaging || (mainw->is_processing && cfile->is_loaded)) return;
11025     if (cfile->next_event) return;
11026 
11027     if (mainw->rte_keys != -1) {
11028       mainw->blend_factor += prefs->blendchange_amount * (double)KEY_RPT_INTERVAL / 1000.;
11029       weed_set_blend_factor(mainw->rte_keys);
11030     }
11031   }
11032 
11033 
11034   void on_faster_pressed(LiVESButton * button, livespointer user_data) {
11035     double change = 1.;
11036     int type = 0;
11037 
11038     lives_clip_t *sfile = cfile;
11039     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) return;
11040 
11041     if (!LIVES_IS_PLAYING || mainw->internal_messaging || (mainw->is_processing && cfile->is_loaded)) return;
11042     if (mainw->record && !(prefs->rec_opts & REC_FRAMES)) return;
11043     if (cfile->next_event) return;
11044 
11045     if (mainw->record && !mainw->record_paused && !(prefs->rec_opts & REC_FPS)) return;
11046 
11047     if (user_data) {
11048       type = LIVES_POINTER_TO_INT(user_data);
11049       if (type == SCREEN_AREA_BACKGROUND) {
11050         if (!IS_NORMAL_CLIP(mainw->blend_file)) return;
11051         sfile = mainw->files[mainw->blend_file];
11052       } else if (!IS_NORMAL_CLIP(mainw->current_file)) return;
11053     }
11054 
11055     if (sfile->play_paused && sfile->freeze_fps < 0.) {
11056       sfile->pb_fps = -.00000001; // want to keep this as a negative value so when we unfreeze we still play in reverse
11057     }
11058 
11059     if (sfile->next_event) return;
11060 
11061     change *= prefs->fpschange_amount / TICKS_PER_SECOND_DBL * (double)KEY_RPT_INTERVAL
11062               * (sfile->pb_fps == 0. ? 1. : sfile->pb_fps);
11063     if (button) change *= 4.;
11064 
11065     if (sfile->pb_fps >= 0.) {
11066       if (sfile->pb_fps == FPS_MAX) return;
11067       if (sfile->pb_fps < 0.5) sfile->pb_fps = .5;
11068       if (sfile->pb_fps > FPS_MAX - change) sfile->pb_fps = FPS_MAX - change;
11069       if (sfile == cfile) lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), (sfile->pb_fps + change));
11070       else sfile->pb_fps = sfile->pb_fps + change;
11071     } else {
11072       if (sfile->pb_fps == -FPS_MAX) return;
11073       if (sfile->pb_fps < -FPS_MAX - change) sfile->pb_fps = -FPS_MAX - change;
11074       if (sfile == cfile) lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), (sfile->pb_fps + change));
11075       else sfile->pb_fps = sfile->pb_fps + change;
11076     }
11077   }
11078 
11079 
11080   void on_back_pressed(LiVESButton * button, livespointer user_data) {
11081     int type = 0;
11082     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_NORMAL) return;
11083     if (mainw->internal_messaging || (mainw->is_processing && cfile->is_loaded)) return;
11084     if (LIVES_IS_PLAYING && !clip_can_reverse(mainw->current_file)) return;
11085 
11086     if (mainw->record && !(prefs->rec_opts & REC_FRAMES)) return;
11087     if (cfile->next_event) return;
11088 
11089     if (!LIVES_IS_PLAYING) {
11090       if (cfile->real_pointer_time > 0.) {
11091         cfile->real_pointer_time--;
11092         if (cfile->real_pointer_time < 0.) cfile->real_pointer_time = 0.;
11093         lives_ce_update_timeline(0, cfile->real_pointer_time);
11094       }
11095       return;
11096     }
11097 
11098     if (user_data) {
11099       type = LIVES_POINTER_TO_INT(user_data);
11100       if (type == SCREEN_AREA_BACKGROUND) return; // TODO: implement scratch play for the blend file
11101     }
11102 
11103     if (button) mainw->scratch |= SCRATCH_BACK_EXTRA;
11104     else mainw->scratch = SCRATCH_BACK;
11105   }
11106 
11107 
11108   void on_forward_pressed(LiVESButton * button, livespointer user_data) {
11109     int type = 0;
11110     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_NORMAL) return;
11111     if (mainw->internal_messaging || (mainw->is_processing && cfile->is_loaded)) return;
11112 
11113     if (mainw->record && !(prefs->rec_opts & REC_FRAMES)) return;
11114     if (cfile->next_event) return;
11115 
11116     if (!LIVES_IS_PLAYING) {
11117       if (cfile->real_pointer_time < CURRENT_CLIP_TOTAL_TIME) {
11118         cfile->real_pointer_time++;
11119         if (cfile->real_pointer_time > CURRENT_CLIP_TOTAL_TIME) cfile->real_pointer_time = CURRENT_CLIP_TOTAL_TIME;
11120         lives_ce_update_timeline(0, cfile->real_pointer_time);
11121       }
11122       return;
11123     }
11124 
11125     if (user_data) {
11126       type = LIVES_POINTER_TO_INT(user_data);
11127       if (type == SCREEN_AREA_BACKGROUND) return; // TODO: implement scratch play for the blend file
11128     }
11129 
11130     if (button) mainw->scratch = SCRATCH_FWD_EXTRA;
11131     else mainw->scratch = SCRATCH_FWD;
11132   }
11133 
11134 
11135   boolean freeze_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
11136                           livespointer user_data) {
11137     weed_timecode_t tc;
11138     static boolean norecurse = FALSE;
11139 
11140     if (norecurse) return TRUE;
11141 
11142     if (mainw->multitrack && (LIVES_IS_PLAYING || mainw->multitrack->is_paused)) {
11143       on_playall_activate(NULL, NULL);
11144       return TRUE;
11145     }
11146     if (CURRENT_CLIP_IS_CLIPBOARD || !CURRENT_CLIP_IS_VALID) return TRUE;
11147     if (!LIVES_IS_PLAYING || (mainw->is_processing && cfile->is_loaded)) return TRUE;
11148     if (mainw->record && !(prefs->rec_opts & REC_FRAMES)) return TRUE;
11149 
11150     // TODO: make pref (reset keymode grab on freeze)
11151     //if (group) mainw->rte_keys = -1;
11152 
11153     if (cfile->play_paused) {
11154       cfile->pb_fps = cfile->freeze_fps;
11155       if (cfile->pb_fps != 0.) mainw->period = TICKS_PER_SECOND_DBL / cfile->pb_fps;
11156       else mainw->period = INT_MAX;
11157       cfile->play_paused = FALSE;
11158       if (mainw->record && !mainw->record_paused) {
11159         pthread_mutex_lock(&mainw->event_list_mutex);
11160         // write a RECORD_START marker
11161         tc = get_event_timecode(get_last_event(mainw->event_list));
11162         mainw->event_list = append_marker_event(mainw->event_list, tc, EVENT_MARKER_RECORD_START); // mark record end
11163         pthread_mutex_unlock(&mainw->event_list_mutex);
11164       }
11165     } else {
11166       if (mainw->record) {
11167         pthread_mutex_lock(&mainw->event_list_mutex);
11168         // write a RECORD_END marker
11169         tc = get_event_timecode(get_last_event(mainw->event_list));
11170         mainw->event_list = append_marker_event(mainw->event_list, tc, EVENT_MARKER_RECORD_END); // mark record end
11171         pthread_mutex_unlock(&mainw->event_list_mutex);
11172       }
11173 
11174       cfile->freeze_fps = cfile->pb_fps;
11175       cfile->play_paused = TRUE;
11176       cfile->pb_fps = 0.;
11177       mainw->timeout_ticks = mainw->currticks;
11178     }
11179 
11180     if (group) {
11181       norecurse = TRUE;
11182       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), cfile->pb_fps);
11183       norecurse = FALSE;
11184     }
11185 
11186     if (prefs->audio_src == AUDIO_SRC_INT) {
11187 #ifdef ENABLE_JACK
11188       if (mainw->jackd && prefs->audio_player == AUD_PLAYER_JACK
11189           && mainw->jackd->playing_file == mainw->playing_file && (prefs->jack_opts & JACK_OPTS_NOPLAY_WHEN_PAUSED ||
11190               prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS)) {
11191         mainw->jackd->is_paused = cfile->play_paused;
11192         if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO) && mainw->agen_key == 0 &&
11193             !mainw->agen_needs_reinit) {
11194           if (cfile->play_paused) {
11195             weed_plant_t *event = get_last_frame_event(mainw->event_list);
11196             insert_audio_event_at(event, -1, mainw->jackd->playing_file, 0., 0.); // audio switch off
11197           } else {
11198             jack_get_rec_avals(mainw->jackd);
11199           }
11200         }
11201 #ifdef ENABLE_JACK_TRANSPORT
11202         if (cfile->play_paused) jack_pb_stop();
11203         else jack_pb_start(-1.);
11204 #endif
11205       }
11206 #endif
11207 #ifdef HAVE_PULSE_AUDIO
11208       if (mainw->pulsed && prefs->audio_player == AUD_PLAYER_PULSE
11209           && mainw->pulsed->playing_file == mainw->playing_file && (prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS)) {
11210         if (!cfile->play_paused) mainw->pulsed->in_arate = cfile->arate * cfile->pb_fps / cfile->fps;
11211         mainw->pulsed->is_paused = cfile->play_paused;
11212         if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO) && mainw->agen_key == 0 &&
11213             !mainw->agen_needs_reinit) {
11214           if (cfile->play_paused) {
11215             if (!mainw->mute) {
11216               weed_plant_t *event = get_last_frame_event(mainw->event_list);
11217               insert_audio_event_at(event, -1, mainw->pulsed->playing_file, 0., 0.); // audio switch off
11218             }
11219           } else {
11220             pulse_get_rec_avals(mainw->pulsed);
11221           }
11222         }
11223       }
11224 #endif
11225     }
11226     if (LIVES_IS_PLAYING) mainw->force_show = TRUE;
11227     return TRUE;
11228   }
11229 
11230 
11231   boolean nervous_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
11232                            livespointer clip_number) {
11233     if (mainw->multitrack) return FALSE;
11234     mainw->nervous = !mainw->nervous;
11235     return TRUE;
11236   }
11237 
11238 
11239   /**
11240      @brief lock or unlock audio track changes in free playback
11241      if lock is switched on then the prefs to follow video clip changes and rate / direction changes
11242      are overriden, in addition  the audio is ismmediately synced to the current video track
11243 
11244      switching off audio lock re-enables these prefs settings
11245 
11246      if LiVES is not playing, or the audio player is non-realtime then there is no effect
11247   */
11248   boolean aud_lock_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
11249                             livespointer statep) {
11250     boolean state = LIVES_POINTER_TO_INT(statep);
11251     if (!LIVES_IS_PLAYING || !is_realtime_aplayer(prefs->audio_player) || mainw->multitrack
11252         || mainw->is_rendering || mainw->preview || mainw->agen_key != 0 || mainw->agen_needs_reinit
11253         || prefs->audio_src == AUDIO_SRC_EXT) return TRUE;
11254 
11255     if (!state) {
11256       // lock OFF
11257       prefs->audio_opts = future_prefs->audio_opts;
11258       return TRUE;
11259     }
11260     if (switch_audio_clip(mainw->current_file, TRUE)) {
11261       if (prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS) {
11262         mainw->currticks = lives_get_current_playback_ticks(mainw->origsecs, mainw->orignsecs, NULL);
11263         resync_audio(cfile->frameno);
11264         changed_fps_during_pb(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), LIVES_INT_TO_POINTER(TRUE));
11265       }
11266     }
11267     prefs->audio_opts &= ~(AUDIO_OPTS_FOLLOW_FPS | AUDIO_OPTS_FOLLOW_CLIPS);
11268     return TRUE;
11269   }
11270 
11271 
11272   char *get_palette_name_for_clip(int clipno) {
11273     lives_clip_t *sfile;
11274     char *palname = NULL;
11275     if (!IS_VALID_CLIP(clipno)) return NULL;
11276     sfile = mainw->files[clipno];
11277     if (IS_NORMAL_CLIP(clipno)) {
11278       if (is_virtual_frame(clipno, sfile->frameno)) {
11279         lives_clip_data_t *cdata = ((lives_decoder_t *)sfile->ext_src)->cdata;
11280         palname = lives_strdup(weed_palette_get_name_full(cdata->current_palette, cdata->YUV_clamping, cdata->YUV_subspace));
11281       } else {
11282         palname = lives_strdup(weed_palette_get_name((sfile->bpp == 24 ? WEED_PALETTE_RGB24 : WEED_PALETTE_RGBA32)));
11283       }
11284     } else switch (sfile->clip_type) {
11285       case CLIP_TYPE_GENERATOR: {
11286         weed_plant_t *inst = (weed_plant_t *)sfile->ext_src;
11287         if (inst) {
11288           weed_plant_t *channel = get_enabled_channel(inst, 0, FALSE);
11289           if (channel) {
11290             int clamping, subspace, pal;
11291             pal = weed_channel_get_palette_yuv(channel, &clamping, NULL, &subspace);
11292             palname = lives_strdup(weed_palette_get_name_full(pal, clamping, subspace));
11293           }
11294         }
11295       }
11296       break;
11297       case CLIP_TYPE_VIDEODEV: {
11298 #ifdef HAVE_UNICAP
11299         lives_vdev_t *ldev = (lives_vdev_t *)sfile->ext_src;
11300         palname = lives_strdup(weed_palette_get_name_full(ldev->current_palette, ldev->YUV_clamping, 0));
11301 #endif
11302       }
11303       break;
11304       default: break;
11305       }
11306     if (!palname) palname = lives_strdup("??????");
11307     return palname;
11308   }
11309 
11310 
11311   boolean show_sync_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
11312                              livespointer keybd) {
11313     if (!LIVES_IS_PLAYING) return FALSE;
11314     if (!CURRENT_CLIP_HAS_VIDEO || CURRENT_CLIP_IS_CLIPBOARD) return FALSE;
11315 
11316     if (!prefs->show_dev_opts) {
11317       int last_dprint_file = mainw->last_dprint_file;
11318       mainw->no_switch_dprint = TRUE;
11319       d_print_overlay(2.0, _("Playing frame %d / %d, at fps %.3f\n"),
11320                       mainw->actual_frame, cfile->frames, cfile->pb_fps);
11321       mainw->no_switch_dprint = FALSE;
11322       mainw->last_dprint_file = last_dprint_file;
11323       return FALSE;
11324     }
11325 
11326 #ifdef USE_LIVES_MFUNCS
11327     show_memstats();
11328 #endif
11329 
11330     lives_freep((void **)&mainw->overlay_msg);
11331 
11332     if (!keybd) mainw->lockstats = !mainw->lockstats;
11333     if (!mainw->lockstats) return FALSE;
11334 
11335     mainw->overlay_msg = get_stats_msg(FALSE);
11336     return FALSE;
11337   }
11338 
11339   /**
11340      @brief jump to a stored clip / frame position during free playback
11341      clip number and frame position can be stored and later triggered
11342      during playback
11343   */
11344   boolean storeclip_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
11345                              livespointer clip_number) {
11346     // ctrl-fn key will store a clip for higher switching
11347     int clip = LIVES_POINTER_TO_INT(clip_number) - 1;
11348     register int i;
11349 
11350     if (!LIVES_IS_INTERACTIVE) return TRUE;
11351 
11352     if (!CURRENT_CLIP_IS_VALID || mainw->preview || (LIVES_IS_PLAYING && mainw->event_list && !mainw->record)
11353         || (mainw->is_processing && cfile->is_loaded) || !mainw->cliplist) return TRUE;
11354 
11355     if (clip >= FN_KEYS - 1) {
11356       // last fn key will clear all
11357       for (i = 0; i < FN_KEYS - 1; i++) {
11358         mainw->clipstore[i][0] = -1;
11359       }
11360       return TRUE;
11361     }
11362 
11363     if (!IS_VALID_CLIP(mainw->clipstore[clip][0])) {
11364       mainw->clipstore[clip][0] = mainw->current_file;
11365       if (LIVES_IS_PLAYING) {
11366         mainw->clipstore[clip][1] = mainw->actual_frame;
11367       } else {
11368         int frame = calc_frame_from_time4(mainw->current_file, cfile->pointer_time);
11369         if (frame <= cfile->frames) mainw->clipstore[clip][1] = frame;
11370         else mainw->clipstore[clip][1] = 1;
11371       }
11372     } else {
11373       lives_clip_t *sfile = mainw->files[mainw->clipstore[clip][0]];
11374       if (LIVES_IS_PLAYING) {
11375         sfile->frameno = sfile->last_frameno = mainw->clipstore[clip][1];
11376         mainw->scratch = SCRATCH_JUMP;
11377       }
11378       if ((LIVES_IS_PLAYING && mainw->clipstore[clip][0] != mainw->playing_file)
11379           || (!LIVES_IS_PLAYING && mainw->clipstore[clip][0] != mainw->current_file)) {
11380         switch_clip(0, mainw->clipstore[clip][0], TRUE);
11381       }
11382       if (!LIVES_IS_PLAYING) {
11383         cfile->real_pointer_time = (mainw->clipstore[clip][1] - 1.) / cfile->fps;
11384         lives_ce_update_timeline(0, cfile->real_pointer_time);
11385       }
11386       if (mainw->loop_locked) unlock_loop_lock();
11387     }
11388     return TRUE;
11389   }
11390 
11391 
11392   void on_toolbar_hide(LiVESButton * button, livespointer user_data) {
11393     lives_widget_hide(mainw->tb_hbox);
11394     fullscreen_internal();
11395     prefs->show_tool = FALSE;
11396     set_boolean_pref(PREF_SHOW_TOOLBAR, FALSE);
11397   }
11398 
11399 
11400   void on_capture_activate(LiVESMenuItem * menuitem, livespointer user_data) {
11401     char **array;
11402     double rec_end_time = -1.;
11403     char *com;
11404     boolean sgui;
11405     int curr_file = mainw->current_file;
11406     LiVESResponseType response;
11407     mainw->mt_needs_idlefunc = FALSE;
11408 
11409 #if !GTK_CHECK_VERSION(3, 0, 0)
11410 #ifndef GDK_WINDOWING_X11
11411     do_error_dialog(
11412       _("\n\nThis function will only work with X11.\nPlease send a patch to get it working on other platforms.\n\n"));
11413     return;
11414 #endif
11415 #endif
11416 
11417     if (!capable->has_xwininfo) {
11418       do_error_dialog(_("\n\nYou must install \"xwininfo\" before you can use this feature\n\n"));
11419       return;
11420     }
11421 
11422     if (mainw->first_free_file == ALL_USED) {
11423       too_many_files();
11424       return;
11425     }
11426 
11427     if (mainw->multitrack) {
11428       if (mainw->multitrack->idlefunc > 0) {
11429         lives_source_remove(mainw->multitrack->idlefunc);
11430         mainw->multitrack->idlefunc = 0;
11431         mainw->mt_needs_idlefunc = TRUE;
11432       }
11433       mt_desensitise(mainw->multitrack);
11434     }
11435 
11436     if (prefs->rec_desktop_audio && ((prefs->audio_player == AUD_PLAYER_JACK && capable->has_jackd) ||
11437                                      (prefs->audio_player == AUD_PLAYER_PULSE && capable->has_pulse_audio))) {
11438       resaudw = create_resaudw(8, NULL, NULL);
11439     } else {
11440       resaudw = create_resaudw(9, NULL, NULL);
11441     }
11442     response = lives_dialog_run(LIVES_DIALOG(resaudw->dialog));
11443 
11444     if (response != LIVES_RESPONSE_OK) {
11445       lives_widget_destroy(resaudw->dialog);
11446 
11447       if (mainw->multitrack) {
11448         mt_sensitise(mainw->multitrack);
11449         maybe_add_mt_idlefunc();
11450       }
11451       return;
11452     }
11453 
11454     if (prefs->rec_desktop_audio && ((prefs->audio_player == AUD_PLAYER_JACK && capable->has_jackd) ||
11455                                      (prefs->audio_player == AUD_PLAYER_PULSE && capable->has_pulse_audio))) {
11456       mainw->rec_arate = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_arate)));
11457       mainw->rec_achans = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_achans)));
11458       mainw->rec_asamps = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps)));
11459 
11460       if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned))) {
11461         mainw->rec_signed_endian = AFORM_UNSIGNED;
11462       }
11463       if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend))) {
11464         mainw->rec_signed_endian |= AFORM_BIG_ENDIAN;
11465       }
11466     } else {
11467       mainw->rec_arate = mainw->rec_achans = mainw->rec_asamps = mainw->rec_signed_endian = 0;
11468     }
11469 
11470     mainw->rec_fps = lives_spin_button_get_value(LIVES_SPIN_BUTTON(resaudw->fps_spinbutton));
11471 
11472     if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->unlim_radiobutton))) {
11473       rec_end_time = (lives_spin_button_get_value(LIVES_SPIN_BUTTON(resaudw->hour_spinbutton)) * 60.
11474                       + lives_spin_button_get_value(LIVES_SPIN_BUTTON(resaudw->minute_spinbutton))) * 60.
11475                      + lives_spin_button_get_value(LIVES_SPIN_BUTTON(resaudw->second_spinbutton));
11476       mainw->rec_vid_frames = (rec_end_time * mainw->rec_fps + .5);
11477     } else mainw->rec_vid_frames = -1;
11478 
11479     lives_widget_destroy(resaudw->dialog);
11480     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
11481 
11482     lives_freep((void **)&resaudw);
11483 
11484     if (prefs->rec_desktop_audio && mainw->rec_arate <= 0 && ((prefs->audio_player == AUD_PLAYER_JACK && capable->has_jackd) ||
11485         (prefs->audio_player == AUD_PLAYER_PULSE && capable->has_pulse_audio))) {
11486       do_audrate_error_dialog();
11487       return;
11488     }
11489 
11490     if (rec_end_time == 0.) {
11491       widget_opts.non_modal = TRUE;
11492       do_error_dialog(_("\nRecord time must be greater than 0.\n"));
11493       widget_opts.non_modal = FALSE;
11494       if (mainw->multitrack) {
11495         mt_sensitise(mainw->multitrack);
11496         maybe_add_mt_idlefunc();
11497       }
11498       return;
11499     }
11500 
11501     lives_widget_hide(LIVES_MAIN_WINDOW_WIDGET);
11502     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
11503 
11504     sgui = prefs->show_gui;
11505     prefs->show_gui = FALSE;
11506 
11507     if (!(do_warning_dialog(
11508             _("Capture an External Window:\n\nClick on 'OK', then click on any desktop window to select it\n"
11509               "Click 'Cancel' to cancel\n\n")))) {
11510       if (sgui) {
11511         prefs->show_gui = TRUE;
11512         lives_widget_show(LIVES_MAIN_WINDOW_WIDGET);
11513       }
11514       d_print(_("External window was released.\n"));
11515       if (mainw->multitrack) {
11516         mt_sensitise(mainw->multitrack);
11517         maybe_add_mt_idlefunc();
11518       }
11519       return;
11520     }
11521 
11522     prefs->show_gui = sgui;
11523 
11524     // an example of using 'get_temp_handle()' ////////
11525     if (!get_temp_handle(-1)) {
11526       if (prefs->show_gui) {
11527         lives_widget_show(LIVES_MAIN_WINDOW_WIDGET);
11528         lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
11529       }
11530 
11531       if (mainw->multitrack) {
11532         mt_sensitise(mainw->multitrack);
11533         maybe_add_mt_idlefunc();
11534       }
11535       return;
11536     }
11537 
11538     com = lives_strdup_printf("%s get_window_id \"%s\"", prefs->backend, cfile->handle);
11539     lives_system(com, FALSE);
11540     lives_free(com);
11541 
11542     if (THREADVAR(com_failed)) {
11543       close_temp_handle(curr_file);
11544       if (prefs->show_gui) {
11545         lives_widget_show(LIVES_MAIN_WINDOW_WIDGET);
11546         lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
11547       }
11548 
11549       if (mainw->multitrack) {
11550         mt_sensitise(mainw->multitrack);
11551         maybe_add_mt_idlefunc();
11552       }
11553       return;
11554     }
11555 
11556     do_progress_dialog(TRUE, FALSE, _("Click on any desktop window to capture it\n"
11557                                       "When a blank LiVES window appears behind it, "
11558                                       "then it is being recorded.\n\n"
11559                                       "<b><big>To end recording, switch focus to the LiVES window,\n"
11560                                       "and press the 'q' key.</big></b>\n"));
11561 
11562     if (get_token_count(mainw->msg, '|') < 6) {
11563       close_temp_handle(curr_file);
11564       if (prefs->show_gui) {
11565         lives_widget_show(LIVES_MAIN_WINDOW_WIDGET);
11566         lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
11567       }
11568 
11569       if (mainw->multitrack) {
11570         mt_sensitise(mainw->multitrack);
11571         maybe_add_mt_idlefunc();
11572       }
11573       return;
11574     }
11575 
11576     array = lives_strsplit(mainw->msg, "|", -1);
11577 #if IS_MINGW
11578     mainw->foreign_id = (HWND)atoi(array[1]);
11579 #else
11580 #if GTK_CHECK_VERSION(3, 0, 0) || defined GUI_QT
11581     mainw->foreign_id = (Window)atoi(array[1]);
11582 #else
11583     mainw->foreign_id = (GdkNativeWindow)atoi(array[1]);
11584 #endif
11585 #endif
11586     mainw->foreign_width = atoi(array[2]);
11587     mainw->foreign_height = atoi(array[3]);
11588     mainw->foreign_bpp = atoi(array[4]);
11589     mainw->foreign_visual = lives_strdup(array[5]);
11590     lives_strfreev(array);
11591 
11592     close_temp_handle(curr_file);
11593 
11594     ////////////////////////////////////////
11595 
11596     d_print(_("\nExternal window captured. Width=%d, height=%d, bpp=%d. *Do not resize*\n\n"
11597               "Stop or 'q' to finish.\n(Default of %.3f frames per second will be used.)\n"),
11598             mainw->foreign_width, mainw->foreign_height, mainw->foreign_bpp, mainw->rec_fps);
11599 
11600     // start another copy of LiVES and wait for it to return values
11601     com = lives_strdup_printf("%s -capture %d %u %d %d %s %d %d %.4f %d %d %d %d \"%s\"",
11602                               capable->myname_full, capable->mainpid,
11603                               (unsigned int)mainw->foreign_id, mainw->foreign_width, mainw->foreign_height,
11604                               get_image_ext_for_type(IMG_TYPE_BEST),
11605                               mainw->foreign_bpp, mainw->rec_vid_frames, mainw->rec_fps, mainw->rec_arate,
11606                               mainw->rec_asamps, mainw->rec_achans, mainw->rec_signed_endian, mainw->foreign_visual);
11607 
11608     // force the dialog to disappear
11609     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
11610 
11611     lives_system(com, FALSE);
11612 
11613     if (prefs->show_gui) {
11614       lives_widget_show(LIVES_MAIN_WINDOW_WIDGET);
11615     }
11616 
11617     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
11618 
11619     if (!after_foreign_play() && mainw->cancelled == CANCEL_NONE) {
11620       widget_opts.non_modal = TRUE;
11621       do_error_dialog(_("LiVES was unable to capture this window. Sorry.\n"));
11622       widget_opts.non_modal = FALSE;
11623       sensitize();
11624     }
11625 
11626     if (mainw->multitrack) {
11627       polymorph(mainw->multitrack, POLY_NONE);
11628       polymorph(mainw->multitrack, POLY_CLIPS);
11629       mt_sensitise(mainw->multitrack);
11630       maybe_add_mt_idlefunc();
11631     }
11632   }
11633 
11634 
11635   void on_capture2_activate(void) {
11636     // this is in the second copy of lives, we are now going to grab frames from the X window
11637     char *capfilename = lives_strdup_printf(".capture.%d", mainw->foreign_key);
11638     char *capfile = lives_build_filename(prefs->workdir, capfilename, NULL);
11639 
11640     char buf[32];
11641 
11642     boolean retval;
11643     int capture_fd;
11644     register int i;
11645 
11646     retval = prepare_to_play_foreign();
11647 
11648     lives_freep((void **)&mainw->foreign_visual);
11649 
11650     if (!retval) exit(2);
11651 
11652     mainw->record_foreign = TRUE; // for now...
11653 
11654     play_file();
11655 
11656     // pass the handle and frames back to the caller
11657     capture_fd = creat(capfile, S_IRUSR | S_IWUSR);
11658     if (capture_fd < 0) {
11659       lives_free(capfile);
11660       exit(1);
11661     }
11662 
11663     for (i = 1; i < MAX_FILES; i++) {
11664       if (!mainw->files[i]) break;
11665       lives_write(capture_fd, mainw->files[i]->handle, lives_strlen(mainw->files[i]->handle), TRUE);
11666       lives_write(capture_fd, "|", 1, TRUE);
11667       lives_snprintf(buf, 32, "%d", cfile->frames);
11668       lives_write(capture_fd, buf, strlen(buf), TRUE);
11669       lives_write(capture_fd, "|", 1, TRUE);
11670     }
11671 
11672     close(capture_fd);
11673     lives_free(capfilename);
11674     lives_free(capfile);
11675     exit(0);
11676   }
11677 
11678 
11679   // TODO - move all encoder related stuff from here and plugins.c into encoders.c
11680   void on_encoder_ofmt_changed(LiVESCombo * combo, livespointer user_data) {
11681     // change encoder format in the encoder plugin
11682     render_details *rdet = (render_details *)user_data;
11683 
11684     LiVESList *ofmt_all = NULL;
11685 
11686     char **array;
11687     int counter, i;
11688     const char *new_fmt;
11689 
11690     if (!rdet) {
11691       new_fmt = lives_combo_get_active_text(LIVES_COMBO(prefsw->ofmt_combo));
11692     } else {
11693       new_fmt = lives_combo_get_active_text(LIVES_COMBO(rdet->ofmt_combo));
11694     }
11695 
11696     if (!strlen(new_fmt) || !strcmp(new_fmt, mainw->string_constants[LIVES_STRING_CONSTANT_ANY])) {
11697       return;
11698     }
11699 
11700     if ((ofmt_all = plugin_request_by_line(PLUGIN_ENCODERS, future_prefs->encoder.name, "get_formats")) != NULL) {
11701       // get details for the current format
11702       counter = 0;
11703       for (i = 0; i < lives_list_length(ofmt_all); i++) {
11704         if (get_token_count((char *)lives_list_nth_data(ofmt_all, i), '|') > 2) {
11705           array = lives_strsplit((char *)lives_list_nth_data(ofmt_all, i), "|", -1);
11706 
11707           if (!strcmp(array[1], new_fmt)) {
11708             if (prefsw) {
11709               lives_signal_handler_block(prefsw->ofmt_combo, prefsw->encoder_ofmt_fn);
11710               lives_combo_set_active_index(LIVES_COMBO(prefsw->ofmt_combo), counter);
11711               lives_signal_handler_unblock(prefsw->ofmt_combo, prefsw->encoder_ofmt_fn);
11712             }
11713             if (rdet) {
11714               lives_signal_handler_block(rdet->ofmt_combo, rdet->encoder_ofmt_fn);
11715               lives_combo_set_active_index(LIVES_COMBO(rdet->ofmt_combo), counter);
11716               lives_signal_handler_unblock(rdet->ofmt_combo, rdet->encoder_ofmt_fn);
11717             }
11718             lives_snprintf(future_prefs->encoder.of_name, 64, "%s", array[0]);
11719             lives_snprintf(future_prefs->encoder.of_desc, 128, "%s", array[1]);
11720 
11721             future_prefs->encoder.of_allowed_acodecs = atoi(array[2]);
11722             lives_snprintf(future_prefs->encoder.of_restrict, 1024, "%s", array[3]);
11723             lives_strfreev(array);
11724             break;
11725           }
11726           lives_strfreev(array);
11727           counter++;
11728         }
11729       }
11730       lives_list_free_all(&ofmt_all);
11731 
11732       if (rdet && !prefsw) {
11733         if (strcmp(prefs->encoder.of_name, future_prefs->encoder.of_name)) {
11734           rdet->enc_changed = TRUE;
11735           lives_snprintf(prefs->encoder.of_name, 64, "%s", future_prefs->encoder.of_name);
11736           lives_snprintf(prefs->encoder.of_desc, 128, "%s", future_prefs->encoder.of_desc);
11737           lives_snprintf(prefs->encoder.of_restrict, 1024, "%s", future_prefs->encoder.of_restrict);
11738           prefs->encoder.of_allowed_acodecs = future_prefs->encoder.of_allowed_acodecs;
11739           set_string_pref(PREF_OUTPUT_TYPE, prefs->encoder.of_name);
11740         }
11741       }
11742       set_acodec_list_from_allowed(prefsw, rdet);
11743     } else {
11744       do_plugin_encoder_error(future_prefs->encoder.name);
11745     }
11746   }
11747 
11748   // TODO - move all this to audio.c
11749 
11750   void on_export_audio_activate(LiVESMenuItem * menuitem, livespointer user_data) {
11751 
11752     char *filt[] = {"*."LIVES_FILE_EXT_WAV, NULL};
11753     char *filename, *file_name;
11754     char *com, *tmp;
11755 
11756     double start, end;
11757 
11758     int nrate = cfile->arps;
11759     int asigned = !(cfile->signed_endian & AFORM_UNSIGNED);
11760 
11761     if (cfile->end > 0 && !LIVES_POINTER_TO_INT(user_data)) {
11762       filename = choose_file((*mainw->audio_dir) ? mainw->audio_dir : NULL, NULL,
11763                              filt, LIVES_FILE_CHOOSER_ACTION_SAVE, _("Export Selected Audio as..."), NULL);
11764     } else {
11765       filename = choose_file((*mainw->audio_dir) ? mainw->audio_dir : NULL, NULL,
11766                              filt, LIVES_FILE_CHOOSER_ACTION_SAVE, _("Export Audio as..."), NULL);
11767     }
11768 
11769     if (!filename) return;
11770     file_name = ensure_extension(filename, LIVES_FILE_EXT_WAV);
11771     lives_free(filename);
11772 
11773     // warn if arps!=arate
11774     if (cfile->arate != cfile->arps) {
11775       if (do_warning_dialog(
11776             _("\n\nThe audio playback speed has been altered for this clip.\nClick 'OK' to export at the new speed, "
11777               "or 'Cancel' to export at the original rate.\n"))) {
11778         nrate = cfile->arate;
11779       }
11780     }
11781 
11782     if (cfile->start * cfile->end > 0 && !LIVES_POINTER_TO_INT(user_data)) {
11783       lives_snprintf(mainw->msg, MAINW_MSG_SIZE, _("Exporting audio frames %d to %d as %s..."), cfile->start, cfile->end, file_name);
11784       start = calc_time_from_frame(mainw->current_file, cfile->start);
11785       end = calc_time_from_frame(mainw->current_file, cfile->end);
11786     } else {
11787       lives_snprintf(mainw->msg, MAINW_MSG_SIZE, _("Exporting audio as %s..."), file_name);
11788       start = 0.;
11789       end = 0.;
11790     }
11791 
11792     d_print(mainw->msg);
11793 
11794     com = lives_strdup_printf("%s export_audio \"%s\" %.8f %.8f %d %d %d %d %d \"%s\"", prefs->backend, cfile->handle,
11795                               start, end, cfile->arps, cfile->achans, cfile->asampsize, asigned, nrate,
11796                               (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)));
11797     lives_free(tmp);
11798 
11799     lives_rm(cfile->info_file);
11800     lives_system(com, FALSE);
11801     lives_free(com);
11802 
11803     if (THREADVAR(com_failed)) {
11804       lives_free(file_name);
11805       d_print_failed();
11806       return;
11807     }
11808 
11809     do_progress_dialog(TRUE, FALSE, _("Exporting audio"));
11810 
11811     if (mainw->error) {
11812       d_print_failed();
11813       widget_opts.non_modal = TRUE;
11814       do_error_dialog(mainw->msg);
11815       widget_opts.non_modal = FALSE;
11816     } else {
11817       d_print_done();
11818       lives_snprintf(mainw->audio_dir, PATH_MAX, "%s", file_name);
11819       get_dirname(mainw->audio_dir);
11820     }
11821     lives_free(file_name);
11822   }
11823 
11824 
11825   void on_normalise_audio_activate(LiVESMenuItem * menuitem, livespointer user_data) {
11826     char *com;
11827 
11828     if (!CURRENT_CLIP_HAS_AUDIO) return;
11829 
11830     d_print(_("Normalising audio..."));
11831 
11832     if (menuitem) desensitize();
11833     do_threaded_dialog(_("Normalizing audio..."), TRUE);
11834 
11835     threaded_dialog_spin(0.);
11836 
11837     if (!prefs->conserve_space) {
11838       com = lives_strdup_printf("%s backup_audio \"%s\"", prefs->backend_sync, cfile->handle);
11839       lives_system(com, FALSE);
11840       lives_free(com);
11841       if (THREADVAR(com_failed)) {
11842         end_threaded_dialog();
11843         d_print_failed();
11844         if (menuitem) sensitize();
11845         return;
11846       }
11847     }
11848     normalise_audio(mainw->current_file, 0., 0., .95);
11849 
11850     if (THREADVAR(write_failed) || THREADVAR(read_failed)) {
11851       THREADVAR(read_failed) = THREADVAR(write_failed) = 0;
11852       end_threaded_dialog();
11853       d_print_failed();
11854       if (menuitem) sensitize();
11855       return;
11856     }
11857 
11858     if (mainw->cancelled != CANCEL_NONE) {
11859       com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, cfile->handle);
11860       mainw->cancelled = CANCEL_NONE;
11861       lives_system(com, FALSE);
11862       lives_free(com);
11863       end_threaded_dialog();
11864       d_print_cancelled();
11865       if (menuitem) sensitize();
11866       return;
11867     }
11868 
11869     if (menuitem) cfile->changed = TRUE;
11870     reget_afilesize(mainw->current_file);
11871 
11872     if (!prefs->conserve_space) {
11873       set_undoable(_("Normalise audio"), TRUE);
11874       cfile->undo_action = UNDO_AUDIO_VOL;
11875     }
11876     if (menuitem) sensitize();
11877     end_threaded_dialog();
11878     d_print_done();
11879   }
11880 
11881 
11882   void on_append_audio_activate(LiVESMenuItem * menuitem, livespointer user_data) {
11883     LiVESWidget *chooser;
11884 
11885     char *filt[] = LIVES_AUDIO_LOAD_FILTER;
11886 
11887     char *com, *tmp, *tmp2;
11888     char *a_type;
11889 
11890     uint32_t chk_mask = WARN_MASK_LAYOUT_ALTER_AUDIO;
11891 
11892     boolean gotit = FALSE;
11893 
11894     int asigned = !(cfile->signed_endian & AFORM_UNSIGNED);
11895     int aendian = !(cfile->signed_endian & AFORM_BIG_ENDIAN);
11896 
11897     int resp;
11898 
11899     register int i;
11900 
11901     if (!CURRENT_CLIP_IS_VALID) return;
11902 
11903     if (!check_for_layout_errors(NULL, mainw->current_file, 1, 0, &chk_mask)) {
11904       return;
11905     }
11906 
11907     chooser = choose_file_with_preview((*mainw->audio_dir) ? mainw->audio_dir : NULL, _("Append Audio File"), filt,
11908                                        LIVES_FILE_SELECTION_AUDIO_ONLY);
11909 
11910     resp = lives_dialog_run(LIVES_DIALOG(chooser));
11911 
11912     end_fs_preview();
11913 
11914     if (resp != LIVES_RESPONSE_ACCEPT) {
11915       on_filechooser_cancel_clicked(chooser);
11916       unbuffer_lmap_errors(FALSE);
11917       return;
11918     }
11919 
11920     lives_snprintf(file_name, PATH_MAX, "%s",
11921                    (tmp = lives_filename_to_utf8((tmp2 = lives_file_chooser_get_filename(LIVES_FILE_CHOOSER(
11922                             chooser))),
11923                           -1, NULL, NULL, NULL)));
11924     lives_free(tmp);
11925     lives_free(tmp2);
11926 
11927     lives_widget_destroy(LIVES_WIDGET(chooser));
11928 
11929     lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
11930 
11931     lives_snprintf(mainw->audio_dir, PATH_MAX, "%s", file_name);
11932     get_dirname(mainw->audio_dir);
11933 
11934     a_type = get_extension(file_name);
11935 
11936     if (strlen(a_type)) {
11937       char *filt[] = LIVES_AUDIO_LOAD_FILTER;
11938       for (i = 0; filt[i]; i++) {
11939         if (!lives_ascii_strcasecmp(a_type, filt[i] + 2)) gotit = TRUE; // skip past "*." in filt
11940       }
11941     }
11942 
11943     if (gotit) {
11944       com = lives_strdup_printf("%s append_audio \"%s\" \"%s\" %d %d %d %d %d \"%s\"", prefs->backend, cfile->handle,
11945                                 a_type, cfile->arate,
11946                                 cfile->achans, cfile->asampsize, asigned, aendian,
11947                                 (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)));
11948       lives_free(tmp);
11949     } else {
11950       lives_free(a_type);
11951       do_audio_import_error();
11952       if (mainw->multitrack) {
11953         mt_sensitise(mainw->multitrack);
11954         maybe_add_mt_idlefunc();
11955       }
11956       unbuffer_lmap_errors(FALSE);
11957       return;
11958     }
11959 
11960     lives_free(a_type);
11961 
11962     lives_snprintf(mainw->msg, MAINW_MSG_SIZE, _("Appending audio file %s..."), file_name);
11963     d_print(""); // force switchtext
11964     d_print(mainw->msg);
11965 
11966     lives_rm(cfile->info_file);
11967     lives_system(com, FALSE);
11968     lives_free(com);
11969 
11970     if (THREADVAR(com_failed)) {
11971       if (mainw->multitrack) {
11972         mt_sensitise(mainw->multitrack);
11973         maybe_add_mt_idlefunc();
11974       }
11975       unbuffer_lmap_errors(FALSE);
11976       return;
11977     }
11978 
11979     if (!do_progress_dialog(TRUE, TRUE, _("Appending audio"))) {
11980       lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
11981 
11982       mainw->cancelled = CANCEL_NONE;
11983       mainw->error = FALSE;
11984       lives_rm(cfile->info_file);
11985       com = lives_strdup_printf("%s cancel_audio \"%s\"", prefs->backend, cfile->handle);
11986       lives_system(com, FALSE);
11987       if (!THREADVAR(com_failed)) {
11988         do_auto_dialog(_("Cancelling"), 0);
11989         check_backend_return(cfile);
11990       }
11991       lives_free(com);
11992       reget_afilesize(mainw->current_file);
11993       if (mainw->error) d_print_failed();
11994       if (mainw->multitrack) {
11995         mt_sensitise(mainw->multitrack);
11996         maybe_add_mt_idlefunc();
11997       }
11998       unbuffer_lmap_errors(FALSE);
11999       return;
12000     }
12001 
12002     if (mainw->error) {
12003       d_print_failed();
12004       widget_opts.non_modal = TRUE;
12005       do_error_dialog(mainw->msg);
12006       widget_opts.non_modal = FALSE;
12007     } else {
12008       lives_widget_queue_draw_and_update(LIVES_MAIN_WINDOW_WIDGET);
12009 
12010       com = lives_strdup_printf("%s commit_audio \"%s\"", prefs->backend, cfile->handle);
12011       mainw->cancelled = CANCEL_NONE;
12012       mainw->error = FALSE;
12013       lives_rm(cfile->info_file);
12014       lives_system(com, FALSE);
12015       lives_free(com);
12016 
12017       if (THREADVAR(com_failed)) {
12018         d_print_failed();
12019         if (mainw->multitrack) {
12020           mt_sensitise(mainw->multitrack);
12021           maybe_add_mt_idlefunc();
12022         }
12023         unbuffer_lmap_errors(FALSE);
12024         return;
12025       }
12026 
12027       do_auto_dialog(_("Committing audio"), 0);
12028       check_backend_return(cfile);
12029       if (mainw->error) {
12030         d_print_failed();
12031         widget_opts.non_modal = TRUE;
12032         if (mainw->cancelled != CANCEL_ERROR) do_error_dialog(mainw->msg);
12033         widget_opts.non_modal = FALSE;
12034       } else {
12035         get_dirname(file_name);
12036         lives_snprintf(mainw->audio_dir, PATH_MAX, "%s", file_name);
12037         reget_afilesize(mainw->current_file);
12038         cfile->changed = TRUE;
12039         d_print_done();
12040       }
12041     }
12042     cfile->undo_action = UNDO_APPEND_AUDIO;
12043     set_undoable(_("Append Audio"), !prefs->conserve_space);
12044 
12045     if (mainw->multitrack) {
12046       mt_sensitise(mainw->multitrack);
12047       maybe_add_mt_idlefunc();
12048     }
12049     if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
12050   }
12051 
12052 
12053   boolean on_trim_audio_activate(LiVESMenuItem * menuitem, livespointer user_data) {
12054     // type 0 == trim selected
12055     // type 1 == trim to play pointer
12056 
12057     char *com, *msg, *tmp;
12058 
12059     double start, end;
12060 
12061     uint32_t chk_mask = WARN_MASK_LAYOUT_DELETE_AUDIO | WARN_MASK_LAYOUT_ALTER_AUDIO;
12062 
12063     int type = LIVES_POINTER_TO_INT(user_data);
12064 
12065     if (!CURRENT_CLIP_IS_VALID) return FALSE;
12066 
12067     if (type == 0) {
12068       start = calc_time_from_frame(mainw->current_file, cfile->start);
12069       end = calc_time_from_frame(mainw->current_file, cfile->end + 1);
12070     } else {
12071       start = 0.;
12072       end = cfile->pointer_time;
12073     }
12074 
12075     tmp = (_("Deletion"));
12076     if (!check_for_layout_errors(tmp, mainw->current_file, calc_frame_from_time(mainw->current_file, start),
12077                                  calc_frame_from_time(mainw->current_file, end), &chk_mask)) {
12078       lives_free(tmp);
12079       return FALSE;
12080     }
12081     lives_free(tmp);
12082 
12083     if (end > cfile->laudio_time && end > cfile->raudio_time)
12084       msg = lives_strdup_printf(_("Padding audio to %.2f seconds..."), end);
12085     else
12086       msg = lives_strdup_printf(_("Trimming audio from %.2f to %.2f seconds..."), start, end);
12087 
12088     d_print(msg);
12089     lives_free(msg);
12090 
12091     com = lives_strdup_printf("%s trim_audio \"%s\" %.8f %.8f %d %d %d %d %d", prefs->backend, cfile->handle,
12092                               start, end, cfile->arate,
12093                               cfile->achans, cfile->asampsize, !(cfile->signed_endian & AFORM_UNSIGNED),
12094                               !(cfile->signed_endian & AFORM_BIG_ENDIAN));
12095     lives_rm(cfile->info_file);
12096     lives_system(com, FALSE);
12097     lives_free(com);
12098 
12099     if (THREADVAR(com_failed)) {
12100       unbuffer_lmap_errors(FALSE);
12101       d_print_failed();
12102       return FALSE;
12103     }
12104 
12105     do_progress_dialog(TRUE, FALSE, _("Trimming/Padding audio"));
12106 
12107     if (mainw->error) {
12108       d_print_failed();
12109       unbuffer_lmap_errors(FALSE);
12110       return FALSE;
12111     }
12112 
12113     if (!prefs->conserve_space) {
12114       set_undoable(_("Trim/Pad Audio"), !prefs->conserve_space);
12115       cfile->undo_action = UNDO_TRIM_AUDIO;
12116     }
12117 
12118     reget_afilesize(mainw->current_file);
12119     cfile->changed = TRUE;
12120     d_print_done();
12121 
12122     if (mainw->sl_undo_mem && cfile->stored_layout_audio != 0.) {
12123       // need to invalidate undo/redo stack, in case file was used in some layout undo
12124       stored_event_list_free_undos();
12125     }
12126 
12127     if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
12128     return TRUE;
12129   }
12130 
12131   void on_voladj_activate(LiVESMenuItem * menuitem, livespointer user_data) {create_new_pb_speed(3);}
12132 
12133   void on_fade_audio_activate(LiVESMenuItem * menuitem, livespointer user_data) {
12134     // type == 0 fade in
12135     // type == 1 fade out
12136 
12137     double startt, endt, startv, endv, time = 0.;
12138     char *msg, *msg2, *utxt, *com;
12139 
12140     uint32_t chk_mask = 0;
12141 
12142     lives_alarm_t alarm_handle;
12143     int type;
12144 
12145     aud_dialog_t *aud_d = NULL;
12146 
12147     if (!CURRENT_CLIP_IS_VALID) return;
12148 
12149     if (menuitem) {
12150       cfile->undo1_int = type = LIVES_POINTER_TO_INT(user_data);
12151       aud_d = create_audfade_dialog(type);
12152       if (lives_dialog_run(LIVES_DIALOG(aud_d->dialog)) == LIVES_RESPONSE_CANCEL) {
12153         lives_free(aud_d);
12154         return;
12155       }
12156 
12157       time = lives_spin_button_get_value(LIVES_SPIN_BUTTON(aud_d->time_spin));
12158 
12159       lives_widget_destroy(aud_d->dialog);
12160     } else {
12161       type = cfile->undo1_int;
12162     }
12163 
12164     if (!menuitem || !aud_d->is_sel) {
12165       if (!menuitem) {
12166         endt = cfile->undo1_dbl;
12167         startt = cfile->undo2_dbl;
12168       } else {
12169         if (type == 0) {
12170           cfile->undo2_dbl = startt = 0.;
12171           cfile->undo1_dbl = endt = time;
12172         } else {
12173           cfile->undo1_dbl = endt = cfile->laudio_time;
12174           cfile->undo2_dbl = startt = cfile->laudio_time - time;
12175         }
12176       }
12177     } else {
12178       cfile->undo2_dbl = startt = ((double)cfile->start - 1.) / cfile->fps;
12179       cfile->undo1_dbl = endt = (double)cfile->end / cfile->fps;
12180     }
12181 
12182     if (type == 0) {
12183       startv = 0.;
12184       endv = 1.;
12185       msg2 = (_("Fading audio in"));
12186       utxt = (_("Fade audio in"));
12187     } else {
12188       startv = 1.;
12189       endv = 0.;
12190       msg2 = (_("Fading audio out"));
12191       utxt = (_("Fade audio out"));
12192     }
12193 
12194     if (menuitem) {
12195       chk_mask = WARN_MASK_LAYOUT_ALTER_AUDIO;
12196       if (!check_for_layout_errors(NULL, mainw->current_file, 1, 0, &chk_mask)) {
12197         return;
12198       }
12199 
12200       if (!aud_d->is_sel)
12201         msg = lives_strdup_printf(_("%s over %.1f seconds..."), msg2, time);
12202       else
12203         msg = lives_strdup_printf(_("%s from time %.2f seconds to %.2f seconds..."), msg2, startt, endt);
12204       d_print(msg);
12205       lives_free(msg);
12206       lives_free(msg2);
12207     }
12208 
12209     desensitize();
12210     do_threaded_dialog(_("Fading audio..."), FALSE);
12211     alarm_handle = lives_alarm_set(LIVES_SHORTEST_TIMEOUT);
12212 
12213     threaded_dialog_spin(0.);
12214     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
12215 
12216     if (!prefs->conserve_space) {
12217       com = lives_strdup_printf("%s backup_audio \"%s\"", prefs->backend_sync, cfile->handle);
12218       lives_system(com, FALSE);
12219       lives_free(com);
12220 
12221       if (THREADVAR(com_failed)) {
12222         lives_alarm_clear(alarm_handle);
12223         end_threaded_dialog();
12224         d_print_failed();
12225         sensitize();
12226         unbuffer_lmap_errors(FALSE);
12227         return;
12228       }
12229     }
12230 
12231     aud_fade(mainw->current_file, startt, endt, startv, endv);
12232     audio_free_fnames();
12233 
12234     while (lives_alarm_check(alarm_handle) > 0) {
12235       lives_usleep(prefs->sleep_time);
12236     }
12237 
12238     lives_alarm_clear(alarm_handle);
12239 
12240     end_threaded_dialog();
12241     d_print_done();
12242 
12243     cfile->changed = TRUE;
12244     reget_afilesize(mainw->current_file);
12245 
12246     if (!prefs->conserve_space) {
12247       set_undoable(utxt, TRUE);
12248       cfile->undo_action = UNDO_FADE_AUDIO;
12249     }
12250     lives_free(utxt);
12251     sensitize();
12252 
12253     lives_freep((void **)&aud_d);
12254     if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
12255   }
12256 
12257 
12258   boolean on_del_audio_activate(LiVESMenuItem * menuitem, livespointer user_data) {
12259     double start, end;
12260     char *com, *tmp, *msg = NULL;
12261 
12262     uint32_t chk_mask = 0;
12263 
12264     boolean bad_header = FALSE;
12265 
12266     int i;
12267 
12268     if (!CURRENT_CLIP_IS_VALID) return FALSE;
12269 
12270     if (!menuitem) {
12271       // undo/redo
12272       start = cfile->undo1_dbl;
12273       end = cfile->undo2_dbl;
12274     } else {
12275       if (LIVES_POINTER_TO_INT(user_data)) {
12276         tmp = (_("Deleting all audio"));
12277         chk_mask = WARN_MASK_LAYOUT_DELETE_AUDIO | WARN_MASK_LAYOUT_ALTER_AUDIO;
12278         if (!check_for_layout_errors(tmp, mainw->current_file, 1, 0, &chk_mask)) {
12279           lives_free(tmp);
12280           return FALSE;
12281         }
12282         lives_free(tmp);
12283 
12284         if (!CURRENT_CLIP_HAS_VIDEO) {
12285           if (do_warning_dialog(_("\nDeleting all audio will close this file.\nAre you sure ?"))) close_current_file(0);
12286           unbuffer_lmap_errors(FALSE);
12287           return FALSE;
12288         }
12289         msg = (_("Deleting all audio..."));
12290         start = end = 0.;
12291       } else {
12292         start = calc_time_from_frame(mainw->current_file, cfile->start);
12293         end = calc_time_from_frame(mainw->current_file, cfile->end + 1);
12294         msg = lives_strdup_printf(_("Deleting audio from %.2f to %.2f seconds..."), start, end);
12295         start *= (double)cfile->arate / (double)cfile->arps;
12296         end *= (double)cfile->arate / (double)cfile->arps;
12297 
12298         tmp = (_("Deleting audio"));
12299         chk_mask = WARN_MASK_LAYOUT_DELETE_AUDIO | WARN_MASK_LAYOUT_ALTER_AUDIO;
12300         if (!check_for_layout_errors(tmp, mainw->current_file, 1, 0, &chk_mask)) {
12301           lives_free(tmp);
12302           return FALSE;
12303         }
12304       }
12305 
12306       cfile->undo1_dbl = start;
12307       cfile->undo2_dbl = end;
12308     }
12309 
12310     cfile->undo_arate = cfile->arate;
12311     cfile->undo_signed_endian = cfile->signed_endian;
12312     cfile->undo_achans = cfile->achans;
12313     cfile->undo_asampsize = cfile->asampsize;
12314     cfile->undo_arps = cfile->arps;
12315 
12316     if (msg) {
12317       d_print("");
12318       d_print(msg);
12319       lives_free(msg);
12320     }
12321 
12322     com = lives_strdup_printf("%s delete_audio \"%s\" %.8f %.8f %d %d %d", prefs->backend,
12323                               cfile->handle, start, end, cfile->arps,
12324                               cfile->achans, cfile->asampsize);
12325     lives_rm(cfile->info_file);
12326     lives_system(com, FALSE);
12327     lives_free(com);
12328 
12329     if (THREADVAR(com_failed)) {
12330       if (menuitem) d_print_failed();
12331       unbuffer_lmap_errors(FALSE);
12332       return FALSE;
12333     }
12334 
12335     do_progress_dialog(TRUE, FALSE, _("Deleting Audio"));
12336 
12337     if (mainw->error) {
12338       if (menuitem) d_print_failed();
12339       unbuffer_lmap_errors(FALSE);
12340       return FALSE;
12341     }
12342 
12343     set_undoable(_("Delete Audio"), TRUE);
12344     cfile->undo_action = UNDO_DELETE_AUDIO;
12345 
12346     reget_afilesize(mainw->current_file);
12347     cfile->changed = TRUE;
12348     sensitize();
12349 
12350     if (cfile->laudio_time == 0. || cfile->raudio_time == 0.) {
12351       if (cfile->audio_waveform) {
12352         for (i = 0; i < cfile->achans; lives_freep((void **)&cfile->audio_waveform[i++]));
12353         lives_freep((void **)&cfile->audio_waveform);
12354         lives_freep((void **)&cfile->aw_sizes);
12355       }
12356       if (cfile->laudio_time == cfile->raudio_time) cfile->achans = 0;
12357       else cfile->achans = 1;
12358       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ACHANS, &cfile->achans)) bad_header = TRUE;
12359 
12360       if (bad_header) do_header_write_error(mainw->current_file);
12361     }
12362 
12363     if (menuitem) {
12364       d_print_done();
12365     }
12366 
12367     if (mainw->sl_undo_mem && cfile->stored_layout_audio != 0.) {
12368       // need to invalidate undo/redo stack, in case file was used in some layout undo
12369       stored_event_list_free_undos();
12370     }
12371 
12372     if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
12373 
12374     return TRUE;
12375   }
12376 
12377 
12378   void on_rb_audrec_time_toggled(LiVESToggleButton * togglebutton, livespointer user_data) {
12379     _resaudw *resaudw = (_resaudw *)user_data;
12380     if (!resaudw) return;
12381     if (lives_toggle_button_get_active(togglebutton)) {
12382       lives_widget_set_sensitive(resaudw->hour_spinbutton, TRUE);
12383       lives_widget_set_sensitive(resaudw->minute_spinbutton, TRUE);
12384       lives_widget_set_sensitive(resaudw->second_spinbutton, TRUE);
12385     } else {
12386       lives_widget_set_sensitive(resaudw->hour_spinbutton, FALSE);
12387       lives_widget_set_sensitive(resaudw->minute_spinbutton, FALSE);
12388       lives_widget_set_sensitive(resaudw->second_spinbutton, FALSE);
12389     }
12390   }
12391 
12392 
12393   void on_recaudclip_activate(LiVESMenuItem * menuitem, livespointer user_data) {
12394     if (!is_realtime_aplayer(prefs->audio_player)) {
12395       do_nojack_rec_error();
12396       return;
12397     }
12398 
12399     mainw->fx1_val = DEFAULT_AUDIO_RATE;
12400     mainw->fx2_val = DEFAULT_AUDIO_CHANS;
12401     mainw->fx3_val = DEFAULT_AUDIO_SAMPS;
12402     mainw->fx4_val = mainw->endian;
12403     mainw->rec_end_time = -1.;
12404     resaudw = create_resaudw(5, NULL, NULL);
12405     lives_widget_show(resaudw->dialog);
12406   }
12407 
12408   static uint32_t lmap_error_recsel;
12409 
12410   void on_recaudsel_activate(LiVESMenuItem * menuitem, livespointer user_data) {
12411     uint32_t chk_mask = WARN_MASK_LAYOUT_ALTER_AUDIO;
12412 
12413     lmap_error_recsel = 0;
12414 
12415     if (!CURRENT_CLIP_IS_VALID) return;
12416     if (!is_realtime_aplayer(prefs->audio_player)) {
12417       do_nojack_rec_error();
12418       return;
12419     }
12420 
12421     if (!check_for_layout_errors(NULL, mainw->current_file, 1, 0, &chk_mask)) {
12422       return;
12423     }
12424 
12425     lmap_error_recsel = chk_mask;
12426 
12427     mainw->rec_end_time = (cfile->end - cfile->start + 1.) / cfile->fps;
12428 
12429     if (cfile->achans > 0) {
12430       mainw->fx1_val = cfile->arate;
12431       mainw->fx2_val = cfile->achans;
12432       mainw->fx3_val = cfile->asampsize;
12433       mainw->fx4_val = cfile->signed_endian;
12434       resaudw = create_resaudw(7, NULL, NULL);
12435     } else {
12436       mainw->fx1_val = DEFAULT_AUDIO_RATE;
12437       mainw->fx2_val = DEFAULT_AUDIO_CHANS;
12438       mainw->fx3_val = DEFAULT_AUDIO_SAMPS;
12439       mainw->fx4_val = mainw->endian;
12440       resaudw = create_resaudw(6, NULL, NULL);
12441     }
12442     lives_widget_show_all(resaudw->dialog);
12443   }
12444 
12445 
12446   void on_recaudclip_ok_clicked(LiVESButton * button, livespointer user_data) {
12447 #ifdef RT_AUDIO
12448     ticks_t ins_pt;
12449     double aud_start, aud_end, vel = 1., vol = 1.;
12450 
12451     uint32_t chk_mask;
12452 
12453     boolean backr = FALSE;
12454 
12455     int asigned = 1, aendian = 1;
12456     int old_file = mainw->current_file, new_file;
12457     int type = LIVES_POINTER_TO_INT(user_data);
12458     int oachans = 0, oarate = 0, oarps = 0, ose = 0, oasamps = 0;
12459 
12460     char *com;
12461 
12462     // type == 0 - new clip
12463     // type == 1 - existing clip
12464 
12465     if (type == 1) d_print(""); // show switch message, if appropriate
12466 
12467     mainw->current_file = mainw->first_free_file;
12468     if (!get_new_handle(mainw->current_file, NULL)) {
12469       mainw->current_file = old_file;
12470       return;
12471     }
12472 
12473     cfile->is_loaded = TRUE;
12474     cfile->img_type = IMG_TYPE_BEST; // override the pref
12475 
12476     cfile->arps = cfile->arate = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_arate)));
12477     cfile->achans = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_achans)));
12478     cfile->asampsize = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps)));
12479 
12480     mainw->rec_samples = -1;
12481     mainw->rec_end_time = -1.;
12482 
12483     if (type == 0) {
12484       if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->unlim_radiobutton))) {
12485         mainw->rec_end_time = (lives_spin_button_get_value(LIVES_SPIN_BUTTON(resaudw->hour_spinbutton)) * 60.
12486                                + lives_spin_button_get_value(LIVES_SPIN_BUTTON(resaudw->minute_spinbutton))) * 60.
12487                               + lives_spin_button_get_value(LIVES_SPIN_BUTTON(resaudw->second_spinbutton));
12488         mainw->rec_samples = mainw->rec_end_time * cfile->arate;
12489       }
12490     }
12491 
12492     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned))) {
12493       asigned = 0;
12494     }
12495     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend))) {
12496       aendian = 0;
12497     }
12498 
12499     mainw->is_processing = TRUE;
12500 
12501     cfile->signed_endian = get_signed_endian(asigned, aendian);
12502     lives_widget_destroy(resaudw->dialog);
12503     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
12504 
12505     lives_freep((void **)&resaudw);
12506 
12507     if (cfile->arate <= 0) {
12508       do_audrate_error_dialog();
12509       mainw->is_processing = FALSE;
12510       close_temp_handle(old_file);
12511       return;
12512     }
12513 
12514     if (mainw->rec_end_time == 0.) {
12515       widget_opts.non_modal = TRUE;
12516       do_error_dialog(_("\nRecord time must be greater than 0.\n"));
12517       widget_opts.non_modal = FALSE;
12518       mainw->is_processing = FALSE;
12519       close_temp_handle(old_file);
12520       return;
12521     }
12522 
12523     asigned = !asigned;
12524 
12525     if (type == 0) {
12526       lives_snprintf(cfile->type, 40, "Audio");
12527       add_to_clipmenu();
12528 
12529       lives_notify(LIVES_OSC_NOTIFY_CLIP_OPENED, "");
12530     }
12531 
12532     mainw->effects_paused = FALSE;
12533 
12534     if (type == 1) {
12535       oachans = mainw->files[old_file]->achans;
12536       oarate = mainw->files[old_file]->arate;
12537       oarps = mainw->files[old_file]->arps;
12538       oasamps = mainw->files[old_file]->asampsize;
12539       ose = mainw->files[old_file]->signed_endian;
12540 
12541       mainw->files[old_file]->arate = mainw->files[old_file]->arps = cfile->arate;
12542       mainw->files[old_file]->asampsize = cfile->asampsize;
12543       mainw->files[old_file]->achans = cfile->achans;
12544       mainw->files[old_file]->signed_endian = cfile->signed_endian;
12545     }
12546 
12547     mainw->suppress_dprint = TRUE;
12548     mainw->no_switch_dprint = TRUE;
12549 
12550 #ifdef ENABLE_JACK
12551     if (prefs->audio_player == AUD_PLAYER_JACK) {
12552       jack_rec_audio_to_clip(mainw->current_file, old_file, type == 0 ? RECA_NEW_CLIP : RECA_EXISTING);
12553     }
12554 #endif
12555 #ifdef HAVE_PULSE_AUDIO
12556     if (prefs->audio_player == AUD_PLAYER_PULSE) {
12557       pulse_rec_audio_to_clip(mainw->current_file, old_file, type == 0 ? RECA_NEW_CLIP : RECA_EXISTING);
12558     }
12559 #endif
12560 
12561     if (type == 1) {
12562       // set these again, as playsel may have reset them
12563       mainw->files[old_file]->arate = mainw->files[old_file]->arps = cfile->arate;
12564       mainw->files[old_file]->asampsize = cfile->asampsize;
12565       mainw->files[old_file]->achans = cfile->achans;
12566       mainw->files[old_file]->signed_endian = cfile->signed_endian;
12567     }
12568 
12569     if (type != 1 && mainw->cancelled == CANCEL_USER) {
12570       mainw->cancelled = CANCEL_NONE;
12571       if (type == 1) {
12572         mainw->files[old_file]->arps = oarps;
12573         mainw->files[old_file]->arate = oarate;
12574         mainw->files[old_file]->achans = oachans;
12575         mainw->files[old_file]->asampsize = oasamps;
12576         mainw->files[old_file]->signed_endian = ose;
12577       }
12578       mainw->is_processing = FALSE;
12579       close_temp_handle(old_file);
12580       mainw->suppress_dprint = FALSE;
12581       d_print_cancelled();
12582       mainw->no_switch_dprint = FALSE;
12583       return;
12584     }
12585 
12586     mainw->cancelled = CANCEL_NONE;
12587     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
12588 
12589     if (type == 1) {
12590       // set these again in case reget_afilesize() reset them
12591       cfile->arate = cfile->arps = mainw->files[old_file]->arate;
12592       cfile->asampsize = mainw->files[old_file]->asampsize;
12593       cfile->achans = mainw->files[old_file]->achans;
12594       cfile->signed_endian = mainw->files[old_file]->signed_endian;
12595 
12596       do_threaded_dialog(_("Committing audio"), FALSE);
12597       aud_start = 0.;
12598       reget_afilesize(mainw->current_file);
12599       aud_end = cfile->laudio_time;
12600 
12601       if (aud_end == 0.) {
12602         end_threaded_dialog();
12603         close_temp_handle(old_file);
12604         mainw->suppress_dprint = FALSE;
12605         d_print("nothing recorded...");
12606         d_print_failed();
12607         return;
12608       }
12609 
12610       ins_pt = (mainw->files[old_file]->start - 1.) / mainw->files[old_file]->fps * TICKS_PER_SECOND_DBL;
12611 
12612       if (!prefs->conserve_space && oachans > 0) {
12613         com = lives_strdup_printf("%s backup_audio \"%s\"", prefs->backend_sync, mainw->files[old_file]->handle);
12614         lives_system(com, FALSE);
12615         lives_free(com);
12616 
12617         if (THREADVAR(com_failed)) {
12618           end_threaded_dialog();
12619           close_temp_handle(old_file);
12620           mainw->suppress_dprint = FALSE;
12621           d_print_failed();
12622           return;
12623         }
12624       }
12625 
12626       THREADVAR(read_failed) = THREADVAR(write_failed) = FALSE;
12627       lives_freep((void **)&THREADVAR(read_failed_file));
12628 
12629       // insert audio from old (new) clip to current
12630       render_audio_segment(1, &(mainw->current_file), old_file, &vel, &aud_start, ins_pt,
12631                            ins_pt + (ticks_t)((aud_end - aud_start)*TICKS_PER_SECOND_DBL), &vol, vol, vol, NULL);
12632 
12633       end_threaded_dialog();
12634       close_current_file(old_file);
12635 
12636       if (THREADVAR(write_failed)) {
12637         // on failure
12638         int outfile = (mainw->multitrack ? mainw->multitrack->render_file : mainw->current_file);
12639         char *outfilename = lives_get_audio_file_name(outfile);
12640         do_write_failed_error_s(outfilename, NULL);
12641         lives_free(outfilename);
12642 
12643         if (!prefs->conserve_space && type == 1) {
12644           // try to recover backup
12645           com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, mainw->files[old_file]->handle);
12646           lives_system(com, FALSE);
12647           lives_free(com);
12648           backr = TRUE;
12649         }
12650       }
12651 
12652       if (THREADVAR(read_failed)) {
12653         do_read_failed_error_s(THREADVAR(read_failed_file), NULL);
12654         lives_freep((void **)&THREADVAR(read_failed_file));
12655         if (!prefs->conserve_space && type == 1 && !backr) {
12656           // try to recover backup
12657           com = lives_strdup_printf("%s undo_audio \"%s\"", prefs->backend_sync, mainw->files[old_file]->handle);
12658           lives_system(com, FALSE);
12659           lives_free(com);
12660         }
12661       }
12662     }
12663 
12664     mainw->suppress_dprint = FALSE;
12665     cfile->changed = TRUE;
12666     save_clip_values(mainw->current_file);
12667 
12668     mainw->cancelled = CANCEL_NONE;
12669 
12670     new_file = mainw->current_file;
12671     if (type == 0) {
12672       if (!mainw->multitrack) {
12673         switch_to_file((mainw->current_file = 0), new_file);
12674       }
12675     } else {
12676       if (!prefs->conserve_space) {
12677         set_undoable(_("Record new audio"), TRUE);
12678         cfile->undo_action = UNDO_REC_AUDIO;
12679       }
12680     }
12681 
12682     d_print_done();
12683     mainw->no_switch_dprint = FALSE;
12684 
12685     chk_mask = lmap_error_recsel;
12686     if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
12687     lmap_error_recsel = 0;
12688     mainw->is_processing = FALSE;
12689 
12690 #endif
12691   }
12692 
12693 
12694   boolean on_ins_silence_activate(LiVESMenuItem * menuitem, livespointer user_data) {
12695     double start = 0, end = 0;
12696     char *com;
12697 
12698     uint32_t chk_mask = 0;
12699 
12700     boolean has_new_audio = FALSE;
12701 
12702     int i;
12703 
12704     if (!CURRENT_CLIP_IS_VALID) return FALSE;
12705 
12706     if (!CURRENT_CLIP_HAS_AUDIO) {
12707       has_new_audio = TRUE;
12708     }
12709 
12710     if (!menuitem) {
12711       // redo
12712       if (cfile->achans != cfile->undo_achans) {
12713         if (cfile->audio_waveform) {
12714           for (i = 0; i < cfile->achans; lives_freep((void **)&cfile->audio_waveform[i++]));
12715           lives_freep((void **)&cfile->audio_waveform);
12716           lives_freep((void **)&cfile->aw_sizes);
12717         }
12718       }
12719       start = cfile->undo1_dbl;
12720       end = cfile->undo2_dbl;
12721       cfile->arate = cfile->undo_arate;
12722       cfile->signed_endian = cfile->undo_signed_endian;
12723       cfile->achans = cfile->undo_achans;
12724       cfile->asampsize = cfile->undo_asampsize;
12725       cfile->arps = cfile->undo_arps;
12726     }
12727 
12728     if (!cfile->achans) {
12729       mainw->fx1_val = DEFAULT_AUDIO_RATE;
12730       mainw->fx2_val = DEFAULT_AUDIO_CHANS;
12731       mainw->fx3_val = DEFAULT_AUDIO_SAMPS;
12732       mainw->fx4_val = mainw->endian;
12733       resaudw = create_resaudw(2, NULL, NULL);
12734       if (lives_dialog_run(LIVES_DIALOG(resaudw->dialog)) != LIVES_RESPONSE_OK) return FALSE;
12735       if (mainw->error) {
12736         mainw->error = FALSE;
12737         return FALSE;
12738       }
12739 
12740       cfile->undo_arate = cfile->arate;
12741       cfile->undo_signed_endian = cfile->signed_endian;
12742       cfile->undo_achans = cfile->achans;
12743       cfile->undo_asampsize = cfile->asampsize;
12744     }
12745 
12746     if (menuitem) {
12747       char *tmp = (_("Inserting silence"));
12748       chk_mask = WARN_MASK_LAYOUT_SHIFT_AUDIO |  WARN_MASK_LAYOUT_ALTER_AUDIO;
12749 
12750       start = calc_time_from_frame(mainw->current_file, cfile->start);
12751       end = calc_time_from_frame(mainw->current_file, cfile->end + 1);
12752 
12753       if (!check_for_layout_errors(tmp, mainw->current_file, cfile->start, cfile->end, &chk_mask)) {
12754         lives_free(tmp);
12755         return FALSE;
12756       }
12757       lives_free(tmp);
12758 
12759       d_print(""); // force switchtext
12760       d_print(_("Inserting silence from %.2f to %.2f seconds..."), start, end);
12761     }
12762 
12763     cfile->undo1_dbl = start;
12764     start *= (double)cfile->arate / (double)cfile->arps;
12765     cfile->undo2_dbl = end;
12766     end *= (double)cfile->arate / (double)cfile->arps;
12767 
12768     // store values for undo
12769     cfile->old_laudio_time = cfile->laudio_time;
12770     cfile->old_raudio_time = cfile->raudio_time;
12771 
12772     // with_sound is 2 (audio only), therefore start, end, where, are in seconds. rate is -ve to indicate silence
12773     com = lives_strdup_printf("%s insert \"%s\" \"%s\" %.8f 0. %.8f \"%s\" 2 0 0 0 0 %d %d %d %d %d 1",
12774                               prefs->backend, cfile->handle,
12775                               get_image_ext_for_type(cfile->img_type), start, end - start, cfile->handle, -cfile->arps,
12776                               cfile->achans, cfile->asampsize, !(cfile->signed_endian & AFORM_UNSIGNED),
12777                               !(cfile->signed_endian & AFORM_BIG_ENDIAN));
12778 
12779     lives_rm(cfile->info_file);
12780     lives_system(com, FALSE);
12781     lives_free(com);
12782 
12783     if (THREADVAR(com_failed)) {
12784       d_print_failed();
12785       if (has_new_audio) cfile->achans = cfile->arate = cfile->asampsize = cfile->arps = 0;
12786       unbuffer_lmap_errors(FALSE);
12787       return FALSE;
12788     }
12789 
12790     do_progress_dialog(TRUE, FALSE, _("Inserting Silence"));
12791 
12792     if (mainw->error) {
12793       d_print_failed();
12794       if (has_new_audio) cfile->achans = cfile->arate = cfile->asampsize = cfile->arps = 0;
12795       unbuffer_lmap_errors(FALSE);
12796       return FALSE;
12797     }
12798 
12799     if (has_new_audio) {
12800       cfile->arate = cfile->arps = cfile->undo_arate;
12801       cfile->signed_endian = cfile->undo_signed_endian;
12802       cfile->achans = cfile->undo_achans;
12803       cfile->asampsize = cfile->undo_asampsize;
12804     }
12805 
12806     set_undoable(_("Insert Silence"), TRUE);
12807     cfile->undo_action = UNDO_INSERT_SILENCE;
12808 
12809     reget_afilesize(mainw->current_file);
12810     cfile->changed = TRUE;
12811 
12812     save_clip_values(mainw->current_file);
12813 
12814     if (menuitem) {
12815       sensitize();
12816       d_print_done();
12817     }
12818 
12819     if (mainw->sl_undo_mem && cfile->stored_layout_audio != 0.) {
12820       // need to invalidate undo/redo stack, in case file was used in some layout undo
12821       stored_event_list_free_undos();
12822     }
12823 
12824     if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
12825 
12826     return TRUE;
12827   }
12828 
12829 
12830   void on_ins_silence_details_clicked(LiVESButton * button, livespointer user_data) {
12831     int asigned = 1, aendian = 1;
12832     boolean bad_header = FALSE;
12833 
12834     cfile->arps = cfile->arate = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_arate)));
12835     cfile->achans = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_achans)));
12836     cfile->asampsize = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps)));
12837     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned))) {
12838       asigned = 0;
12839     }
12840     if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend))) {
12841       aendian = 0;
12842     }
12843     cfile->signed_endian = get_signed_endian(asigned, aendian);
12844     lives_widget_destroy(resaudw->dialog);
12845     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
12846 
12847     lives_freep((void **)&resaudw);
12848     if (cfile->arate <= 0) {
12849       do_audrate_error_dialog();
12850       cfile->achans = cfile->arate = cfile->arps = cfile->asampsize = 0;
12851       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ARATE, &cfile->arps)) bad_header = TRUE;
12852       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_ARATE, &cfile->arate)) bad_header = TRUE;
12853       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ACHANS, &cfile->achans)) bad_header = TRUE;
12854       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_ASAMPS, &cfile->asampsize)) bad_header = TRUE;
12855 
12856       if (bad_header) do_header_write_error(mainw->current_file);
12857       mainw->error = TRUE;
12858       return;
12859     }
12860     mainw->error = FALSE;
12861   }
12862 
12863 
12864   void on_lerrors_clear_clicked(LiVESButton * button, livespointer user_data) {
12865     boolean close = LIVES_POINTER_TO_INT(user_data);
12866     mainw->mt_needs_idlefunc = FALSE;
12867 
12868     if (mainw->multitrack) {
12869       if (mainw->multitrack->idlefunc > 0) {
12870         lives_source_remove(mainw->multitrack->idlefunc);
12871         mainw->multitrack->idlefunc = 0;
12872         mainw->mt_needs_idlefunc = TRUE;
12873       }
12874       mt_desensitise(mainw->multitrack);
12875     }
12876 
12877     clear_lmap_errors();
12878     save_layout_map(NULL, NULL, NULL, NULL);
12879     if (close) {
12880       boolean needs_idlefunc = mainw->mt_needs_idlefunc;
12881       mainw->mt_needs_idlefunc = FALSE;
12882       lives_general_button_clicked(button, textwindow);
12883       mainw->mt_needs_idlefunc = needs_idlefunc;
12884     } else {
12885       lives_widget_queue_draw(lives_widget_get_toplevel(LIVES_WIDGET(button)));
12886       lives_widget_set_sensitive(textwindow->clear_button, FALSE);
12887       lives_widget_set_sensitive(textwindow->delete_button, FALSE);
12888 
12889       if (mainw->multitrack) {
12890         mt_sensitise(mainw->multitrack);
12891         maybe_add_mt_idlefunc();
12892       }
12893     }
12894   }
12895 
12896 
12897   void on_lerrors_delete_clicked(LiVESButton * button, livespointer user_data) {
12898     int num_maps = lives_list_length(mainw->affected_layouts_map);
12899     char *msg = lives_strdup_printf(P_("\nDelete %d layout...are you sure ?\n", "\nDelete %d layouts...are you sure ?\n", num_maps),
12900                                     num_maps);
12901     mainw->mt_needs_idlefunc = FALSE;
12902 
12903     if (mainw->multitrack) {
12904       if (mainw->multitrack->idlefunc > 0) {
12905         lives_source_remove(mainw->multitrack->idlefunc);
12906         mainw->multitrack->idlefunc = 0;
12907         mainw->mt_needs_idlefunc = TRUE;
12908       }
12909       mt_desensitise(mainw->multitrack);
12910     }
12911 
12912     if (!do_warning_dialog(msg)) {
12913       lives_free(msg);
12914       if (mainw->multitrack) {
12915         mt_sensitise(mainw->multitrack);
12916         maybe_add_mt_idlefunc();
12917       }
12918       return;
12919     }
12920 
12921     lives_free(msg);
12922     remove_layout_files(mainw->affected_layouts_map);
12923     on_lerrors_clear_clicked(button, LIVES_INT_TO_POINTER(TRUE));
12924   }
12925