b7af252fc68f0b9b7b00690d3427abfd92f4858f
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / mwindow.C
1 /*
2  * CINELERRA
3  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include "aboutprefs.h"
22 #include "asset.h"
23 #include "assets.h"
24 #include "atrack.h"
25 #include "audioalsa.h"
26 #include "autos.h"
27 #include "awindowgui.h"
28 #include "awindow.h"
29 #include "batchrender.h"
30 #include "bccmodels.h"
31 #include "bcdisplayinfo.h"
32 #include "bcprogressbox.h"
33 #include "bcsignals.h"
34 #include "bctimer.h"
35 #include "bctrace.h"
36 #include "bdcreate.h"
37 #include "brender.h"
38 #include "cache.h"
39 #include "channel.h"
40 #include "channeldb.h"
41 #include "channelinfo.h"
42 #include "clip.h"
43 #include "clipedls.h"
44 #include "commercials.h"
45 #include "confirmsave.h"
46 #include "convert.h"
47 #include "cplayback.h"
48 #include "ctimebar.h"
49 #include "cwindowgui.h"
50 #include "cwindow.h"
51 #include "cwindowtool.h"
52 #include "bchash.h"
53 #include "devicedvbinput.inc"
54 #include "dvdcreate.h"
55 #include "editpanel.h"
56 #include "edl.h"
57 #include "edlsession.h"
58 #include "errorbox.h"
59 #include "fileformat.h"
60 #include "file.h"
61 #include "filesystem.h"
62 #include "filexml.h"
63 #include "floatautos.h"
64 #include "framecache.h"
65 #include "gwindow.h"
66 #include "gwindowgui.h"
67 #include "keyframegui.h"
68 #include "indexfile.h"
69 #include "intautos.h"
70 #include "interlacemodes.h"
71 #include "language.h"
72 #include "levelwindowgui.h"
73 #include "levelwindow.h"
74 #include "loadfile.inc"
75 #include "localsession.h"
76 #include "maincursor.h"
77 #include "mainerror.h"
78 #include "mainindexes.h"
79 #include "mainmenu.h"
80 #include "mainprogress.h"
81 #include "mainsession.h"
82 #include "mainundo.h"
83 #include "mbuttons.h"
84 #include "mixersalign.h"
85 #include "mutex.h"
86 #include "mwindowgui.h"
87 #include "mwindow.h"
88 #include "new.h"
89 #include "panautos.h"
90 #include "patchbay.h"
91 #include "playback3d.h"
92 #include "playbackengine.h"
93 #include "plugin.h"
94 #include "pluginserver.h"
95 #include "pluginset.h"
96 #include "preferences.h"
97 #include "proxy.h"
98 #include "record.h"
99 #include "recordmonitor.h"
100 #include "recordlabel.h"
101 #include "removefile.h"
102 #include "render.h"
103 #include "resourcethread.h"
104 #include "savefile.inc"
105 #include "samplescroll.h"
106 #include "sha1.h"
107 #include "shuttle.h"
108 #include "sighandler.h"
109 #include "splashgui.h"
110 #include "statusbar.h"
111 #include "theme.h"
112 #include "threadloader.h"
113 #include "timebar.h"
114 #include "timelinepane.h"
115 #include "tipwindow.h"
116 #include "trackcanvas.h"
117 #include "track.h"
118 #include "tracking.h"
119 #include "trackscroll.h"
120 #include "tracks.h"
121 #include "transition.h"
122 #include "transportque.h"
123 #include "vframe.h"
124 #include "vtrack.h"
125 #include "versioninfo.h"
126 #include "vicon.h"
127 #include "videodevice.inc"
128 #include "videowindow.h"
129 #include "vplayback.h"
130 #include "vwindowgui.h"
131 #include "vwindow.h"
132 #include "wavecache.h"
133 #include "wintv.h"
134 #include "wwindow.h"
135 #include "x10tv.h"
136 #include "zoombar.h"
137 #include "zwindow.h"
138 #include "zwindowgui.h"
139 #include "exportedl.h"
140
141 #include "defaultformats.h"
142 #include "ntsczones.h"
143
144 #include <stdint.h>
145 #include <stdlib.h>
146 #include <unistd.h>
147 #include <fcntl.h>
148 #include <string.h>
149 #include <sys/types.h>
150 #include <sys/stat.h>
151 #include <sys/time.h>
152 #include <sys/mman.h>
153 #include <sys/file.h>
154 #include <limits.h>
155 #include <errno.h>
156
157
158 extern "C"
159 {
160
161
162
163
164 // Hack for libdv to remove glib dependancy
165
166 // void
167 // g_log (const char    *log_domain,
168 //        int  log_level,
169 //        const char    *format,
170 //        ...)
171 // {
172 // }
173 //
174 // void
175 // g_logv (const char    *log_domain,
176 //        int  log_level,
177 //        const char    *format,
178 //        ...)
179 // {
180 // }
181 //
182
183 }
184
185 extern long cin_timezone;
186
187 ArrayList<PluginServer*>* MWindow::plugindb = 0;
188 Commercials* MWindow::commercials = 0;
189
190
191 MWindow::MWindow()
192  : Thread(1, 0, 0)
193 {
194         run_lock = new Mutex("MWindow::run_lock");
195         plugin_gui_lock = new Mutex("MWindow::plugin_gui_lock");
196         dead_plugin_lock = new Mutex("MWindow::dead_plugin_lock");
197         vwindows_lock = new Mutex("MWindow::vwindows_lock");
198         zwindows_lock = new Mutex("MWindow::zwindows_lock");
199         brender_lock = new Mutex("MWindow::brender_lock");
200         keyframe_gui_lock = new Mutex("MWindow::keyframe_gui_lock");
201
202         playback_3d = 0;
203         splash_window = 0;
204         undo = 0;
205         undo_command = COMMAND_NONE;
206         defaults = 0;
207         assets = 0;
208         //commercials = 0;
209         commercial_active = 0;
210         audio_cache = 0;
211         video_cache = 0;
212         frame_cache = 0;
213         wave_cache = 0;
214         preferences = 0;
215         session = 0;
216         theme = 0;
217         mainindexes = 0;
218         mainprogress = 0;
219         brender = 0;
220         brender_active = 0;
221         strcpy(cin_lang,"en");
222         channeldb_buz =  new ChannelDB;
223         channeldb_v4l2jpeg =  new ChannelDB;
224         plugin_guis = 0;
225         dead_plugins = 0;
226         keyframe_threads = 0;
227         create_bd = 0;
228         create_dvd = 0;
229         batch_render = 0;
230         convert_render = 0;
231         render = 0;
232         edl = 0;
233         gui = 0;
234         cwindow = 0;
235         awindow = 0;
236         gwindow = 0;
237         twindow = 0;
238         wwindow = 0;
239         lwindow = 0;
240         sighandler = 0;
241         restart_status = 0;
242         screens = 1;
243         appimageDir = getenv("APPDIR"); //NULL if not running as appimage
244         in_destructor = 0;
245         speed_edl = 0;
246         beeper = 0;
247         redraw_tracks = 0;
248         shuttle = 0;
249         wintv = 0;
250         x10tv = 0;
251         mixers_align = 0;
252 }
253
254
255 // Need to delete brender temporary here.
256 MWindow::~MWindow()
257 {
258         run_lock->lock("MWindow::~MWindow");
259         in_destructor = 1;
260 //printf("MWindow::~MWindow %d\n", __LINE__);
261         if( wwindow && wwindow->is_running() )
262                 wwindow->close_window();
263         if( twindow && twindow->is_running() )
264                 twindow->close_window();
265         gui->remote_control->deactivate();
266         gui->record->stop();
267 #ifdef HAVE_DVB
268         gui->channel_info->stop();
269 #endif
270         delete beeper;          beeper = 0;
271         delete redraw_tracks;   redraw_tracks = 0;
272         delete create_bd;       create_bd = 0;
273         delete create_dvd;      create_dvd = 0;
274 #ifdef HAVE_SHUTTLE
275         delete shuttle;         shuttle = 0;
276 #endif
277 #ifdef HAVE_WINTV
278         delete wintv;           wintv = 0;
279 #endif
280 #ifdef HAVE_X10TV
281         delete x10tv;           x10tv = 0;
282 #endif
283         delete batch_render;    batch_render = 0;
284         delete convert_render;  convert_render = 0;
285         delete render;          render = 0;
286         delete mixers_align;    mixers_align = 0;
287         commit_commercial();
288         if( commercials && !commercials->remove_user() ) commercials = 0;
289         close_mixers();
290         if( speed_edl ) { speed_edl->remove_user();  speed_edl = 0; }
291 // Save defaults for open plugins
292         plugin_gui_lock->lock("MWindow::~MWindow");
293         for(int i = 0; i < plugin_guis->size(); i++) {
294                 plugin_guis->get(i)->hide_gui();
295                 delete_plugin(plugin_guis->get(i));
296         }
297         plugin_gui_lock->unlock();
298         hide_keyframe_guis();
299         clean_indexes();
300         clean_backups();
301         save_defaults();
302 // Give up and go to a movie
303 //  cant run valgrind if this is used
304
305         gui->del_keyboard_listener(
306                 (int (BC_WindowBase::*)(BC_WindowBase *))
307                 &MWindowGUI::keyboard_listener);
308         reset_caches(0);
309 #if 0
310 // release the hounds
311         if( awindow && awindow->gui ) awindow->gui->close(0);
312         if( cwindow && cwindow->gui ) cwindow->gui->close(0);
313         if( lwindow && lwindow->gui ) lwindow->gui->close(0);
314         if( gwindow && gwindow->gui ) gwindow->gui->close(0);
315         vwindows.remove_all_objects();
316         zwindows.remove_all_objects();
317         gui->close(0);
318         if( awindow ) awindow->join();
319         if( cwindow ) cwindow->join();
320         if( lwindow ) lwindow->join();
321         if( gwindow ) gwindow->join();
322         join();
323 #else
324 // one at a time, or nouveau chokes
325 #define close_gui(win) if( win ) { \
326   if( win->gui ) win->gui->close(0); \
327   win->join(); }
328         close_gui(awindow);
329         close_gui(cwindow);
330         close_gui(lwindow);
331         close_gui(gwindow);
332         vwindows.remove_all_objects();
333         zwindows.remove_all_objects();
334         gui->close(0);
335         join();
336 #endif
337         dead_plugins->remove_all_objects();
338 // must delete theme before destroying plugindb
339 //  theme destructor will be deleted by delete_plugins
340         delete theme;           theme = 0;
341         delete_plugins();
342         finit_error();
343         keyframe_threads->remove_all_objects();
344         colormodels.remove_all_objects();
345         delete awindow;         awindow = 0;
346         delete lwindow;         lwindow = 0;
347         delete twindow;         twindow = 0;
348         delete wwindow;         wwindow = 0;
349         delete gwindow;         gwindow = 0;
350         delete cwindow;         cwindow = 0;
351         delete gui;             gui = 0;
352         delete mainindexes;     mainindexes = 0;
353         delete mainprogress;    mainprogress = 0;
354  // delete the caches after the assets
355         if( audio_cache ) { audio_cache->remove_user();  audio_cache = 0; }
356         if( video_cache ) { video_cache->remove_user();  video_cache = 0; }
357         delete frame_cache;     frame_cache = 0;
358         delete wave_cache;      wave_cache = 0;
359         delete plugin_guis;     plugin_guis = 0;
360         delete dead_plugins;    dead_plugins = 0;
361         delete keyframe_threads;  keyframe_threads = 0;
362         delete undo;            undo = 0;
363         delete preferences;     preferences = 0;
364         delete exportedl;       exportedl = 0;
365         delete session;         session = 0;
366         delete defaults;        defaults = 0;
367         delete assets;          assets = 0;
368         delete splash_window;   splash_window = 0;
369         if( !edl->Garbage::remove_user() ) edl = 0;
370         delete channeldb_buz;
371         delete channeldb_v4l2jpeg;
372 // This must be last thread to exit
373         delete playback_3d;     playback_3d = 0;
374         delete dead_plugin_lock;
375         delete plugin_gui_lock;
376         delete vwindows_lock;
377         delete zwindows_lock;
378         delete brender_lock;
379         delete keyframe_gui_lock;
380         colormodels.remove_all_objects();
381         interlace_project_modes.remove_all_objects();
382         interlace_asset_modes.remove_all_objects();
383         sighandler->terminate();
384         delete sighandler;
385         delete run_lock;
386 }
387
388
389 void MWindow::quit()
390 {
391         gui->set_done(0);
392 }
393
394 void MWindow::init_error()
395 {
396         MainError::init_error(this);
397 }
398
399 void MWindow::finit_error()
400 {
401         MainError::finit_error();
402 }
403
404 void MWindow::create_defaults_path(char *string, const char *config_file)
405 {
406 // set the .bcast path
407         FileSystem fs;
408
409         sprintf(string, "%s/", File::get_config_path());
410         fs.complete_path(string);
411         if(!fs.is_dir(string))
412                 fs.create_dir(string);
413
414 // load the defaults
415         strcat(string, config_file);
416 }
417 const char *MWindow::default_std()
418 {
419         char buf[BCTEXTLEN], *p = 0;
420
421         int fd = open(TIMEZONE_NAME, O_RDONLY);
422         if( fd >= 0 ) {
423                 int l = read(fd, buf, sizeof(buf)-1);
424                 close(fd);
425                 if( l > 0 ) {
426                         if( buf[l-1] == '\n' ) --l;
427                         buf[l] = 0;
428                         p = buf;
429                 }
430         }
431         if( !p ) {
432                 int l = readlink(LOCALTIME_LINK, buf, sizeof(buf)-1);
433                 if( l > 0 ) {
434                         buf[l] = 0;
435                         if( !(p=strstr(buf, ZONEINFO_STR)) != 0 ) return "PAL";
436                         p += strlen(ZONEINFO_STR);
437                 }
438         }
439
440         if( p ) {
441                 for( int i=0; ntsc_zones[i]; ++i ) {
442                         if( !strcmp(ntsc_zones[i], p) )
443                                 return "NTSC";
444                 }
445         }
446
447         p = getenv("TZ");
448         if( p ) {
449                 for( int i=0; ntsc_zones[i]; ++i ) {
450                         if( !strcmp(ntsc_zones[i], p) )
451                                 return "NTSC";
452                 }
453         }
454
455 // cin_timezone: Seconds west of UTC.  240sec/deg
456         double tz_deg =  -cin_timezone / 240.;
457 // from Honolulu = -10, to New York = -5, 15deg/hr   lat -150..-75
458         return tz_deg >= -10*15 && tz_deg <= -5*15 ? "NTSC" : "PAL";
459 }
460
461 void MWindow::fill_preset_defaults(const char *preset, EDLSession *session)
462 {
463         struct formatpresets *fpr;
464
465         for(fpr = &format_presets[0]; fpr->name; fpr++)
466         {
467                 if(strcmp(_(fpr->name), preset) == 0)
468                 {
469                         session->audio_channels = fpr->audio_channels;
470                         session->audio_tracks = fpr->audio_tracks;
471                         session->sample_rate = fpr->sample_rate;
472                         session->video_channels = fpr->video_channels;
473                         session->video_tracks = fpr->video_tracks;
474                         session->frame_rate = fpr->frame_rate;
475                         session->output_w = fpr->output_w;
476                         session->output_h = fpr->output_h;
477                         session->aspect_w = fpr->aspect_w;
478                         session->aspect_h = fpr->aspect_h;
479                         session->interlace_mode = fpr->interlace_mode;
480                         session->color_model = fpr->color_model;
481                         return;
482                 }
483         }
484 }
485
486 const char *MWindow::get_preset_name(int index)
487 {
488         if(index < 0 || index >= (int)MAX_NUM_PRESETS)
489                 return "Error";
490         return _(format_presets[index].name);
491 }
492
493
494 void MWindow::init_defaults(BC_Hash* &defaults, char *config_path)
495 {
496         char path[BCTEXTLEN];
497 // Use user supplied path
498         if(config_path[0])
499                 strcpy(path, config_path);
500         else
501                 create_defaults_path(path, CONFIG_FILE);
502
503         delete defaults;
504         defaults = new BC_Hash(path);
505         defaults->load();
506 }
507
508
509 void MWindow::check_language()
510 {
511         char pref_locale[BCSTRLEN];
512         strcpy(pref_locale, DEFAULT_LOCALE);
513         defaults->get("LOCALE",pref_locale);
514 // set LANGUAGE if pref locale != sys
515         if( strcmp(pref_locale, DEFAULT_LOCALE) )
516                 setenv("LANGUAGE", pref_locale, 1);
517
518         char curr_lang[BCTEXTLEN]; curr_lang[0] = 0;
519         const char *env_lang = getenv("LANGUAGE");
520         if( !env_lang ) env_lang = getenv("LC_ALL");
521         if( !env_lang ) env_lang = getenv("LANG");
522         if( !env_lang ) {
523                 snprintf(curr_lang, sizeof(curr_lang), "%s_%s-%s",
524                         BC_Resources::language, BC_Resources::region, BC_Resources::encoding);
525                 env_lang = curr_lang;
526         }
527         char last_lang[BCTEXTLEN]; last_lang[0] = 0;
528         defaults->get("LAST_LANG",last_lang);
529         if( strcmp(env_lang,last_lang)) {
530                 printf("lang changed from '%s' to '%s'\n", last_lang, env_lang);
531                 defaults->update("LAST_LANG",env_lang);
532                 char plugin_path[BCTEXTLEN];
533                 create_defaults_path(plugin_path, PLUGIN_FILE);
534                 ::remove(plugin_path);
535                 char ladspa_path[BCTEXTLEN];
536                 create_defaults_path(ladspa_path, LADSPA_FILE);
537                 ::remove(ladspa_path);
538                 defaults->save();
539         }
540         if( strlen(env_lang) > 1 &&
541             ( env_lang[2] == 0 || env_lang[2] == '_'  || env_lang[2] == '.' ) ) {
542                 cin_lang[0] = env_lang[0];  cin_lang[1] = env_lang[1];  cin_lang[2] = 0;
543         }
544         else
545                 strcpy(cin_lang, "en");
546 }
547
548 void MWindow::get_plugin_path(char *path, const char *plug_dir, const char *fs_path)
549 {
550         char *base_path = FileSystem::basepath(fs_path), *bp = base_path;
551         if( plug_dir ) {
552                 const char *dp = plug_dir;
553                 while( *bp && *dp && *bp == *dp ) { ++bp; ++dp; }
554                 bp = !*dp && *bp == '/' ? bp+1 : base_path;
555         }
556         strcpy(path, bp);
557         delete [] base_path;
558 }
559
560 /**
561 * @brief Load plugins according to an index file.
562
563 * @details Builds an ArrayList of plugin servers only if there is no
564 * mismatch for file layout version, index identifier, or executable
565 * timestamp mismatch for the built-in plugins. If OK, add the plugin
566 * servers to the global list.
567
568 * @note If an error is returned the index file needs to be rebuilt, and
569 * then this function must be called again.
570 * There are two types of index files, with the same layout internally.
571 * One called "Cinelerra_plugins" for built-ins, ffmpeg and lv2 .
572 * The other type "ladspa_plugins.index_id" where index_id is either the
573 * path or the $CIN_BUILD identifier if the path is from the running
574 * AppImage itself. If there are multiple ladspa directories in the
575 * path, there will be multiple index files.
576
577 * @return  -1 if file no open, 0 if OK, 1 if error. 
578 */
579 int MWindow::load_plugin_index(MWindow *mwindow, FILE *fp, const char *plugin_dir, const char *index_id)
580 {
581         if( !fp ) return -1;
582         struct stat st;
583         fstat (fileno(fp), &st);        // don't bother if the file has just been created.
584         if( st.st_size < 4 ) return 1;
585         
586 // load index
587         fseek(fp, 0, SEEK_SET);
588         int ret = 0;
589         char index_line[BCTEXTLEN];
590         int index_version = -1, len = strlen(index_id);
591         if( !fgets(index_line, BCTEXTLEN, fp) ||
592             sscanf(index_line, "%d", &index_version) != 1 ||
593             index_version != PLUGIN_FILE_VERSION ||
594             !fgets(index_line, BCTEXTLEN, fp) ||
595             (int)strlen(index_line)-1 != len || index_line[len] != '\n' ||
596             strncmp(index_line, index_id, len) != 0 ) {
597 //              printf("index file mismatch, version %d, index id length %d, expected id %s, file id %s\n", index_version, len, index_id, index_line);
598                 ret = 1;
599         }
600         
601         ArrayList<PluginServer*> plugins;
602         while( !ret && !feof(fp) && fgets(index_line, BCTEXTLEN, fp) ) {
603                 if( index_line[0] == ';' ) continue;
604                 if( index_line[0] == '#' ) continue;
605                 int type = PLUGIN_TYPE_UNKNOWN;
606                 char path[BCTEXTLEN], title[BCTEXTLEN];
607                 int64_t mtime = 0;
608                 if( PluginServer::scan_table(index_line, type, path, title, mtime) ) {
609 //                      printf("PluginServer::scan_table failed for %s\n", index_line);
610                         ret = 1; continue;
611                 }
612                 PluginServer *server = 0;
613                 switch( type ) {
614                 case PLUGIN_TYPE_BUILTIN:
615                 case PLUGIN_TYPE_EXECUTABLE:
616                 case PLUGIN_TYPE_LADSPA: {
617                         char plugin_path[BCTEXTLEN];  struct stat st;
618                         sprintf(plugin_path, "%s/%s", plugin_dir, path);
619                         if( stat(plugin_path, &st) || st.st_mtime != mtime ) {
620 //                              printf("Plugin %s index time %ld, file time %ld\n", plugin_path, mtime, st.st_mtime); 
621                                 ret = 1; continue;
622                         }
623                         server = new PluginServer(mwindow, plugin_path, type);
624                         break; }
625                 case PLUGIN_TYPE_FFMPEG: {
626                         server = new_ffmpeg_server(mwindow, path);
627                         break; }
628                 case PLUGIN_TYPE_LV2: {
629                         server = new_lv2_server(mwindow, path);
630                         break; }
631                 }
632                 if( !server ) continue;
633                 plugins.append(server);
634 // Create plugin server from index entry
635                 server->set_title(title);
636                 if( server->read_table(index_line) ) {
637 //                      printf("server->read_table failed for title %s, %s\n", title, index_line);
638                         ret = 1;  continue;
639                 }
640         }
641
642         if( !ret )
643                 ret = check_plugin_index(plugins, plugin_dir, ".");
644
645         if( !ret )
646                 add_plugins(plugins);
647         else
648                 plugins.remove_all_objects();
649
650         return ret;
651 }
652
653 int MWindow::check_plugin_index(ArrayList<PluginServer*> &plugins,
654         const char *plug_dir, const char *plug_path)
655 {
656         char plugin_path[BCTEXTLEN];
657         sprintf(plugin_path, "%s/%s", plug_dir, plug_path);
658         FileSystem fs;
659         fs.set_filter( "[*.plugin][*.so]" );
660         if( fs.update(plugin_path) )
661                 return 1;
662
663         for( int i=0; i<fs.dir_list.total; ++i ) {
664                 char fs_path[BCTEXTLEN];
665                 get_plugin_path(fs_path, 0, fs.dir_list[i]->path);
666                 if( fs.is_dir(fs_path) ) {
667                         get_plugin_path(plugin_path, plug_dir, fs_path);
668                         if( check_plugin_index(plugins, plug_dir, plugin_path) )
669                                 return 1;
670                 }
671                 else if( !plugin_exists(fs_path, plugins) )
672                         return 1;
673         }
674         return 0;
675 }
676 /*
677 * @brief Load built-in and LV2 plugins as specified in index file,
678 *        rebuild the index file if needed.
679 */
680 int MWindow::init_plugins(MWindow *mwindow, Preferences *preferences)
681 {
682         if( !plugindb )
683                 plugindb = new ArrayList<PluginServer*>;
684         init_ffmpeg();
685         char index_path[BCTEXTLEN], plugin_path[BCTEXTLEN], index_id[BCTEXTLEN];
686         create_defaults_path(index_path, PLUGIN_FILE);
687         char *plugin_dir = FileSystem::basepath(preferences->plugin_dir);
688         strcpy(plugin_path, plugin_dir);  delete [] plugin_dir;
689         // index_id is 2nd line of the index file, normally full plugin path,
690         // but fixed value if AppImage because the path changes on each run.
691         // And if the second line does not match on the next run the index is rebuilt.
692         if( mwindow->appimageDir ) strcpy(index_id, getenv("CINGG_BUILD"));
693         else strcpy(index_id, plugin_path);
694         FILE *fp = fopen(index_path,"a+");
695         if( !fp ) {
696                 fprintf(stderr,_("MWindow::init_plugins: "
697                         "can't open plugin index: %s\n"), index_path);
698                 return -1;
699         }
700         int fd = fileno(fp), ret = -1;
701         if( !flock(fd, LOCK_EX) ) {
702                 fseek(fp, 0, SEEK_SET);
703                 ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
704         }
705         if( ret > 0 ) {
706                 ftruncate(fd, 0);
707                 fseek(fp, 0, SEEK_SET);
708                 printf("build plugin index for: %s\n", plugin_path);
709                 fprintf(fp, "%d\n", PLUGIN_FILE_VERSION);
710                 fprintf(fp, "%s\n", index_id);
711                 init_plugin_index(mwindow, preferences, fp, plugin_path);
712                 init_ffmpeg_index(mwindow, preferences, fp);
713                 init_lv2_index(mwindow, preferences, fp);
714                 fseek(fp, 0, SEEK_SET);
715                 ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
716         }
717         if( ret ) {
718                 fprintf(stderr,_("MWindow::init_plugins: "
719                         "can't %s plugin index: %s\n"),
720                         ret>0 ? _("create") : _("lock"), index_path);
721         }
722         fclose(fp);
723         return ret;
724 }
725
726 /*
727 * @brief Load ladspa plugins as specified in index files, for each ladspa
728 *        directory keep a separate index file. Rebuild index file(s) if needed.
729 **/
730 int MWindow::init_ladspa_plugins(MWindow *mwindow, Preferences *preferences)
731 {
732 #ifdef HAVE_LADSPA
733         char *path = getenv("LADSPA_PATH");
734         char ladspa_path[BCTEXTLEN];
735         if( !path ) {                   // if no env var, use CinGG's own ladspa dir
736                 strncpy(ladspa_path, File::get_ladspa_path(), sizeof(ladspa_path));
737                 path = ladspa_path;
738         }
739         for( int len=0; *path; path+=len ) {
740                 char *cp = strchr(path,':');
741                 len = !cp ? strlen(path) : cp-path;
742                 char index_path[BCTEXTLEN], plugin_path[BCTEXTLEN], index_id[BCTEXTLEN];
743                 memcpy(plugin_path, path, len);  plugin_path[len] = 0;
744                 if( cp ) ++len;
745                 char *plugin_dir = FileSystem::basepath(plugin_path);
746                 strcpy(plugin_path, plugin_dir);  delete [] plugin_dir;
747                 create_defaults_path(index_path, LADSPA_FILE);
748                 cp = index_path + strlen(index_path);
749                 // If the first part of the plugin_path matches the APPDIR, we are
750                 // referring to CinGG's ladspa, replace the path by a fixed ID. APPDIR
751                 // only exists if we are running as AppImage (with variable mount points).
752                 if( mwindow->appimageDir && strncmp(plugin_path, mwindow->appimageDir, strlen(mwindow->appimageDir)) == 0 )
753                         strcpy(index_id, getenv("CINGG_BUILD"));
754                 else strcpy(index_id, plugin_path);
755
756                 // Concatenate the path, replacing '/' with '_'. 
757                 for( char *bp=index_id; *bp!=0; ++bp )
758                         *cp++ = *bp=='/' ? '_' : *bp;
759                 *cp = 0;
760                 FILE *fp = fopen(index_path,"a+");
761                 if( !fp ) {
762                         fprintf(stderr,_("MWindow::init_ladspa_plugins: "
763                                 "can't open ladspa plugin index: %s\n"), index_path);
764                         continue;
765                 }
766                 int fd = fileno(fp), ret = -1;
767                 if( !flock(fd, LOCK_EX) ) {
768                         fseek(fp, 0, SEEK_SET);
769                         ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
770                 }
771                 if( ret > 0 ) {
772                         ftruncate(fd, 0);
773                         fseek(fp, 0, SEEK_SET);
774                         printf("build ladspa plugin index for: %s\n", plugin_path);
775                         fprintf(fp, "%d\n", PLUGIN_FILE_VERSION);
776                         fprintf(fp, "%s\n", index_id);
777                         init_plugin_index(mwindow, preferences, fp, plugin_path);
778                         fseek(fp, 0, SEEK_SET);
779                         ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
780                 }
781                 if( ret ) {
782                         fprintf(stderr,_("MWindow::init_ladspa_plugins: "
783                                 "can't %s ladspa plugin index: %s\n"),
784                                 ret>0 ? _("create") : _("lock"), index_path);
785                 }
786                 fclose(fp);
787         }
788 #endif
789         return 1;
790 }
791
792 void MWindow::init_plugin_index(MWindow *mwindow, Preferences *preferences,
793         FILE *fp, const char *plugin_dir)
794 {
795         int idx = PLUGIN_IDS;
796         if( plugindb ) {
797                 for( int i=0; i<plugindb->size(); ++i ) {
798                         PluginServer *server = plugindb->get(i);
799                         if( server->dir_idx >= idx )
800                                 idx = server->dir_idx+1;
801                 }
802         }
803         scan_plugin_index(mwindow, preferences, fp, plugin_dir, ".", idx);
804 }
805
806 void MWindow::scan_plugin_index(MWindow *mwindow, Preferences *preferences, FILE *fp,
807         const char *plug_dir, const char *plug_path, int &idx)
808 {
809         char plugin_path[BCTEXTLEN];
810         sprintf(plugin_path, "%s/%s", plug_dir, plug_path);
811         FileSystem fs;
812         fs.set_filter( "[*.plugin][*.so][*.dll]" );
813         int result = fs.update(plugin_path);
814         if( result || !fs.dir_list.total ) return;
815         int vis_id = idx++;
816
817         for( int i=0; i<fs.dir_list.total; ++i ) {
818                 char fs_path[BCTEXTLEN], path[BCTEXTLEN];
819                 get_plugin_path(fs_path, 0, fs.dir_list[i]->path);
820                 get_plugin_path(path, plug_dir, fs_path);
821                 if( fs.is_dir(fs_path) ) {
822                         scan_plugin_index(mwindow, preferences, fp, plug_dir, path, idx);
823                         continue;
824                 }
825                 if( plugin_exists(path) ) continue;
826                 struct stat st;
827                 if( stat(fs_path, &st) ) continue;
828                 int64_t mtime = st.st_mtime;
829                 PluginServer server(mwindow, fs_path, PLUGIN_TYPE_UNKNOWN);
830                 result = server.open_plugin(1, preferences, 0, 0);
831                 if( !result ) {
832                         server.write_table(fp, path, vis_id, mtime);
833                         server.close_plugin();
834                 }
835                 else if( result == PLUGINSERVER_IS_LAD ) {
836                         int lad_index = 0;
837                         for(;;) {
838                                 PluginServer ladspa(mwindow, fs_path, PLUGIN_TYPE_LADSPA);
839                                 ladspa.set_lad_index(lad_index++);
840                                 result = ladspa.open_plugin(1, preferences, 0, 0);
841                                 if( result ) break;
842                                 ladspa.write_table(fp, path, PLUGIN_LADSPA_ID, mtime);
843                                 ladspa.close_plugin();
844                         }
845                 }
846         }
847 }
848
849 void MWindow::add_plugins(ArrayList<PluginServer*> &plugins)
850 {
851         for( int i=0; i<plugins.size(); ++i )
852                 plugindb->append(plugins[i]);
853         plugins.remove_all();
854 }
855
856 void MWindow::init_plugin_tips(ArrayList<PluginServer*> &plugins, const char *lang)
857 {
858         const char *dat_path = File::get_cindat_path();
859         char msg_path[BCTEXTLEN];
860         FILE *fp = 0;
861         if( BC_Resources::language[0] ) {
862                 snprintf(msg_path, sizeof(msg_path), "%s/info/plugins.%s",
863                         dat_path, lang);
864                 fp = fopen(msg_path, "r");
865         }
866         if( !fp ) {
867                 snprintf(msg_path, sizeof(msg_path), "%s/info/plugins.txt",
868                         dat_path);
869                 fp = fopen(msg_path, "r");
870         }
871         if( !fp ) return;
872         char text[BCTEXTLEN];
873         char *tp = text, *ep = tp + sizeof(text)-1;
874         char title[BCTEXTLEN];
875         title[0] = 0;
876         int no = 0;
877         for(;;) {
878                 ++no;  int done = 1;
879                 char line[BCTEXTLEN], *cp = line;
880                 if( fgets(line,sizeof(line)-1,fp) ) {
881                         if( *cp == '#' ) continue;
882                         done = *cp == ' ' || *cp == '\t' ? 0 : -1;
883                 }
884                 if( done ) {
885                         if( tp > text && *--tp == '\n' ) *tp = 0;
886                         if( title[0] ) {
887                                 int idx = plugins.size();
888                                 while( --idx>=0 && strcmp(plugins[idx]->title, title) );
889                                 if( idx >= 0 ) {
890                                         delete [] plugins[idx]->tip;
891                                         plugins[idx]->tip = cstrdup(text);
892                                 }
893                                 title[0] = 0;
894                         }
895                         if( done > 0 ) break;
896                         tp = text;  *tp = 0;
897                         char *dp = strchr(cp, ':');
898                         if( !dp ) {
899                                 printf("plugin tips: error on line %d\n", no);
900                                 continue;
901                         }
902                         char *bp = title;
903                         while( cp < dp ) *bp++ = *cp++;
904                         *bp = 0;
905                         ++cp;
906                 }
907
908                 while( *cp == ' ' || *cp == '\t' ) ++cp;
909                 for( ; tp<ep && (*tp=*cp)!=0; ++tp,++cp );
910         }
911         fclose(fp);
912 }
913
914 void MWindow::delete_plugins()
915 {
916         plugindb->remove_all_objects();
917         delete plugindb;
918         plugindb = 0;
919 }
920
921 void MWindow::search_plugindb(int do_audio,
922                 int do_video,
923                 int is_realtime,
924                 int is_transition,
925                 int is_theme,
926                 ArrayList<PluginServer*> &results)
927 {
928 // Get plugins
929         for(int i = 0; i < MWindow::plugindb->total; i++)
930         {
931                 PluginServer *current = MWindow::plugindb->get(i);
932
933                 if(current->audio == do_audio &&
934                         current->video == do_video &&
935                         (current->realtime == is_realtime || is_realtime < 0) &&
936                         current->transition == is_transition &&
937                         current->theme == is_theme)
938                         results.append(current);
939         }
940
941 // Alphabetize list by title
942         int done = 0;
943         while(!done)
944         {
945                 done = 1;
946
947                 for(int i = 0; i < results.total - 1; i++)
948                 {
949                         PluginServer *value1 = results[i];
950                         PluginServer *value2 = results[i + 1];
951                         if(strcmp(_(value1->title), _(value2->title)) > 0)
952                         {
953                                 done = 0;
954                                 results[i] = value2;
955                                 results[i + 1] = value1;
956                         }
957                 }
958         }
959 }
960
961 PluginServer* MWindow::scan_plugindb(char *title,
962                 int data_type)
963 {
964 //      if(data_type < 0)
965 //      {
966 //              printf("MWindow::scan_plugindb data_type < 0\n");
967 //              return 0;
968 //      }
969
970         for(int i = 0; i < plugindb->total; i++)
971         {
972                 PluginServer *server = plugindb->get(i);
973                 if(server->title &&
974                         !strcasecmp(server->title, title) &&
975                         (data_type < 0 ||
976                                 (data_type == TRACK_AUDIO && server->audio) ||
977                                 (data_type == TRACK_VIDEO && server->video)))
978                         return plugindb->get(i);
979         }
980         return 0;
981 }
982
983 // repair session files with xlated plugin titles
984 void MWindow::fix_plugin_title(char *title)
985 {
986         for(int i = 0; i < plugindb->total; i++) {
987                 PluginServer *server = plugindb->get(i);
988                 if( !server->title ) continue;
989                 const char *server_title = server->title;
990                 if( !bstrcasecmp(title, _(server_title)) ) {
991                         strcpy(title, server_title);
992                         return;
993                 }
994         }
995 }
996
997 int MWindow::plugin_exists(const char *plugin_path, ArrayList<PluginServer*> &plugins)
998 {
999         for( int i=0; i<plugins.size(); ++i )
1000                 if( !strcmp(plugin_path, plugins[i]->get_path()) ) return 1;
1001         return 0;
1002 }
1003
1004 int MWindow::plugin_exists(char *plugin_path)
1005 {
1006         return !plugindb ? 0 : plugin_exists(plugin_path, *plugindb);
1007 }
1008
1009 void MWindow::remove_plugin_index()
1010 {
1011         char index_path[BCTEXTLEN];
1012         MWindow::create_defaults_path(index_path, PLUGIN_FILE);
1013         ::remove(index_path);
1014 }
1015
1016 void MWindow::init_preferences()
1017 {
1018         preferences = new Preferences;
1019         preferences->load_defaults(defaults);
1020         File::setenv_path("LV2_PATH",preferences->lv2_path, 1);
1021         session = new MainSession(this);
1022         session->load_defaults(defaults);
1023         // set x11_host, screens, window_config
1024         screens = session->set_default_x11_host();
1025         BC_Signals::set_trap_hook(trap_hook, this);
1026         BC_Signals::set_catch_segv(preferences->trap_sigsegv);
1027         BC_Signals::set_catch_intr(preferences->trap_sigintr);
1028         if( preferences->trap_sigsegv || preferences->trap_sigintr ) {
1029                 BC_Trace::enable_locks();
1030         }
1031         else {
1032                 BC_Trace::disable_locks();
1033         }
1034         BC_WindowBase::get_resources()->popupmenu_btnup = preferences->popupmenu_btnup;
1035         BC_WindowBase::get_resources()->textbox_focus_policy = preferences->textbox_focus_policy;
1036         BC_WindowBase::get_resources()->grab_input_focus = preferences->grab_input_focus;
1037         YUV::yuv.yuv_set_colors(preferences->yuv_color_space, preferences->yuv_color_range);
1038 }
1039
1040 void MWindow::clean_backups()
1041 {
1042     FileSystem fs;
1043     int total_excess;
1044     long oldest = 0;
1045     int oldest_item = -1;
1046     char string[BCTEXTLEN];
1047
1048 // Delete extra backups
1049     fs.set_filter("backup*.prev_*");
1050     fs.complete_path(preferences->index_directory);
1051     fs.update(preferences->index_directory);
1052
1053     // set to 50 for now
1054     // total_excess = fs.dir_list.total - preferences->index_count;
1055     total_excess = fs.dir_list.total - 50;
1056     printf("Total excess of backups: %i \n", total_excess);
1057
1058 //printf("MWindow::clean_backups 1 %d\n", fs.dir_list.total);
1059
1060     while(total_excess > 0)
1061     {
1062 // Get oldest
1063        for(int i = 0; i < fs.dir_list.total; i++)
1064        {
1065            fs.join_names(string, preferences->index_directory, fs.dir_list[i]->name);
1066
1067            if(i == 0 || fs.get_date(string) <= oldest)
1068            {
1069                oldest = fs.get_date(string);
1070                oldest_item = i;
1071            }
1072        }
1073
1074        if(oldest_item >= 0)
1075        {
1076 // Remove backup file
1077            fs.join_names(string,
1078                preferences->index_directory,
1079                fs.dir_list[oldest_item]->name);
1080 //printf("MWindow::clean_backups 1 %s\n", string);
1081            if(remove(string))
1082                perror("delete_backups");
1083            delete fs.dir_list[oldest_item];
1084            fs.dir_list.remove_number(oldest_item);
1085
1086        }
1087
1088        total_excess--;
1089     }
1090 }
1091
1092 void MWindow::clean_indexes()
1093 {
1094         FileSystem fs;
1095         int total_excess;
1096         long oldest = 0;
1097         int oldest_item = -1;
1098         int result;
1099         char string[BCTEXTLEN];
1100         char string2[BCTEXTLEN];
1101
1102 // Delete extra indexes
1103         fs.set_filter("*.idx");
1104         fs.complete_path(preferences->index_directory);
1105         fs.update(preferences->index_directory);
1106 //printf("MWindow::clean_indexes 1 %d\n", fs.dir_list.total);
1107
1108 // Eliminate directories
1109         result = 1;
1110         while(result)
1111         {
1112                 result = 0;
1113                 for(int i = 0; i < fs.dir_list.total && !result; i++)
1114                 {
1115                         fs.join_names(string, preferences->index_directory, fs.dir_list[i]->name);
1116                         if(fs.is_dir(string))
1117                         {
1118                                 delete fs.dir_list[i];
1119                                 fs.dir_list.remove_number(i);
1120                                 result = 1;
1121                         }
1122                 }
1123         }
1124         total_excess = fs.dir_list.total - preferences->index_count;
1125
1126 //printf("MWindow::clean_indexes 2 %d\n", fs.dir_list.total);
1127         while(total_excess > 0)
1128         {
1129 // Get oldest
1130                 for(int i = 0; i < fs.dir_list.total; i++)
1131                 {
1132                         fs.join_names(string, preferences->index_directory, fs.dir_list[i]->name);
1133
1134                         if(i == 0 || fs.get_date(string) <= oldest)
1135                         {
1136                                 oldest = fs.get_date(string);
1137                                 oldest_item = i;
1138                         }
1139                 }
1140
1141                 if(oldest_item >= 0)
1142                 {
1143 // Remove index file
1144                         fs.join_names(string,
1145                                 preferences->index_directory,
1146                                 fs.dir_list[oldest_item]->name);
1147 //printf("MWindow::clean_indexes 1 %s\n", string);
1148                         if(remove(string))
1149                                 perror("delete_indexes");
1150                         delete fs.dir_list[oldest_item];
1151                         fs.dir_list.remove_number(oldest_item);
1152
1153 // Remove table of contents if it exists
1154                         strcpy(string2, string);
1155                         char *ptr = strrchr(string2, '.');
1156                         if(ptr)
1157                         {
1158 //printf("MWindow::clean_indexes 2 %s\n", string2);
1159                                 sprintf(ptr, ".toc");
1160                                 remove(string2);
1161                                 sprintf(ptr, ".mkr");
1162                                 remove(string2);
1163                         }
1164                 }
1165
1166                 total_excess--;
1167         }
1168 }
1169
1170 void MWindow::init_awindow()
1171 {
1172         awindow = new AWindow(this);
1173         awindow->create_objects();
1174 }
1175
1176 void MWindow::init_gwindow()
1177 {
1178         gwindow = new GWindow(this);
1179         gwindow->create_objects();
1180 }
1181
1182 void MWindow::init_tipwindow()
1183 {
1184         TipWindow::load_tips(cin_lang);
1185         if( !twindow )
1186                 twindow = new TipWindow(this);
1187         twindow->start();
1188 }
1189
1190 void MWindow::show_warning(int *do_warning, const char *text)
1191 {
1192         if( do_warning && !*do_warning ) return;
1193         if( !wwindow )
1194                 wwindow = new WWindow(this);
1195         wwindow->show_warning(do_warning, text);
1196 }
1197
1198 int MWindow::wait_warning()
1199 {
1200         return wwindow->wait_result();
1201 }
1202
1203 void MWindow::init_theme()
1204 {
1205         Timer timer;
1206         theme = 0;
1207
1208         PluginServer *theme_plugin = 0;
1209         for(int i = 0; i < plugindb->total && !theme_plugin; i++) {
1210                 if( plugindb->get(i)->theme &&
1211                     !strcasecmp(preferences->theme, plugindb->get(i)->title) )
1212                         theme_plugin = plugindb->get(i);
1213         }
1214
1215         if( !theme_plugin )
1216                 fprintf(stderr, _("MWindow::init_theme: prefered theme %s not found.\n"),
1217                          preferences->theme);
1218
1219         const char *default_theme = DEFAULT_THEME;
1220         if( !theme_plugin && strcasecmp(preferences->theme, default_theme) ) {
1221                 fprintf(stderr, _("MWindow::init_theme: trying default theme %s\n"),
1222                         default_theme);
1223                 for(int i = 0; i < plugindb->total && !theme_plugin; i++) {
1224                         if( plugindb->get(i)->theme &&
1225                             !strcasecmp(default_theme, plugindb->get(i)->title) )
1226                                 theme_plugin = plugindb->get(i);
1227                 }
1228         }
1229
1230         if(!theme_plugin) {
1231                 fprintf(stderr, _("MWindow::init_theme: theme_plugin not found.\n"));
1232                 exit(1);
1233         }
1234
1235         PluginServer *plugin = new PluginServer(*theme_plugin);
1236         if( plugin->open_plugin(0, preferences, 0, 0) ) {
1237                 fprintf(stderr, _("MWindow::init_theme: unable to load theme %s\n"),
1238                         theme_plugin->title);
1239                 exit(1);
1240         }
1241
1242         theme = plugin->new_theme();
1243         theme->mwindow = this;
1244         strcpy(theme->path, plugin->path);
1245         delete plugin;
1246
1247 // Load default images & settings
1248         theme->Theme::initialize();
1249 // Load user images & settings
1250         theme->initialize();
1251 // Create menus with user colors
1252         theme->build_menus();
1253         init_menus();
1254
1255         theme->sort_image_sets();
1256         theme->check_used();
1257 //printf("MWindow::init_theme %d total_time=%d\n", __LINE__, (int)timer.get_difference());
1258 }
1259
1260 void MWindow::init_3d()
1261 {
1262         playback_3d = new Playback3D(this);
1263         playback_3d->create_objects();
1264 }
1265
1266 void MWindow::init_edl()
1267 {
1268         edl = new EDL;
1269         edl->create_objects();
1270         fill_preset_defaults(default_standard, edl->session);
1271         edl->load_defaults(defaults);
1272         edl->session->brender_start = edl->session->brender_end = 0;
1273         edl->create_default_tracks();
1274         edl->tracks->update_y_pixels(theme);
1275 }
1276
1277 void MWindow::init_compositor()
1278 {
1279         cwindow = new CWindow(this);
1280         cwindow->create_objects();
1281 }
1282
1283 void MWindow::init_levelwindow()
1284 {
1285         lwindow = new LevelWindow(this);
1286         lwindow->create_objects();
1287 }
1288
1289 VWindow *MWindow::get_viewer(int start_it, int idx)
1290 {
1291         vwindows_lock->lock("MWindow::get_viewer");
1292         VWindow *vwindow = idx >= 0 && idx < vwindows.size() ? vwindows[idx] : 0;
1293         if( !vwindow ) idx = vwindows.size();
1294         while( !vwindow && --idx >= 0 ) {
1295                 VWindow *vwin = vwindows[idx];
1296                 if( !vwin->is_running() || !vwin->get_edl() )
1297                         vwindow = vwin;
1298         }
1299         if( !vwindow ) {
1300                 vwindow = new VWindow(this);
1301                 vwindow->load_defaults();
1302                 vwindow->create_objects();
1303                 vwindows.append(vwindow);
1304         }
1305         vwindows_lock->unlock();
1306         if( start_it ) vwindow->start();
1307         return vwindow;
1308 }
1309
1310 ZWindow *MWindow::get_mixer(Mixer *&mixer)
1311 {
1312         zwindows_lock->lock("MWindow::get_mixer");
1313         if( !mixer ) mixer = edl->mixers.new_mixer();
1314         ZWindow *zwindow = 0;
1315         for( int i=0; !zwindow && i<zwindows.size(); ++i ) {
1316                 ZWindow *zwdw = zwindows[i];
1317                 if( zwdw->running() ) continue;
1318                 if( zwdw->idx >= 0 ) continue;
1319                 zwindow = zwindows[i];
1320         }
1321         if( !zwindow )
1322                 zwindows.append(zwindow = new ZWindow(this));
1323         zwindow->idx = mixer->idx;
1324         zwindows_lock->unlock();
1325         return zwindow;
1326 }
1327
1328 ZWindow *MWindow::get_mixer(int idx)
1329 {
1330         ZWindow *zwindow = 0;
1331         zwindows_lock->lock("MWindow::get_mixer");
1332         for( int i=0; !zwindow && i<zwindows.size(); ++i ) {
1333                 ZWindow *zwdw = zwindows[i];
1334                 if( !zwdw->running() ) continue;
1335                 if( zwdw->idx != idx ) continue;
1336                 zwindow = zwindows[i];
1337         }
1338         zwindows_lock->unlock();
1339         return zwindow;
1340 }
1341
1342 void MWindow::close_mixer(ZWindow *zwindow)
1343 {
1344         zwindows_lock->lock("MWindow::close_mixer 0");
1345         if( session->selected_zwindow >= 0 ) {
1346                 int i = zwindows.number_of(zwindow);
1347                 if( i >= 0 && i < session->selected_zwindow )
1348                         --session->selected_zwindow;
1349                 else if( i == session->selected_zwindow )
1350                         session->selected_zwindow = -1;
1351         }
1352         zwindows_lock->unlock();
1353         gui->lock_window("MWindow::close_mixer 1");
1354         gui->update_mixers(0, -1);
1355         gui->unlock_window();
1356 }
1357
1358 void MWindow::start_mixer()
1359 {
1360         Mixer *mixer = 0;
1361         ZWindow *zwindow = get_mixer(mixer);
1362         const char *title = 0;
1363
1364         for( Track *track=edl->tracks->first; track!=0; track=track->next ) {
1365                 PatchGUI *patchgui = get_patchgui(track);
1366                 if( !patchgui || !patchgui->mixer ) continue;
1367                 mixer->mixer_ids.append(track->get_mixer_id());
1368                 if( !title ) title = track->title;
1369         }
1370
1371         session->selected_zwindow = -1;
1372         gui->lock_window("MWindow::start_mixer");
1373         gui->update_mixers(0, 0);
1374         gui->unlock_window();
1375
1376         zwindow->set_title(title);
1377         zwindow->start();
1378         refresh_mixers();
1379 }
1380
1381 int MWindow::mixer_track_active(Track *track)
1382 {
1383         int i = session->selected_zwindow;
1384         if( i < 0 || i >= zwindows.size() ) return 0;
1385         ZWindow *zwindow = zwindows[i];
1386         Mixer *mixer = edl->mixers.get_mixer(zwindow->idx);
1387         if( !mixer ) return 0;
1388         int n = mixer->mixer_ids.number_of(track->get_mixer_id());
1389         return n >= 0 ? 1 : 0;
1390 }
1391
1392 void MWindow::update_mixer_tracks()
1393 {
1394         zwindows_lock->lock("MixPatch::handle_event");
1395         int i = session->selected_zwindow;
1396         if( i >= 0 && i < zwindows.size() ) {
1397                 ZWindow *zwindow = zwindows[i];
1398                 zwindow->update_mixer_ids();
1399         }
1400         zwindows_lock->unlock();
1401 }
1402
1403 void MWindow::handle_mixers(EDL *edl, int command, int wait_tracking,
1404                 int use_inout, int toggle_audio, int loop_play, float speed)
1405 {
1406         zwindows_lock->lock("MWindow::handle_mixers");
1407         for( int vidx=0; vidx<zwindows.size(); ++vidx ) {
1408                 ZWindow *zwindow = zwindows[vidx];
1409                 if( zwindow->idx < 0 ) continue;
1410                 Mixer *mixer = edl->mixers.get_mixer(zwindow->idx);
1411                 if( !mixer || !mixer->mixer_ids.size() ) continue;
1412                 int k = -1;
1413                 for( Track *track = edl->tracks->first; k<0 && track!=0; track=track->next ) {
1414                         if( track->data_type != TRACK_VIDEO ) continue;
1415                         int mixer_id = track->get_mixer_id();
1416                         k = mixer->mixer_ids.size();
1417                         while( --k >= 0 && mixer_id != mixer->mixer_ids[k] );
1418                 }
1419                 if( k < 0 ) continue;
1420                 EDL *mixer_edl = new EDL(this->edl);
1421                 mixer_edl->create_objects();
1422                 mixer_edl->copy_all(edl);
1423                 mixer_edl->remove_vwindow_edls();
1424                 for( Track *track = mixer_edl->tracks->first; track!=0; track=track->next ) {
1425                         k = mixer->mixer_ids.size();
1426                         while( --k >= 0 && track->get_mixer_id() != mixer->mixer_ids[k] );
1427                         if( k >= 0 ) {
1428                                 track->armed = 1;
1429                                 track->play = track->data_type == TRACK_VIDEO ? 1 : 0;
1430                         }
1431                         else
1432                                 track->armed = track->play = 0;
1433                 }
1434                 zwindow->change_source(mixer_edl);
1435                 zwindow->handle_mixer(command, 0,
1436                                 use_inout, toggle_audio, loop_play, speed);
1437         }
1438         zwindows_lock->unlock();
1439 }
1440
1441 void MWindow::refresh_mixers(int dir)
1442 {
1443         int command = dir >= 0 ? CURRENT_FRAME : LAST_FRAME;
1444         handle_mixers(edl, command, 0, 0, 0, 0, 0);
1445 }
1446
1447 void MWindow::stop_mixers()
1448 {
1449         for( int vidx=0; vidx<zwindows.size(); ++vidx ) {
1450                 ZWindow *zwindow = zwindows[vidx];
1451                 if( zwindow->idx < 0 ) continue;
1452                 zwindow->handle_mixer(STOP, 0, 0, 0, 0, 0);
1453         }
1454 }
1455
1456 void MWindow::close_mixers(int result)
1457 {
1458         ArrayList<ZWindow*> closed;
1459         zwindows_lock->lock("MWindow::close_mixers");
1460         for( int i=zwindows.size(); --i>=0; ) {
1461                 ZWindow *zwindow = zwindows[i];
1462                 if( zwindow->idx < 0 ) continue;
1463                 zwindow->idx = -1;
1464                 ZWindowGUI *zgui = zwindow->zgui;
1465                 zgui->lock_window("MWindow::select_zwindow 0");
1466                 zgui->set_done(result);
1467                 zgui->unlock_window();
1468                 closed.append(zwindow);
1469         }
1470         zwindows_lock->unlock();
1471         for( int i=0; i<closed.size(); ++i ) {
1472                 ZWindow *zwindow = closed[i];
1473                 zwindow->join();
1474         }
1475 }
1476
1477 ZWindow *MWindow::create_mixer(Indexable *indexable, double position)
1478 {
1479         ArrayList<Indexable*> new_assets;
1480         new_assets.append(indexable);
1481         Track *track = edl->tracks->last;
1482         load_assets(&new_assets, position, LOADMODE_NEW_TRACKS, 0, 0, 0, 0, 0, 1);
1483         track = !track ? edl->tracks->first : track->next;
1484         Mixer *mixer = 0;
1485         ZWindow *zwindow = get_mixer(mixer);
1486         while( track ) {
1487                 track->play = track->armed = 0;
1488                 if( track->data_type == TRACK_VIDEO ) {
1489                         sprintf(track->title, _("Mixer %d"), zwindow->idx);
1490                 }
1491                 mixer->mixer_ids.append(track->get_mixer_id());
1492                 track = track->next;
1493         }
1494         if(  indexable->is_asset ) {
1495                 char *path = indexable->path;
1496                 char *tp = strrchr(path, '/');
1497                 if( !tp ) tp = path; else ++tp;
1498                 zwindow->set_title(tp);
1499         }
1500         else {
1501                 char *title = ((EDL*)indexable)->local_session->clip_title;
1502                 zwindow->set_title(title);
1503         }
1504         return zwindow;
1505 }
1506
1507 void MWindow::create_mixers(double position)
1508 {
1509         if( !session->drag_assets->size() &&
1510             !session->drag_clips->size() ) return;
1511         undo_before();
1512
1513         select_zwindow(0);
1514         ArrayList<ZWindow *>new_mixers;
1515
1516         for( int i=0; i<session->drag_assets->size(); ++i ) {
1517                 Indexable *indexable = session->drag_assets->get(i);
1518                 if( !indexable->have_video() ) continue;
1519                 ZWindow *zwindow = create_mixer(indexable, position);
1520                 new_mixers.append(zwindow);
1521         }
1522         for( int i=0; i<session->drag_clips->size(); ++i ) {
1523                 Indexable *indexable = (Indexable*)session->drag_clips->get(i);
1524                 if( !indexable->have_video() ) continue;
1525                 ZWindow *zwindow = create_mixer(indexable, position);
1526                 new_mixers.append(zwindow);
1527         }
1528
1529         tile_mixers();
1530         for( int i=0; i<new_mixers.size(); ++i )
1531                 new_mixers[i]->start();
1532
1533         refresh_mixers();
1534         save_backup();
1535         undo_after(_("create mixers"), LOAD_ALL);
1536         restart_brender();
1537         gui->update(1, FORCE_REDRAW, 1, 1, 1, 1, 0);
1538         sync_parameters(CHANGE_ALL);
1539 }
1540
1541 void MWindow::open_mixers()
1542 {
1543         for( int i=0; i<edl->mixers.size(); ++i ) {
1544                 Mixer *mixer = edl->mixers[i];
1545                 ZWindow *zwindow = get_mixer(mixer);
1546                 zwindow->set_title(mixer->title);
1547                 zwindow->start();
1548         }
1549         refresh_mixers();
1550 }
1551
1552 int MWindow::select_zwindow(ZWindow *zwindow)
1553 {
1554         int ret = 0, n = zwindows.number_of(zwindow);
1555         if( session->selected_zwindow != n ) {
1556                 session->selected_zwindow = n;
1557                 for( int i=0; i<zwindows.size(); ++i ) {
1558                         ZWindow *zwindow = zwindows[i];
1559                         if( zwindow->idx < 0 ) continue;
1560                         ZWindowGUI *zgui = zwindow->zgui;
1561                         zgui->lock_window("MWindow::select_zwindow 0");
1562                         zwindow->highlighted = i == n ? 1 : 0;
1563                         if( zgui->draw_overlays() )
1564                                 zgui->canvas->get_canvas()->flash(1);
1565                         zgui->unlock_window();
1566                 }
1567                 ret = 1;
1568                 gui->lock_window("MWindow::select_window 1");
1569                 gui->update_mixers(0, -1);
1570                 gui->unlock_window();
1571         }
1572         return ret;
1573 }
1574
1575 void MWindow::tile_mixers()
1576 {
1577         int x1 = session->tile_mixers_x;
1578         int y1 = session->tile_mixers_y;
1579         int x2 = x1 + session->tile_mixers_w;
1580         int y2 = y1 + session->tile_mixers_h;
1581         tile_mixers(x1, y1, x2, y2);
1582 }
1583
1584 void MWindow::tile_mixers(int x1, int y1, int x2, int y2)
1585 {
1586         if( x1 == x2 || y1 == y2 ) {
1587 // use top left quadrent
1588                 int sw = gui->get_screen_w(1, -1);
1589                 int sh = gui->get_screen_w(1, -1);
1590                 x1 = 1;     y1 = 1;
1591                 x2 = sw/2;  y2 = sh/2;
1592         }
1593         else {
1594                 if( x1 > x2 ) { int t = x1;  x1 = x2;  x2 = t; }
1595                 if( y1 > y2 ) { int t = y1;  y1 = y2;  y2 = t; }
1596         }
1597         int ow = edl->session->output_w, oh = edl->session->output_h;
1598         int nz = 0;
1599         for( int i=0; i<zwindows.size(); ++i ) {
1600                 ZWindow *zwindow = zwindows[i];
1601                 if( zwindow->idx < 0 ) continue;
1602                 ++nz;
1603         }
1604         if( !nz ) return;
1605         int lt = BC_DisplayInfo::get_left_border();
1606         int top = BC_DisplayInfo::get_top_border();
1607         int bw = lt + BC_DisplayInfo::get_right_border();  // borders
1608         int bh = top + BC_DisplayInfo::get_bottom_border();
1609         int mw = xS(10+10), mh = yS(10+10); // canvas margins
1610         int dx = x2 - x1, dy = y2 - y1;
1611         int64_t sz = dx * dy, best_r = sz;
1612         int bx = 1, by = nz;
1613         for( int nx=1; nx<=nz; ++nx ) {
1614                 int ny = ceil((double)nz / nx);
1615                 int zw = dx / nx;
1616                 int ww = zw - bw;
1617                 int hh = (ww - mw) * oh / ow + mh;
1618                 int zh = hh + bh;
1619                 int64_t za = zw*nx * zh*ny;
1620                 int64_t r = sz - za;
1621                 if( r < 0 ) continue;
1622                 if( r >= best_r ) continue;
1623                 best_r = r;
1624                 bx = nx;  by = ny;
1625         }
1626         for( int ny=1; ny<=nz; ++ny ) {
1627                 int nx = ceil((double)nz / ny);
1628                 int zh = dy / ny;
1629                 int hh = zh - bh;
1630                 int ww = (hh - mh) * ow / oh + mw;
1631                 int zw = ww + bw;
1632                 int64_t za = zw*nx * zh*ny;
1633                 int64_t r = sz - za;
1634                 if( r < 0 ) continue;
1635                 if( r >= best_r ) continue;
1636                 best_r = r;
1637                 bx = nx;  by = ny;
1638         }
1639         int zw, zh, ww, hh;
1640         if( bx < by ) {
1641                 zh = dy / by;
1642                 hh = zh - bh;
1643                 ww = (hh - mh) * ow / oh + mw;
1644                 zw = ww + bw;
1645         }
1646         else {
1647                 zw = dx / bx;
1648                 ww = zw - bw;
1649                 hh = (ww - mw) * oh / ow + mh;
1650                 zh = hh + bh;
1651         }
1652
1653         int zx = 0, zy = 0;  // window origins
1654         int n = 0;
1655         for( int i=0; i<zwindows.size(); ++i ) {
1656                 ZWindow *zwindow = zwindows[i];
1657                 if( zwindow->idx < 0 ) continue;
1658                 int xx = zx + x1, yy = zy + y1;
1659                 int mx = x2 - zw, my = y2 - zh;
1660                 if( xx > mx ) xx = mx;
1661                 if( yy > my ) yy = my;
1662                 zwindow->reposition(xx,yy, ww,hh);
1663                 if( zwindow->running() ) {
1664                         ZWindowGUI *gui = (ZWindowGUI *)zwindow->get_gui();
1665                         gui->lock_window("MWindow::tile_mixers");
1666                         gui->BC_WindowBase::reposition_window(xx,yy, ww,hh);
1667                         gui->unlock_window();
1668                 }
1669                 if( ++n >= bx ) {
1670                         zx = 0;  zy += zh;
1671                         n = 0;
1672                 }
1673                 else
1674                         zx += zw;
1675         }
1676 }
1677
1678 void MWindow::set_gang_tracks(int v)
1679 {
1680         edl->local_session->gang_tracks = v;
1681         sync_parameters(CHANGE_PARAMS);
1682         gui->update(1, 1, 0, 0, 1, 0, 0);
1683         gui->flush();
1684 }
1685
1686
1687 void MWindow::init_cache()
1688 {
1689         audio_cache = new CICache(preferences);
1690         video_cache = new CICache(preferences);
1691         frame_cache = new FrameCache;
1692         wave_cache = new WaveCache;
1693 }
1694
1695 void MWindow::init_channeldb()
1696 {
1697         channeldb_buz->load("channeldb_buz");
1698         channeldb_v4l2jpeg->load("channeldb_v4l2jpeg");
1699 }
1700
1701 void MWindow::init_menus()
1702 {
1703         char string[BCTEXTLEN];
1704
1705         // Color Models
1706         BC_CModels::to_text(string, BC_RGB888);
1707         colormodels.append(new ColormodelItem(string, BC_RGB888));
1708         BC_CModels::to_text(string, BC_RGBA8888);
1709         colormodels.append(new ColormodelItem(string, BC_RGBA8888));
1710 //      BC_CModels::to_text(string, BC_RGB161616);
1711 //      colormodels.append(new ColormodelItem(string, BC_RGB161616));
1712 //      BC_CModels::to_text(string, BC_RGBA16161616);
1713 //      colormodels.append(new ColormodelItem(string, BC_RGBA16161616));
1714         BC_CModels::to_text(string, BC_RGB_FLOAT);
1715         colormodels.append(new ColormodelItem(string, BC_RGB_FLOAT));
1716         BC_CModels::to_text(string, BC_RGBA_FLOAT);
1717         colormodels.append(new ColormodelItem(string, BC_RGBA_FLOAT));
1718         BC_CModels::to_text(string, BC_YUV888);
1719         colormodels.append(new ColormodelItem(string, BC_YUV888));
1720         BC_CModels::to_text(string, BC_YUVA8888);
1721         colormodels.append(new ColormodelItem(string, BC_YUVA8888));
1722 //      BC_CModels::to_text(string, BC_YUV161616);
1723 //      colormodels.append(new ColormodelItem(string, BC_YUV161616));
1724 //      BC_CModels::to_text(string, BC_YUVA16161616);
1725 //      colormodels.append(new ColormodelItem(string, BC_YUVA16161616));
1726
1727 #define ILACEPROJECTMODELISTADD(x) ilacemode_to_text(string, x); \
1728                            interlace_project_modes.append(new InterlacemodeItem(string, x));
1729
1730 #define ILACEASSETMODELISTADD(x) ilacemode_to_text(string, x); \
1731                            interlace_asset_modes.append(new InterlacemodeItem(string, x));
1732
1733         // Interlacing Modes
1734         ILACEASSETMODELISTADD(ILACE_MODE_UNDETECTED); // Not included in the list for the project options.
1735
1736         ILACEASSETMODELISTADD(ILACE_MODE_TOP_FIRST);
1737         ILACEPROJECTMODELISTADD(ILACE_MODE_TOP_FIRST);
1738
1739         ILACEASSETMODELISTADD(ILACE_MODE_BOTTOM_FIRST);
1740         ILACEPROJECTMODELISTADD(ILACE_MODE_BOTTOM_FIRST);
1741
1742         ILACEASSETMODELISTADD(ILACE_MODE_NOTINTERLACED);
1743         ILACEPROJECTMODELISTADD(ILACE_MODE_NOTINTERLACED);
1744
1745         mixers_align = new MixersAlign(this);
1746 }
1747
1748 void MWindow::init_indexes()
1749 {
1750         mainindexes = new MainIndexes(this);
1751         mainindexes->start_loop();
1752 }
1753
1754 void MWindow::init_gui()
1755 {
1756         gui = new MWindowGUI(this);
1757         gui->create_objects();
1758         gui->load_defaults(defaults);
1759 }
1760
1761 void MWindow::init_signals()
1762 {
1763         sighandler = new SigHandler;
1764         sighandler->initialize("/tmp/cinelerra_%d.dmp");
1765 ENABLE_BUFFER
1766 }
1767
1768 void MWindow::init_render()
1769 {
1770         render = new Render(this);
1771         create_bd = new CreateBD_Thread(this);
1772         create_dvd = new CreateDVD_Thread(this);
1773         batch_render = new BatchRenderThread(this);
1774 }
1775
1776 void MWindow::init_exportedl()
1777 {
1778         exportedl = new ExportEDL(this);
1779 }
1780
1781
1782 void MWindow::init_shuttle()
1783 {
1784 #ifdef HAVE_SHUTTLE
1785         int ret = Shuttle::probe();
1786         if( ret >= 0 ) {
1787                 shuttle = new Shuttle(this);
1788                 if( shuttle->read_config_file() > 0 ) {
1789                         printf("shuttle: bad config file\n");
1790                         delete shuttle;  shuttle = 0;
1791                         return;
1792                 }
1793                 shuttle->start(ret);
1794         }
1795 #endif
1796 }
1797 void MWindow::init_wintv()
1798 {
1799 #ifdef HAVE_WINTV
1800         wintv = WinTV::probe(this);
1801         if( wintv )
1802                 wintv->start();
1803 #endif
1804 }
1805 void MWindow::init_x10tv()
1806 {
1807 #ifdef HAVE_X10TV
1808         x10tv = X10TV::probe(this);
1809         if( x10tv )
1810                 x10tv->start();
1811 #endif
1812 }
1813
1814
1815 void MWindow::init_brender()
1816 {
1817         if(preferences->use_brender && !brender)
1818         {
1819                 brender_lock->lock("MWindow::init_brender 1");
1820                 brender = new BRender(this);
1821                 brender->initialize();
1822                 session->brender_end = 0;
1823                 brender_lock->unlock();
1824         }
1825         else
1826         if(!preferences->use_brender && brender)
1827         {
1828                 brender_lock->lock("MWindow::init_brender 2");
1829                 delete brender;
1830                 brender = 0;
1831                 session->brender_end = 0;
1832                 brender_lock->unlock();
1833         }
1834         brender_active = 0;
1835         stop_brender();
1836 }
1837
1838 void MWindow::restart_brender()
1839 {
1840 //printf("MWindow::restart_brender 1\n");
1841         if( !brender_active || !preferences->use_brender ) return;
1842         if( !brender ) return;
1843         brender->restart(edl);
1844 }
1845
1846 void MWindow::stop_brender()
1847 {
1848         if( !brender ) return;
1849 // cannot be holding mwindow->gui display lock
1850         brender->stop();
1851 }
1852
1853 int MWindow::brender_available(int position)
1854 {
1855         int result = 0;
1856         brender_lock->lock("MWindow::brender_available 1");
1857         if(brender && brender_active)
1858         {
1859                 if(brender->map_valid)
1860                 {
1861                         brender->map_lock->lock("MWindow::brender_available 2");
1862                         if(position < brender->map_size &&
1863                                 position >= 0)
1864                         {
1865 //printf("MWindow::brender_available 1 %d %d\n", position, brender->map[position]);
1866                                 if(brender->map[position] == BRender::RENDERED)
1867                                         result = 1;
1868                         }
1869                         brender->map_lock->unlock();
1870                 }
1871         }
1872         brender_lock->unlock();
1873         return result;
1874 }
1875
1876 void MWindow::set_brender_active(int v, int update)
1877 {
1878         if( !preferences->use_brender ) v = 0;
1879         brender_active = v;
1880         gui->mainmenu->brender_active->set_checked(v);
1881         if( v != 0 ) {
1882                 edl->session->brender_start = edl->local_session->get_selectionstart(1);
1883                 edl->session->brender_end = edl->local_session->get_selectionend(1);
1884
1885                 if(EQUIV(edl->session->brender_end, edl->session->brender_start)) {
1886                         edl->session->brender_end = edl->tracks->total_video_length();
1887                 }
1888                 restart_brender();
1889         }
1890         else {
1891                 edl->session->brender_start = edl->session->brender_end = 0;
1892                 gui->unlock_window();
1893                 stop_brender();
1894                 gui->lock_window("MWindow::set_brender_active");
1895         }
1896         if( update ) {
1897                 gui->update_timebar(0);
1898                 gui->draw_overlays(1);
1899         }
1900 }
1901
1902 int MWindow::has_commercials()
1903 {
1904 #ifdef HAVE_COMMERCIAL
1905         return theme->use_commercials;
1906 #else
1907         return 0;
1908 #endif
1909 }
1910
1911 void MWindow::init_commercials()
1912 {
1913 #ifdef HAVE_COMMERCIAL
1914         if( !commercials ) {
1915                 commercials = new Commercials(this);
1916                 commercial_active = 0;
1917         }
1918         else
1919                 commercials->add_user();
1920 #endif
1921 }
1922
1923 void MWindow::commit_commercial()
1924 {
1925 #ifdef HAVE_COMMERCIAL
1926         if( !commercial_active ) return;
1927         commercial_active = 0;
1928         if( !commercials ) return;
1929         commercials->commitDb();
1930 #endif
1931 }
1932
1933 void MWindow::undo_commercial()
1934 {
1935 #ifdef HAVE_COMMERCIAL
1936         if( !commercial_active ) return;
1937         commercial_active = 0;
1938         if( !commercials ) return;
1939         commercials->undoDb();
1940 #endif
1941 }
1942
1943 int MWindow::put_commercial()
1944 {
1945         int result = 0;
1946 #ifdef HAVE_COMMERCIAL
1947         double start = edl->local_session->get_selectionstart();
1948         double end = edl->local_session->get_selectionend();
1949         if( start >= end ) return 0;
1950
1951         const char *errmsg = 0;
1952         int count = 0;
1953         Tracks *tracks = edl->tracks;
1954         //check it
1955         for(Track *track=tracks->first; track && !errmsg; track=track->next) {
1956                 if( track->data_type != TRACK_VIDEO ) continue;
1957                 if( !track->armed ) continue;
1958                 if( count > 0 ) { errmsg = _("multiple video tracks"); break; }
1959                 ++count;
1960                 int64_t units_start = track->to_units(start,0);
1961                 int64_t units_end = track->to_units(end,0);
1962                 Edits *edits = track->edits;
1963                 Edit *edit1 = edits->editof(units_start, PLAY_FORWARD, 0);
1964                 Edit *edit2 = edits->editof(units_end, PLAY_FORWARD, 0);
1965                 if(!edit1 && !edit2) continue;  // nothing selected
1966                 if(!edit2) {                    // edit2 beyond end of track
1967                         edit2 = edits->last;
1968                         units_end = edits->length();
1969                 }
1970                 if(edit1 != edit2) { errmsg = _("crosses edits"); break; }
1971                 Indexable *indexable = edit1->get_source();
1972                 if( !indexable->is_asset ) { errmsg = _("not asset"); break; }
1973         }
1974         //run it
1975         for(Track *track=tracks->first; track && !errmsg; track=track->next) {
1976                 if( track->data_type != TRACK_VIDEO ) continue;
1977                 if( !track->armed ) continue;
1978                 int64_t units_start = track->to_units(start,0);
1979                 int64_t units_end = track->to_units(end,0);
1980                 Edits *edits = track->edits;
1981                 Edit *edit1 = edits->editof(units_start, PLAY_FORWARD, 0);
1982                 Edit *edit2 = edits->editof(units_end, PLAY_FORWARD, 0);
1983                 if(!edit1 && !edit2) continue;  // nothing selected
1984                 if(!edit2) {                    // edit2 beyond end of track
1985                         edit2 = edits->last;
1986                         units_end = edits->length();
1987                 }
1988                 Indexable *indexable = edit1->get_source();
1989                 Asset *asset = (Asset *)indexable;
1990                 File *file = video_cache->check_out(asset, edl);
1991                 if( !file ) { errmsg = _("no file"); break; }
1992                 int64_t edit_length = units_end - units_start;
1993                 int64_t edit_start = units_start - edit1->startproject + edit1->startsource;
1994                 result = commercials->put_clip(file, edit1->channel,
1995                         track->from_units(edit_start), track->from_units(edit_length));
1996                 video_cache->check_in(asset);
1997                 if( result ) { errmsg = _("db failed"); break; }
1998         }
1999         if( errmsg ) {
2000                 char string[BCTEXTLEN];
2001                 sprintf(string, _("put_commercial: %s"), errmsg);
2002                 MainError::show_error(string);
2003                 undo_commercial();
2004                 result = 1;
2005         }
2006 #endif
2007         return result;
2008 }
2009
2010 void MWindow::stop_playback(int wait)
2011 {
2012         gui->stop_drawing();
2013
2014         cwindow->stop_playback(wait);
2015
2016         for(int i = 0; i < vwindows.size(); i++) {
2017                 VWindow *vwindow = vwindows[i];
2018                 if( !vwindow->is_running() ) continue;
2019                 vwindow->stop_playback(wait);
2020         }
2021         for(int i = 0; i < zwindows.size(); i++) {
2022                 ZWindow *zwindow = zwindows[i];
2023                 if( zwindow->idx < 0 ) continue;
2024                 zwindow->stop_playback(wait);
2025         }
2026 }
2027
2028 void MWindow::stop_transport()
2029 {
2030         gui->stop_transport(gui->get_window_lock() ? "MWindow::stop_transport" : 0);
2031 }
2032
2033 void MWindow::undo_before(const char *description, void *creator)
2034 {
2035         undo->update_undo_before(description, creator);
2036 }
2037
2038 void MWindow::undo_after(const char *description, uint32_t load_flags, int changes_made)
2039 {
2040         undo->update_undo_after(description, load_flags, changes_made);
2041 }
2042
2043 void MWindow::beep(double freq, double secs, double gain)
2044 {
2045         if( !beeper ) beeper = new Beeper(this);
2046         beeper->tone(freq, secs, gain);
2047 }
2048
2049 Beeper::Beeper(MWindow *mwindow)
2050  : Thread(1, 0, 0)
2051 {
2052         this->mwindow = mwindow;
2053         audio = new AudioDevice(mwindow);
2054         playing_audio = 0;
2055         interrupted = -1;
2056 }
2057
2058 Beeper::~Beeper()
2059 {
2060         stop(0);
2061         delete audio;
2062 }
2063
2064 void Beeper::run()
2065 {
2066         int channels = 2;
2067         int64_t bfrsz = BEEP_SAMPLE_RATE;
2068         EDL *edl = mwindow->edl;
2069         EDLSession *session = edl->session;
2070         AudioOutConfig *aconfig = session->playback_config->aconfig;
2071         audio->open_output(aconfig, BEEP_SAMPLE_RATE, bfrsz, channels, 0);
2072         audio->start_playback();
2073
2074         double out0[bfrsz], out1[bfrsz], *out[2] = { out0, out1 };
2075         const double two_pi = 2*M_PI;
2076         int64_t audio_len = BEEP_SAMPLE_RATE * secs;
2077         const double dt = two_pi * freq/BEEP_SAMPLE_RATE;
2078         double th = 0;
2079
2080         audio_pos = 0;
2081         playing_audio = 1;
2082         while( !interrupted ) {
2083                 int len = audio_len - audio_pos;
2084                 if( len <= 0 ) break;
2085                 if( len > bfrsz ) len = bfrsz;
2086                 int k = audio_pos;
2087                 for( int i=0; i<len; ++i,++k,th+=dt ) {
2088                         double t = th - two_pi;
2089                         if( t >= 0 ) th = t;
2090                         out0[i] = out1[i] = sin(th) * gain;
2091                 }
2092                 audio->write_buffer(out, channels, len);
2093                 audio_pos = k;
2094         }
2095
2096         if( !interrupted )
2097                 audio->set_last_buffer();
2098         audio->stop_audio(interrupted ? 0 : 1);
2099         playing_audio = 0;
2100
2101         audio->close_all();
2102 }
2103
2104 void Beeper::start()
2105 {
2106         if( running() ) return;
2107         audio_pos = -1;
2108         interrupted = 0;
2109         Thread::start();
2110 }
2111
2112 void Beeper::stop(int wait)
2113 {
2114         if( running() && !interrupted ) {
2115                 interrupted = 1;
2116                 audio->stop_audio(wait);
2117         }
2118         Thread::join();
2119 }
2120
2121 void Beeper::tone(double freq, double secs, double gain)
2122 {
2123         stop(0);
2124         this->freq = freq;
2125         this->secs = secs;
2126         this->gain = gain;
2127         start();
2128 }
2129
2130
2131 int MWindow::load_filenames(ArrayList<char*> *filenames,
2132                 int load_mode, int edl_mode, int update_filename)
2133 {
2134         ArrayList<EDL*> new_edls;
2135         ArrayList<Asset*> new_assets;
2136         ArrayList<File*> new_files;
2137
2138 //      save_defaults();
2139         gui->start_hourglass();
2140
2141 // Need to stop playback since tracking depends on the EDL not getting
2142 // deleted.
2143         gui->unlock_window();
2144         stop_playback(1);
2145         gui->lock_window("MWindow::load_filenames 0");
2146         undo_before();
2147
2148 const int debug = 0;
2149 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2150
2151 // Define new_edls and new_assets to load
2152         int result = 0, ftype = -1;
2153
2154         for( int i=0; i<filenames->size(); ++i ) {
2155 // Get type of file
2156                 File *new_file = new File;
2157                 Asset *new_asset = new Asset(filenames->get(i));
2158                 EDL *new_edl = new EDL;
2159                 new_edl->create_objects();
2160                 new_edl->copy_session(edl, -1);
2161                 new_file->set_program(edl->session->program_no);
2162
2163                 char string[BCTEXTLEN];
2164                 sprintf(string, _("Loading %s"), new_asset->path);
2165                 gui->show_message(string);
2166
2167                 ftype = new_file->open_file(preferences, new_asset, 1, 0);
2168
2169                 result = 1;
2170                 switch( ftype ) {
2171 // Convert media file to EDL
2172                 case FILE_OK: {
2173 // Warn about odd image dimensions
2174                         if( new_asset->video_data &&
2175                             ((new_asset->width % 2) || (new_asset->height % 2)) ) {
2176                                 eprintf(_("%s's resolution is %dx%d.\n"
2177                                         "Images with odd dimensions may not decode properly."),
2178                                         new_asset->path, new_asset->width, new_asset->height);
2179                         }
2180
2181                         if( new_asset->program >= 0 &&
2182                             edl->session->program_no != new_asset->program ) {
2183                                 eprintf(_("%s's index was built for program number %d\n"
2184                                         "Playback preference is %d.\n  Using program %d."),
2185                                         new_asset->path, new_asset->program,
2186                                         edl->session->program_no, new_asset->program);
2187                         }
2188
2189                         if( load_mode == LOADMODE_RESOURCESONLY ) {
2190                                 new_assets.append(new_asset);
2191                                 new_asset->add_user();
2192                                 result = 0;
2193                                 break;
2194                         }
2195
2196                         RecordLabels *labels = edl->session->label_cells ?
2197                                 new RecordLabels(new_file) : 0;
2198                         asset_to_edl(new_edl, new_asset, labels);
2199                         new_edls.append(new_edl);
2200                         new_edl->add_user();
2201                         delete labels;
2202
2203                         if( load_mode == LOADMODE_REPLACE ||
2204                             load_mode == LOADMODE_REPLACE_CONCATENATE ) {
2205 // Set filename to nothing for assets since save EDL would overwrite them.
2206                                 set_filename("");
2207 // Reset timeline position
2208                                 for( int i=0; i<TOTAL_PANES; ++i ) {
2209                                         new_edl->local_session->view_start[i] = 0;
2210                                         new_edl->local_session->track_start[i] = 0;
2211                                 }
2212                         }
2213                         result = 0;
2214                         break; }
2215
2216                 case FILE_NOT_FOUND: {
2217                         eprintf(_("Failed to open %s"), new_asset->path);
2218                         sprintf(string, _("Failed to open %s"), new_asset->path);
2219                         gui->show_message(string, theme->message_error);
2220                         gui->update_default_message();
2221                         break; }
2222
2223 // Unknown format
2224                 case FILE_UNRECOGNIZED_CODEC: {
2225 // Test index file
2226                         { IndexFile indexfile(this, new_asset);
2227                         if( !(result = indexfile.open_index()) )
2228                                 indexfile.close_index(); }
2229
2230 // Test existing EDLs
2231                         for( int j=0; result && j<new_edls.total; ++j ) {
2232                                 Asset *old_asset = new_edls[j]->assets->get_asset(new_asset->path);
2233                                 if( old_asset ) {
2234                                         new_asset->copy_from(old_asset,1);
2235                                         result = 0;
2236                                 }
2237                         }
2238                         if( result ) {
2239                                 Asset *old_asset = edl->assets->get_asset(new_asset->path);
2240                                 if( old_asset ) {
2241                                         new_asset->copy_from(old_asset,1);
2242                                         result = 0;
2243                                 }
2244                         }
2245
2246 // Prompt user
2247                         if( result ) {
2248                                 new_asset->audio_data = 1;
2249                                 new_asset->format = FILE_PCM;
2250                                 new_asset->channels = defaults->get("AUDIO_CHANNELS", 2);
2251                                 new_asset->sample_rate = defaults->get("SAMPLE_RATE", 44100);
2252                                 new_asset->bits = defaults->get("AUDIO_BITS", 16);
2253                                 new_asset->byte_order = defaults->get("BYTE_ORDER", 1);
2254                                 new_asset->signed_ = defaults->get("SIGNED_", 1);
2255                                 new_asset->header = defaults->get("HEADER", 0);
2256
2257                                 FileSystem fs;
2258                                 fs.extract_name(string, new_asset->path);
2259                                 strcat(string, _("'s format couldn't be determined."));
2260                                 FileFormat fwindow(this);
2261                                 fwindow.create_objects(new_asset, string);
2262                                 result = fwindow.run_window();
2263
2264                                 defaults->update("AUDIO_CHANNELS", new_asset->channels);
2265                                 defaults->update("SAMPLE_RATE", new_asset->sample_rate);
2266                                 defaults->update("AUDIO_BITS", new_asset->bits);
2267                                 defaults->update("BYTE_ORDER", new_asset->byte_order);
2268                                 defaults->update("SIGNED_", new_asset->signed_);
2269                                 defaults->update("HEADER", new_asset->header);
2270                                 save_defaults();
2271                         }
2272 // Append to list
2273                         if( !result ) {
2274 // Recalculate length
2275                                 delete new_file;
2276                                 new_file = new File;
2277                                 result = new_file->open_file(preferences, new_asset, 1, 0);
2278
2279                                 if( load_mode != LOADMODE_RESOURCESONLY ) {
2280                                         RecordLabels *labels = edl->session->label_cells ?
2281                                                 new RecordLabels(new_file) : 0;
2282                                         asset_to_edl(new_edl, new_asset, labels);
2283                                         new_edls.append(new_edl);
2284                                         new_edl->add_user();
2285                                         delete labels;
2286                                 }
2287                                 else {
2288                                         new_assets.append(new_asset);
2289                                         new_asset->add_user();
2290                                 }
2291                         }
2292                         break; }
2293
2294                 case FILE_IS_XML: {
2295                         FileXML xml_file;
2296                         const char *filename = filenames->get(i);
2297                         if( xml_file.read_from_file(filename, 1) ) {
2298                                 eprintf(_("Error: unable to open:\n  %s"), filename);
2299                                 break;
2300                         }
2301                         const char *cin_version = 0;
2302                         while( !xml_file.read_tag() ) {
2303                                 if( xml_file.tag.title_is("EDL") ) {
2304                                         cin_version = xml_file.tag.get_property("VERSION");
2305                                         break;
2306                                 }
2307                         }
2308                         xml_file.rewind();
2309                         if( !cin_version ) {
2310                                 eprintf(_("XML file %s\n not from cinelerra."),filename);
2311                                 char string[BCTEXTLEN];
2312                                 sprintf(string,_("Unknown %s"), filename);
2313                                 gui->show_message(string);
2314                                 break;
2315                         }
2316                         if( strcmp(cin_version, CINELERRA_VERSION) &&
2317                             strcmp(cin_version, "Unify") &&
2318                             strcmp(cin_version, "5.1") ) {
2319                                 eprintf(_("Warning: XML from cinelerra version %s\n"
2320                                         "Session data may be incompatible."), cin_version);
2321                         }
2322                         if( new_edl->load_xml(&xml_file, LOAD_ALL) ) {
2323                                 eprintf(_("Error: unable to load:\n  %s"), filename);
2324                                 break;
2325                         }
2326                         test_plugins(new_edl, filename);
2327                         int groups = new_edl->regroup(session->group_number);
2328                         session->group_number += groups;
2329                         switch( edl_mode ) {
2330                         case LOADMODE_EDL_CLIP: {
2331                                 strcpy(new_edl->local_session->clip_title,
2332                                         filenames->get(i));
2333                                 struct stat st;
2334                                 time_t t = !stat(filenames->get(i),&st) ?
2335                                         st.st_mtime : time(&t);
2336                                 ctime_r(&t, new_edl->local_session->clip_notes);
2337                                 switch( load_mode ) {
2338                                 case LOADMODE_REPLACE:
2339                                 case LOADMODE_REPLACE_CONCATENATE:
2340                                         strcpy(session->filename, filename);
2341                                         if( update_filename ) set_filename(filename);
2342                                         break;
2343                                 }
2344                                 result = 0;
2345                                 break; }
2346                         case LOADMODE_EDL_NESTED: {
2347                                 EDL *nested_edl = new EDL;
2348                                 nested_edl->create_objects();
2349                                 nested_edl->copy_session(edl, -1);
2350                                 nested_edl->create_nested(new_edl);
2351                                 nested_edl->set_path(filename);
2352                                 new_edl->remove_user();
2353                                 new_edl = nested_edl;
2354                                 result = 0;
2355                                 break; }
2356                         case LOADMODE_EDL_FILEREF: {
2357                                 result = create_ref(new_asset, new_edl);
2358                                 if( result ) break;
2359                                 new_assets.append(new_asset);
2360                                 new_asset->add_user();
2361                                 new_edl->remove_user();
2362                                 new_edl = new EDL;
2363                                 new_edl->create_objects();
2364                                 new_edl->copy_session(edl, -1);
2365                                 asset_to_edl(new_edl, new_asset);
2366                                 delete new_file;
2367                                 new_file = new File;
2368                                 result = new_file->open_file(preferences, new_asset, 1, 0);
2369                                 break; }
2370                         }
2371                         if( !result ) {
2372                                 new_edls.append(new_edl);
2373                                 new_edl->add_user();
2374                         }
2375                         else
2376                                 eprintf(_("Error: Unable to load xml:\n  %s"), new_asset->path);
2377                         break; }
2378                 }
2379
2380                 new_edl->Garbage::remove_user();
2381                 new_asset->Garbage::remove_user();
2382
2383 // Store for testing index
2384                 new_files.append(new_file);
2385         }
2386
2387 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2388
2389         if(!result) {
2390                 gui->reset_default_message();
2391                 gui->default_message();
2392         }
2393
2394 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2395
2396 // Paste them.
2397 // Don't back up here.
2398         if( new_edls.size() ) {
2399 // For pasting, clear the active region
2400                 if( load_mode == LOADMODE_PASTE ) {
2401                         double start = edl->local_session->get_selectionstart();
2402                         double end = edl->local_session->get_selectionend();
2403                         if(!EQUIV(start, end))
2404                                 edl->clear(start, end,
2405                                         edl->session->labels_follow_edits,
2406                                         edl->session->plugins_follow_edits,
2407                                         edl->session->autos_follow_edits);
2408
2409                         paste_edls(&new_edls, load_mode, 0, -1,
2410                                 edl->session->labels_follow_edits,
2411                                 edl->session->plugins_follow_edits,
2412                                 edl->session->autos_follow_edits,
2413                                 0); // overwrite
2414                 }
2415                 else if( load_mode == LOADMODE_NEW_TRACKS &&
2416                          edl_mode != LOADMODE_EDL_CLIP )
2417                         paste_edls(&new_edls, load_mode, 0, -1, 0, 0, 0, 0);
2418                 else if( load_mode != LOADMODE_RESOURCESONLY ||
2419                          edl_mode == LOADMODE_EDL_CLIP )
2420                         paste_edls(&new_edls, load_mode, 0, -1, 1, 1, 1, 0);
2421                 else
2422                         paste_edls(&new_edls, LOADMODE_NOTHING, 0, -1, 0, 0, 0, 0);
2423
2424         }
2425
2426 // Add new assets to EDL and schedule assets for index building.
2427         int got_indexes = 0;
2428         for( int i=0; i<new_edls.size(); ++i ) {
2429                 EDL *new_edl = new_edls[i];
2430                 for( int j=0; j<new_edl->nested_edls.size(); ++j ) {
2431                         mainindexes->add_indexable(new_edl->nested_edls[j]);
2432                         edl->nested_edls.update_index(new_edl->nested_edls[j]);
2433                         got_indexes = 1;
2434                 }
2435
2436         }
2437
2438         for( int i=0; i<new_assets.size(); ++i ) {
2439                 Asset *new_asset = new_assets[i];
2440                 mainindexes->add_indexable(new_asset);
2441                 edl->assets->update(new_asset);
2442                 got_indexes = 1;
2443         }
2444
2445 // Start examining next batch of index files
2446         if(got_indexes) mainindexes->start_build();
2447
2448 // reload renderengine edl or the plugin guis will flip out
2449         sync_parameters(CHANGE_ALL);
2450 // Open plugin GUIs
2451         show_plugins();
2452
2453         // opening new session
2454         if( ( load_mode == LOADMODE_REPLACE ||
2455               load_mode == LOADMODE_REPLACE_CONCATENATE ) &&
2456             (ftype != FILE_IS_XML || edl_mode != LOADMODE_EDL_CLIP) ) {
2457                 select_asset(0, 0);
2458                 edl->session->proxy_state = PROXY_INACTIVE;
2459                 edl->session->proxy_scale = 1;
2460                 edl->session->proxy_disabled_scale = 1;
2461                 edl->session->proxy_use_scaler = 0;
2462                 edl->session->proxy_auto_scale = 0;
2463                 edl->session->proxy_beep = 0;
2464                 edl->local_session->preview_start = 0;
2465                 edl->local_session->preview_end = -1;
2466                 edl->local_session->loop_playback = 0;
2467                 edl->local_session->set_selectionstart(0);
2468                 edl->local_session->set_selectionend(0);
2469                 edl->local_session->unset_inpoint();
2470                 edl->local_session-> unset_outpoint();
2471                 set_brender_active(0, 0);
2472                 fit_selection();
2473                 goto_start();
2474         }
2475
2476         if( edl->session->proxy_state != PROXY_INACTIVE && edl->session->proxy_auto_scale &&
2477             load_mode != LOADMODE_REPLACE && load_mode != LOADMODE_REPLACE_CONCATENATE ) {
2478                 ArrayList<Indexable *> orig_idxbls;
2479                 for( int i=0; i<new_assets.size(); ++i )
2480                         orig_idxbls.append(new_assets.get(i));
2481                 for( int i=0; i<new_edls.size(); ++i ) {
2482                         EDL *new_edl = new_edls[i];
2483                         for( Track *track=new_edl->tracks->first; track; track=track->next ) {
2484                                 if( track->data_type != TRACK_VIDEO ) continue;
2485                                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
2486                                         Indexable *idxbl = (Indexable *)edit->asset;
2487                                         if( !idxbl ) continue;
2488                                         if( !idxbl->have_video() ) continue;
2489                                         if( edit->channel != 0 ) continue; // first layer only
2490                                         orig_idxbls.append(edit->asset);
2491                                 }
2492                         }
2493                 }
2494                 gui->unlock_window(); // to update progress bar
2495                 int ret = render_proxy(orig_idxbls);
2496                 gui->lock_window("MWindow::load_filenames");
2497                 float gain = edl->session->proxy_beep;
2498                 if( ret >= 0 && gain > 0 ) {
2499                         if( ret > 0 )
2500                                 beep(2000., 1.5, gain);
2501                         else
2502                                 beep(4000., 0.25, gain);
2503                 }
2504         }
2505
2506 // need to update undo before project, since mwindow is unlocked & a new load
2507 // can begin here.  Should really prevent loading until we're done.
2508 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2509         undo_after(_("load"), LOAD_ALL);
2510
2511         for(int i = 0; i < new_edls.size(); i++)
2512         {
2513                 new_edls[i]->remove_user();
2514         }
2515 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2516
2517         new_edls.remove_all();
2518
2519         for(int i = 0; i < new_assets.size(); i++)
2520         {
2521                 new_assets[i]->Garbage::remove_user();
2522         }
2523
2524         new_assets.remove_all();
2525         new_files.remove_all_objects();
2526
2527
2528 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2529         if(load_mode == LOADMODE_REPLACE ||
2530                 load_mode == LOADMODE_REPLACE_CONCATENATE)
2531         {
2532                 session->changes_made = 0;
2533         }
2534         else
2535         {
2536                 session->changes_made = 1;
2537         }
2538
2539         gui->stop_hourglass();
2540
2541
2542 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2543         update_project(load_mode);
2544
2545 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2546
2547         return 0;
2548 }
2549
2550 int MWindow::render_proxy(ArrayList<Indexable *> &new_idxbls)
2551 {
2552         Asset *format_asset = new Asset;
2553         format_asset->format = FILE_FFMPEG;
2554         format_asset->load_defaults(defaults, "PROXY_", 1, 1, 0, 0, 0);
2555         int new_scale = edl->session->proxy_scale;
2556         ProxyRender proxy_render(this, format_asset, new_scale);
2557
2558         for( int i=0; i<new_idxbls.size(); ++i ) {
2559                 Indexable *orig = new_idxbls.get(i);
2560                 Asset *proxy = proxy_render.add_original(orig, new_scale);
2561                 if( !proxy ) continue;
2562                 FileSystem fs;
2563                 int exists = fs.get_size(proxy->path) > 0 ? 1 : 0;
2564                 int got_it = exists && // if proxy exists, and is newer than orig
2565                     fs.get_date(proxy->path) > fs.get_date(orig->path) ? 1 : 0;
2566                 if( got_it ) continue;
2567                 proxy_render.add_needed(orig, proxy);
2568         }
2569
2570 // render needed proxies
2571         int result = proxy_render.create_needed_proxies(new_scale);
2572         if( !result ) {
2573                 add_proxy(&proxy_render.orig_idxbls, &proxy_render.orig_proxies);
2574         }
2575         format_asset->remove_user();
2576         return !result ? proxy_render.needed_proxies.size() : -1;
2577 }
2578
2579 int MWindow::enable_proxy()
2580 {
2581         int ret = 0;
2582         if( edl->session->proxy_state == PROXY_DISABLED ) {
2583                 int new_scale = edl->session->proxy_disabled_scale;
2584                 int new_use_scaler = edl->session->proxy_use_scaler;
2585                 Asset *asset = new Asset;
2586                 asset->format = FILE_FFMPEG;
2587                 asset->load_defaults(defaults, "PROXY_", 1, 1, 0, 0, 0);
2588                 ret = to_proxy(asset, new_scale, new_use_scaler);
2589                 asset->remove_user();
2590                 if( ret > 0 ) {
2591                         float gain = edl->session->proxy_beep;
2592                         beep(2000., 1.5, gain);
2593                 }
2594                 edl->session->proxy_disabled_scale = 1;
2595                 edl->session->proxy_state = PROXY_ACTIVE;
2596                 gui->lock_window("MWindow::to_proxy");
2597                 update_project(LOADMODE_REPLACE);
2598                 gui->unlock_window();
2599         }
2600         return 1;
2601 }
2602
2603 int MWindow::disable_proxy()
2604 {
2605         if( edl->session->proxy_state == PROXY_ACTIVE ) {
2606                 int old_scale = edl->session->proxy_scale, new_scale = 0;
2607                 int new_use_scaler = edl->session->proxy_use_scaler;
2608                 Asset *asset = new Asset;
2609                 asset->format = FILE_FFMPEG;
2610                 asset->load_defaults(defaults, "PROXY_", 1, 1, 0, 0, 0);
2611                 to_proxy(asset, new_scale, new_use_scaler);
2612                 asset->remove_user();
2613                 edl->session->proxy_state = PROXY_DISABLED;
2614                 edl->session->proxy_disabled_scale = old_scale;
2615                 gui->lock_window("MWindow::to_proxy");
2616                 update_project(LOADMODE_REPLACE);
2617                 gui->unlock_window();
2618         }
2619         return 1;
2620 }
2621
2622 int MWindow::to_proxy(Asset *asset, int new_scale, int new_use_scaler)
2623 {
2624         ArrayList<Indexable*> orig_idxbls;
2625         ArrayList<Indexable*> proxy_assets;
2626
2627         edl->Garbage::add_user();
2628         save_backup();
2629         undo_before(_("proxy"), this);
2630         int asset_scale = !new_scale ? 0 :
2631                         !new_use_scaler ? 1 : new_scale;
2632         ProxyRender proxy_render(this, asset, asset_scale);
2633
2634 // revert project to original size from current size
2635 // remove all session proxy assets at the at the current proxy_scale
2636         int proxy_scale = edl->session->proxy_scale;
2637
2638         if( edl->session->proxy_state == PROXY_ACTIVE ) {
2639                 Asset *orig_asset = edl->assets->first;
2640                 for( ; orig_asset; orig_asset=orig_asset->next ) {
2641                         char new_path[BCTEXTLEN];
2642                         proxy_render.to_proxy_path(new_path, orig_asset, proxy_scale);
2643 // test if proxy asset was already added to proxy_assets
2644                         int got_it = 0;
2645                         for( int i = 0; !got_it && i<proxy_assets.size(); ++i )
2646                                 got_it = !strcmp(proxy_assets[i]->path, new_path);
2647                         if( got_it ) continue;
2648                         Asset *proxy_asset = edl->assets->get_asset(new_path);
2649                         if( !proxy_asset ) continue;
2650 // add pointer to existing EDL asset if it exists
2651 // EDL won't delete it unless it's the same pointer.
2652                         proxy_assets.append(proxy_asset);
2653                         proxy_asset->add_user();
2654                         orig_idxbls.append(orig_asset);
2655                         orig_asset->add_user();
2656                 }
2657                 for( int i=0,n=edl->nested_edls.size(); i<n; ++i ) {
2658                         EDL *orig_nested = edl->nested_edls[i];
2659                         char new_path[BCTEXTLEN];
2660                         proxy_render.to_proxy_path(new_path, orig_nested, proxy_scale);
2661 // test if proxy asset was already added to proxy_assets
2662                         int got_it = 0;
2663                         for( int i = 0; !got_it && i<proxy_assets.size(); ++i )
2664                                 got_it = !strcmp(proxy_assets[i]->path, new_path);
2665                         if( got_it ) continue;
2666                         Asset *proxy_nested = edl->assets->get_asset(new_path);
2667                         if( !proxy_nested ) continue;
2668 // add pointer to existing EDL asset if it exists
2669 // EDL won't delete it unless it's the same pointer.
2670                         proxy_assets.append(proxy_nested);
2671                         proxy_nested->add_user();
2672                         orig_idxbls.append(orig_nested);
2673                         orig_nested->add_user();
2674                 }
2675
2676 // convert from the proxy assets to the original assets
2677                 edl->set_proxy(0, 0, &proxy_assets, &orig_idxbls);
2678
2679 // remove the references
2680                 for( int i=0; i<proxy_assets.size(); ++i ) {
2681                         Asset *proxy = (Asset *) proxy_assets[i];
2682                         proxy->width = proxy->actual_width;
2683                         proxy->height = proxy->actual_height;
2684                         proxy->remove_user();
2685                         edl->assets->remove_pointer(proxy);
2686                         proxy->remove_user();
2687                 }
2688                 proxy_assets.remove_all();
2689                 for( int i = 0; i < orig_idxbls.size(); i++ )
2690                         orig_idxbls[i]->remove_user();
2691                 orig_idxbls.remove_all();
2692         }
2693
2694         ArrayList<char *> confirm_paths;    // test for new files
2695         confirm_paths.set_array_delete();
2696
2697 // convert to new size if not original size
2698         if( new_scale ) {
2699                 FileSystem fs;
2700                 Asset *orig = edl->assets->first;
2701                 for( ; orig; orig=orig->next ) {
2702                         Asset *proxy = proxy_render.add_original(orig, new_scale);
2703                         if( !proxy ) continue;
2704                         int exists = fs.get_size(proxy->path) > 0 ? 1 : 0;
2705                         int got_it = exists && // if proxy exists, and is newer than orig
2706                             fs.get_date(proxy->path) > fs.get_date(orig->path) ? 1 : 0;
2707                         if( !got_it ) {
2708                                 if( exists ) // prompt user to overwrite
2709                                         confirm_paths.append(cstrdup(proxy->path));
2710                                 proxy_render.add_needed(orig, proxy);
2711                         }
2712                 }
2713                 for( int i=0,n=edl->nested_edls.size(); i<n; ++i ) {
2714                         EDL *orig_nested = edl->nested_edls[i];
2715                         Asset *proxy = proxy_render.add_original(orig_nested, new_scale);
2716                         if( !proxy ) continue;
2717                         int exists = fs.get_size(proxy->path) > 0 ? 1 : 0;
2718                         int got_it = exists && // if proxy exists, and is newer than orig_nested
2719                             fs.get_date(proxy->path) > fs.get_date(orig_nested->path) ? 1 : 0;
2720                         if( !got_it ) {
2721                                 if( exists ) // prompt user to overwrite
2722                                         confirm_paths.append(cstrdup(proxy->path));
2723                                 proxy_render.add_needed(orig_nested, proxy);
2724                         }
2725                 }
2726                 edl->session->proxy_state = PROXY_ACTIVE;
2727         }
2728         else
2729                 edl->session->proxy_state = PROXY_INACTIVE;
2730
2731         int result = 0;
2732 // test for existing files
2733         if( confirm_paths.size() ) {
2734                 result = ConfirmSave::test_files(this, &confirm_paths);
2735                 confirm_paths.remove_all_objects();
2736         }
2737
2738         if( !result && new_scale )
2739                 result = proxy_render.create_needed_proxies(new_scale);
2740
2741         if( !result ) { // resize project
2742                 edl->set_proxy(new_scale, new_use_scaler,
2743                         &proxy_render.orig_idxbls, &proxy_render.orig_proxies);
2744         }
2745         undo_after(_("proxy"), LOAD_ALL);
2746         edl->Garbage::remove_user();
2747         restart_brender();
2748
2749         return !result ? proxy_render.needed_proxies.size() : -1;
2750 }
2751
2752 void MWindow::test_plugins(EDL *new_edl, const char *path)
2753 {
2754         char string[BCTEXTLEN];
2755
2756 // Do a check whether plugins exist
2757         for( Track *track=new_edl->tracks->first; track; track=track->next ) {
2758                 for( int k=0; k<track->plugin_set.total; ++k ) {
2759                         PluginSet *plugin_set = track->plugin_set[k];
2760                         for( Plugin *plugin = (Plugin*)plugin_set->first;
2761                                         plugin; plugin = (Plugin*)plugin->next ) {
2762                                 if( plugin->plugin_type != PLUGIN_STANDALONE ) continue;
2763 // ok we need to find it in plugindb
2764                                 PluginServer *server =
2765                                         scan_plugindb(plugin->title, track->data_type);
2766                                 if( !server || server->transition ) {
2767                                         sprintf(string,
2768         _("The %s '%s' in file '%s' is not part of your installation of Cinelerra.\n"
2769           "The project won't be rendered as it was meant and Cinelerra might crash.\n"),
2770                                                 "effect", _(plugin->title), path);
2771                                         MainError::show_error(string);
2772                                 }
2773                         }
2774                 }
2775
2776                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
2777                         if( !edit->transition ) continue;
2778 // ok we need to find transition in plugindb
2779                         PluginServer *server =
2780                                 scan_plugindb(edit->transition->title, track->data_type);
2781                         if( !server || !server->transition ) {
2782                                 sprintf(string,
2783         _("The %s '%s' in file '%s' is not part of your installation of Cinelerra.\n"
2784           "The project won't be rendered as it was meant and Cinelerra might crash.\n"),
2785                                         "transition", _(edit->transition->title), path);
2786                                 MainError::show_error(string);
2787                         }
2788                 }
2789         }
2790 }
2791
2792
2793 void MWindow::init_shm(const char *pfn, int64_t min)
2794 {
2795         int64_t result = 0;
2796 // Fix shared memory
2797         FILE *fd = fopen(pfn, "r");
2798         if( fd ) {
2799                 fscanf(fd, "%jd", &result);
2800                 fclose(fd);
2801                 if( result >= min ) return;
2802         }
2803
2804         fd = fopen(pfn, "w");
2805         if( !fd ) return;
2806         fprintf(fd, "0x%jx", min);
2807         fclose(fd);
2808
2809         fd = fopen(pfn, "r");
2810         if( !fd ) {
2811                 eprintf(_("MWindow::init_shm: couldn't open %s for reading.\n"), pfn);
2812                 return;
2813         }
2814
2815         fscanf(fd, "%jd", &result);
2816         fclose(fd);
2817         if( result < min ) {
2818                 eprintf(_("MWindow::init_shm: %s is %p.\n"
2819                         "you probably need to be root, or:\n"
2820                         "as root, run: echo 0x%jx > %s\n"
2821                         "before trying to start cinelerra.\n"
2822                         "It should be at least 0x%jx for Cinelerra.\n"),
2823                         pfn, (void *)result, min, pfn, min);
2824         }
2825 }
2826
2827 void MWindow::create_objects(int want_gui,
2828         int want_new,
2829         char *config_path)
2830 {
2831         const int debug = 0;
2832         if(debug) PRINT_TRACE
2833
2834 // For some reason, init_signals must come after show_splash or the signals won't
2835 // get trapped.
2836         init_signals();
2837         if(debug) PRINT_TRACE
2838         init_3d();
2839
2840         if(debug) PRINT_TRACE
2841
2842         if(debug) PRINT_TRACE
2843         default_standard = default_std();
2844         init_defaults(defaults, config_path);
2845         check_language();
2846         show_splash();
2847         init_preferences();
2848         if(splash_window)
2849                 splash_window->update_status(_("Initializing Plugins"));
2850         init_plugins(this, preferences);
2851         if(debug) PRINT_TRACE
2852         init_ladspa_plugins(this, preferences);
2853         if(debug) PRINT_TRACE
2854         init_plugin_tips(*plugindb, cin_lang);
2855         if(splash_window)
2856                 splash_window->update_status(_("Initializing GUI"));
2857         if(debug) PRINT_TRACE
2858         init_theme();
2859
2860         if(debug) PRINT_TRACE
2861         init_error();
2862
2863         if(splash_window)
2864                 splash_window->update_status(_("Initializing Fonts"));
2865         char string[BCTEXTLEN];
2866         strcpy(string, preferences->plugin_dir);
2867         strcat(string, "/" FONT_SEARCHPATH);
2868         BC_Resources::init_fontconfig(string);
2869         if(debug) PRINT_TRACE
2870 // use if plugged
2871         init_x10tv();
2872         if( !x10tv )
2873                 init_wintv();
2874
2875 // Default project created here
2876         init_edl();
2877         if(debug) PRINT_TRACE
2878
2879         init_cache();
2880         if(debug) PRINT_TRACE
2881
2882         Timer timer;
2883
2884         init_awindow();
2885         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2886
2887         init_compositor();
2888         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2889
2890 //printf("MWindow::create_objects %d session->show_vwindow=%d\n", __LINE__, session->show_vwindow);
2891         if(session->show_vwindow)
2892                 get_viewer(1, DEFAULT_VWINDOW);
2893         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2894
2895         init_gui();
2896         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2897
2898         init_levelwindow();
2899         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2900
2901         init_indexes();
2902         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2903
2904         init_channeldb();
2905         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2906
2907         init_gwindow();
2908         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2909
2910         init_render();
2911         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2912
2913         init_shuttle();
2914         init_brender();
2915         init_exportedl();
2916         init_commercials();
2917         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2918         mainprogress = new MainProgress(this, gui);
2919         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2920         undo = new MainUndo(this);
2921
2922         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2923
2924         plugin_guis = new PluginGUIs(this);
2925         dead_plugins = new ArrayList<PluginServer*>;
2926         keyframe_threads = new ArrayList<KeyFrameThread*>;
2927
2928         if(debug) printf("MWindow::create_objects %d vwindows=%d show_vwindow=%d\n",
2929                 __LINE__,
2930                 vwindows.size(),
2931                 session->show_vwindow);
2932
2933 // Show all vwindows
2934 //      if(session->show_vwindow) {
2935 //              for(int j = 0; j < vwindows.size(); j++) {
2936 //                      VWindow *vwindow = vwindows[j];
2937 //                      if( !vwindow->is_running() ) continue;
2938 //                      if(debug) printf("MWindow::create_objects %d vwindow=%p\n",
2939 //                              __LINE__,
2940 //                              vwindow);
2941 //                      if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2942 //                      vwindow->gui->lock_window("MWindow::create_objects 1");
2943 //                      if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2944 //                      vwindow->gui->show_window();
2945 //                      if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2946 //                      vwindow->gui->unlock_window();
2947 //              }
2948 //      }
2949
2950
2951         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2952
2953         if(session->show_cwindow)
2954         {
2955                 cwindow->gui->lock_window("MWindow::create_objects 1");
2956                 cwindow->gui->show_window();
2957                 cwindow->gui->unlock_window();
2958         }
2959
2960         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2961         if(session->show_awindow)
2962         {
2963                 awindow->gui->lock_window("MWindow::create_objects 1");
2964                 awindow->gui->show_window();
2965                 awindow->gui->unlock_window();
2966         }
2967
2968         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2969         if(session->show_lwindow)
2970         {
2971                 lwindow->gui->lock_window("MWindow::create_objects 1");
2972                 lwindow->gui->show_window();
2973                 lwindow->gui->unlock_window();
2974         }
2975
2976         if(debug) printf("MWindow::create_objects %d total_time=%d gwindow=%p\n",
2977                 __LINE__,
2978                 (int)timer.get_difference(),
2979                 gwindow->gui);
2980         if(session->show_gwindow)
2981         {
2982                 gwindow->gui->lock_window("MWindow::create_objects 1");
2983                 gwindow->gui->show_window();
2984                 gwindow->gui->unlock_window();
2985         }
2986
2987         if(debug) PRINT_TRACE
2988
2989         gui->lock_window("MWindow::create_objects 1");
2990         gui->mainmenu->load_defaults(defaults);
2991         gui->mainmenu->update_toggles(0);
2992         gui->update_patchbay();
2993         gui->draw_canvas(0, 0);
2994         gui->draw_cursor(1);
2995         gui->show_window();
2996         gui->raise_window();
2997         gui->unlock_window();
2998         cwindow->gui->lock_window("MWindow::create_objects 1");
2999         cwindow->gui->tool_panel->raise_tool();
3000         cwindow->gui->unlock_window();
3001         if(debug) PRINT_TRACE
3002
3003         if(preferences->use_tipwindow)
3004                 init_tipwindow();
3005         if(debug) PRINT_TRACE
3006
3007         gui->add_keyboard_listener(
3008                 (int (BC_WindowBase::*)(BC_WindowBase *))
3009                 &MWindowGUI::keyboard_listener);
3010
3011         hide_splash();
3012         init_shm("/proc/sys/kernel/shmmax", 0x7fffffff);
3013         init_shm("/proc/sys/kernel/shmmni", 0x4000);
3014         if(debug) PRINT_TRACE
3015
3016         BC_WindowBase::get_resources()->vframe_shm = 1;
3017 }
3018
3019 int MWindow::uses_opengl()
3020 {
3021         if( !playback_3d || !playback_3d->running() ) return 0;
3022         PlaybackConfig *playback_config = edl->session->playback_config;
3023         return playback_config->vconfig->driver == PLAYBACK_X11_GL ? 1 : 0;
3024 }
3025
3026 void MWindow::show_splash()
3027 {
3028 #include "data/heroine_logo9_png.h"
3029         VFrame *frame = new VFramePng(heroine_logo9_png);
3030         BC_DisplayInfo dpyi;
3031         int rw = dpyi.get_root_w(), rh = dpyi.get_root_h();
3032         int rr = (rw < rh ? rw : rh) / 4;
3033         splash_window = new SplashGUI(frame, rr, rr);
3034         splash_window->create_objects();
3035 }
3036
3037 void MWindow::hide_splash()
3038 {
3039         if(splash_window)
3040                 delete splash_window;
3041         splash_window = 0;
3042 }
3043
3044
3045 void MWindow::start()
3046 {
3047 ENABLE_BUFFER
3048 //PRINT_TRACE
3049 //      vwindows[DEFAULT_VWINDOW]->start();
3050         awindow->start();
3051 //PRINT_TRACE
3052         cwindow->start();
3053 //PRINT_TRACE
3054         lwindow->start();
3055 //PRINT_TRACE
3056         gwindow->start();
3057 //PRINT_TRACE
3058 //      Thread::start();
3059 //PRINT_TRACE
3060         playback_3d->start();
3061 //PRINT_TRACE
3062 }
3063
3064 void MWindow::run()
3065 {
3066         run_lock->lock("MWindow::run");
3067         gui->run_window();
3068         stop_playback(1);
3069
3070         brender_lock->lock("MWindow::run 1");
3071         delete brender;         brender = 0;
3072         brender_lock->unlock();
3073
3074         interrupt_indexes();
3075         clean_indexes();
3076         save_defaults();
3077         run_lock->unlock();
3078 }
3079
3080 void MWindow::show_vwindow(int raise)
3081 {
3082         int total_running = 0;
3083         session->show_vwindow = 1;
3084
3085 //printf("MWindow::show_vwindow %d %d\n", __LINE__, vwindows.size());
3086 // Raise all windows which are visible
3087         for(int j = 0; j < vwindows.size(); j++) {
3088                 VWindow *vwindow = vwindows[j];
3089                 if( !vwindow->is_running() ) continue;
3090                 total_running++;
3091                 if( !raise && !vwindow->gui->is_hidden() ) continue;
3092                 vwindow->gui->lock_window("MWindow::show_vwindow");
3093                 vwindow->gui->show_window(0);
3094                 vwindow->gui->raise_window();
3095                 vwindow->gui->flush();
3096                 vwindow->gui->unlock_window();
3097         }
3098 // If no windows visible
3099         if( !total_running )
3100                 get_viewer(1, DEFAULT_VWINDOW);
3101
3102         gui->mainmenu->show_vwindow->set_checked(1);
3103 }
3104
3105
3106 void MWindow::hide_vwindow(int raise)
3107 {
3108         session->show_vwindow = 0;
3109         int total_running = 0;
3110
3111         for(int j = 0; j < vwindows.size(); j++) {
3112                 VWindow *vwindow = vwindows[j];
3113                 if( !vwindow->is_running() ) continue;
3114                 total_running++;
3115                 if( !raise && !vwindow->gui->is_hidden() ) continue;
3116                 vwindow->gui->lock_window("MWindow::show_vwindow");
3117                 vwindow->gui->hide_window(0);
3118                 vwindow->gui->unlock_window();
3119         }
3120         gui->mainmenu->show_vwindow->set_checked(0);
3121 }
3122
3123
3124 void MWindow::show_awindow()
3125 {
3126         session->show_awindow = 1;
3127         awindow->gui->lock_window("MWindow::show_awindow");
3128         awindow->gui->show_window();
3129         awindow->gui->raise_window();
3130         awindow->gui->flush();
3131         awindow->gui->unlock_window();
3132         gui->mainmenu->show_awindow->set_checked(1);
3133 }
3134
3135 void MWindow::hide_awindow()
3136 {
3137         session->show_awindow = 0;
3138
3139         awindow->gui->lock_window("MWindow::show_awindow");
3140         awindow->gui->hide_window();
3141         awindow->gui->unlock_window();
3142         gui->mainmenu->show_awindow->set_checked(0);
3143 }
3144
3145
3146 char *MWindow::get_cwindow_display()
3147 {
3148         char *x11_host = screens < 2 || session->window_config == 0 ?
3149                 session->a_x11_host : session->b_x11_host;
3150         return *x11_host ? x11_host : 0;
3151 }
3152
3153 void MWindow::show_cwindow()
3154 {
3155         session->show_cwindow = 1;
3156         cwindow->show_window();
3157         cwindow->gui->tool_panel->raise_tool();
3158         gui->mainmenu->show_cwindow->set_checked(1);
3159 }
3160
3161
3162 void MWindow::hide_cwindow()
3163 {
3164         session->show_cwindow = 0;
3165
3166         cwindow->gui->lock_window("MWindow::show_cwindow");
3167         cwindow->gui->hide_window();
3168         cwindow->gui->unlock_window();
3169         gui->mainmenu->show_cwindow->set_checked(0);
3170 }
3171
3172
3173 void MWindow::show_gwindow()
3174 {
3175         session->show_gwindow = 1;
3176
3177         gwindow->gui->lock_window("MWindow::show_gwindow");
3178         gwindow->gui->show_window();
3179         gwindow->gui->raise_window();
3180         gwindow->gui->flush();
3181         gwindow->gui->unlock_window();
3182
3183         gui->mainmenu->show_gwindow->set_checked(1);
3184 }
3185
3186 void MWindow::hide_gwindow()
3187 {
3188         session->show_gwindow = 0;
3189
3190         gwindow->gui->lock_window("MWindow::show_gwindow");
3191         gwindow->gui->hide_window();
3192         gwindow->gui->unlock_window();
3193         gui->mainmenu->show_gwindow->set_checked(0);
3194 }
3195
3196 void MWindow::show_lwindow()
3197 {
3198         session->show_lwindow = 1;
3199         lwindow->gui->lock_window("MWindow::show_lwindow");
3200         lwindow->gui->show_window();
3201         lwindow->gui->raise_window();
3202         lwindow->gui->flush();
3203         lwindow->gui->unlock_window();
3204         gui->mainmenu->show_lwindow->set_checked(1);
3205 }
3206
3207 void MWindow::hide_lwindow()
3208 {
3209         session->show_lwindow = 0;
3210
3211         lwindow->gui->lock_window("MWindow::show_lwindow");
3212         lwindow->gui->hide_window();
3213         lwindow->gui->unlock_window();
3214         gui->mainmenu->show_lwindow->set_checked(0);
3215 }
3216
3217
3218 void MWindow::restore_windows()
3219 {
3220         gui->unlock_window();
3221         if( !session->show_vwindow ) {
3222                 for( int i=0, n=vwindows.size(); i<n; ++i ) {
3223                         VWindow *vwindow = vwindows[i];
3224                         if( !vwindow || !vwindow->is_running() ) continue;
3225                         vwindow->gui->lock_window("MWindow::restore_windows");
3226                         vwindow->gui->close(1);
3227                         vwindow->gui->unlock_window();
3228                 }
3229         }
3230         else 
3231                 show_vwindow(0);
3232
3233         if( !session->show_awindow && !awindow->gui->is_hidden() ) {
3234                 awindow->gui->lock_window("MWindow::restore_windows");
3235                 awindow->gui->close_event();
3236                 awindow->gui->unlock_window();
3237         }
3238         else if( session->show_awindow && awindow->gui->is_hidden() )
3239                 show_awindow();
3240
3241         if( !session->show_cwindow && !cwindow->gui->is_hidden() ) {
3242                 cwindow->gui->lock_window("MWindow::restore_windows");
3243                 cwindow->hide_window();
3244                 cwindow->gui->unlock_window();
3245         }
3246         else if( session->show_cwindow && cwindow->gui->is_hidden() )
3247                 show_cwindow();
3248
3249         if( !session->show_gwindow && !gwindow->gui->is_hidden() ) {
3250                 gwindow->gui->lock_window("MWindow::restore_windows");
3251                 gwindow->gui->close_event();
3252                 gwindow->gui->unlock_window();
3253         }
3254         else if( session->show_gwindow && gwindow->gui->is_hidden() )
3255                 show_gwindow();
3256
3257         if( !session->show_lwindow && !lwindow->gui->is_hidden() ) {
3258                 lwindow->gui->lock_window("MWindow::restore_windows");
3259                 lwindow->gui->close_event();
3260                 lwindow->gui->unlock_window();
3261         }
3262         else if( session->show_lwindow && lwindow->gui->is_hidden() )
3263                 show_lwindow();
3264
3265         tile_mixers();
3266         gui->lock_window("MWindow::restore_windows");
3267         gui->focus();
3268 }
3269
3270 void MWindow::load_layout(const char *layout)
3271 {
3272         char path[BCTEXTLEN];
3273         snprintf(path, sizeof(path), "%s/%s", File::get_config_path(), layout);
3274         session->load_file(path);
3275         restore_windows();
3276         gui->default_positions();
3277         save_defaults();
3278 }
3279
3280 void MWindow::save_layout(const char *layout)
3281 {
3282         char path[BCTEXTLEN];
3283         snprintf(path, sizeof(path), "%s/%s", File::get_config_path(), layout);
3284         session->save_file(path);
3285 }
3286
3287 void MWindow::delete_layout(const char *layout)
3288 {
3289         char path[BCTEXTLEN];
3290         snprintf(path, sizeof(path), "%s/%s", File::get_config_path(), layout);
3291         unlink(path);
3292 }
3293
3294 int MWindow::tile_windows(int window_config)
3295 {
3296         int need_reload = 0;
3297         int play_config = window_config==0 ? 0 : 1;
3298         edl->session->playback_config->load_defaults(defaults, play_config);
3299         session->default_window_positions(window_config);
3300         if( screens == 1 ) {
3301                 gui->default_positions();
3302                 sync_parameters(CHANGE_ALL);
3303         }
3304         else
3305                 need_reload = 1;
3306         return need_reload;
3307 }
3308
3309 void MWindow::toggle_loop_playback()
3310 {
3311         edl->local_session->loop_playback = !edl->local_session->loop_playback;
3312         set_loop_boundaries();
3313         save_backup();
3314
3315         gui->draw_overlays(1);
3316         sync_parameters(CHANGE_PARAMS);
3317 }
3318
3319 void MWindow::set_screens(int value)
3320 {
3321         screens = value;
3322 }
3323
3324 void MWindow::set_auto_keyframes(int value)
3325 {
3326         edl->session->auto_keyframes = value;
3327         gui->mbuttons->edit_panel->keyframe->update(value);
3328         gui->flush();
3329         cwindow->gui->lock_window("MWindow::set_auto_keyframes");
3330         cwindow->gui->flush();
3331         cwindow->gui->unlock_window();
3332 }
3333
3334 void MWindow::set_span_keyframes(int value)
3335 {
3336         edl->session->span_keyframes = value;
3337         gui->mbuttons->edit_panel->span_keyframe->update(value);
3338         gui->flush();
3339         cwindow->gui->lock_window("MWindow::set_span_keyframes");
3340         cwindow->gui->flush();
3341         cwindow->gui->unlock_window();
3342 }
3343
3344 void MWindow::set_auto_visibility(Autos *autos, int value)
3345 {
3346         if( autos->type == Autos::AUTOMATION_TYPE_PLUGIN )
3347                 edl->session->auto_conf->plugins = value;
3348         else if( autos->autoidx >= 0 )
3349                 edl->session->auto_conf->autos[autos->autoidx] = value;
3350         else
3351                 return;
3352
3353         gui->update(0, NORMAL_DRAW, 0, 0, 0, 0, 0);
3354         gui->mainmenu->update_toggles(1);
3355         gui->unlock_window();
3356         gwindow->gui->update_toggles(1);
3357         gui->lock_window("MWindow::set_auto_visibility");
3358 }
3359
3360 void MWindow::set_keyframe_type(int mode)
3361 {
3362         gui->lock_window("MWindow::set_keyframe_type");
3363         edl->local_session->floatauto_type = mode;
3364         gui->mainmenu->update_toggles(0);
3365         gui->unlock_window();
3366 }
3367
3368 int MWindow::set_editing_mode(int new_editing_mode)
3369 {
3370         edl->session->editing_mode = new_editing_mode;
3371         gui->mbuttons->edit_panel->editing_mode = edl->session->editing_mode;
3372         gui->mbuttons->edit_panel->update();
3373         gui->set_editing_mode(1);
3374
3375         cwindow->gui->lock_window("MWindow::set_editing_mode");
3376         cwindow->gui->edit_panel->update();
3377         cwindow->gui->edit_panel->editing_mode = edl->session->editing_mode;
3378         cwindow->gui->unlock_window();
3379         return 0;
3380 }
3381
3382 void MWindow::toggle_editing_mode()
3383 {
3384         int mode = edl->session->editing_mode;
3385         if( mode == EDITING_ARROW )
3386                 set_editing_mode(EDITING_IBEAM);
3387         else
3388                 set_editing_mode(EDITING_ARROW);
3389 }
3390
3391 void MWindow::toggle_camera_xyz()
3392 {
3393         gwindow->gui->lock_window("MWindow::toggle_camera_xyz");
3394         gwindow->gui->toggle_camera_xyz();
3395         gwindow->gui->unlock_window();
3396 }
3397
3398 void MWindow::toggle_projector_xyz()
3399 {
3400         gwindow->gui->lock_window("MWindow::toggle_projector_xyz");
3401         gwindow->gui->toggle_projector_xyz();
3402         gwindow->gui->unlock_window();
3403 }
3404
3405 void MWindow::set_labels_follow_edits(int value)
3406 {
3407         edl->session->labels_follow_edits = value;
3408         gui->mbuttons->edit_panel->locklabels->update(value);
3409         gui->mainmenu->labels_follow_edits->set_checked(value);
3410         gui->flush();
3411 }
3412
3413 void MWindow::sync_parameters(int change_type)
3414 {
3415         if( in_destructor ) return;
3416
3417 // Sync engines which are playing back
3418         if( cwindow->playback_engine->is_playing_back ) {
3419                 if( change_type == CHANGE_PARAMS ) {
3420 // TODO: block keyframes until synchronization is done
3421                         cwindow->playback_engine->sync_parameters(edl);
3422                 }
3423                 else {
3424 // Stop and restart
3425                         int command = cwindow->playback_engine->command->command;
3426 // Waiting for tracking to finish would make the restart position more
3427 // accurate but it can't lock the window to stop tracking for some reason.
3428 // Not waiting for tracking gives a faster response but restart position is
3429 // only as accurate as the last tracking update.
3430                         cwindow->playback_engine->transport_stop(0);
3431                         cwindow->playback_engine->next_command->realtime = 1;
3432                         cwindow->playback_engine->transport_command(command, change_type, edl, 0);
3433                 }
3434         }
3435         else {
3436                 cwindow->refresh_frame(change_type);
3437         }
3438 }
3439
3440 void MWindow::age_caches()
3441 {
3442 // printf("MWindow::age_caches %d %lld %lld %lld %lld\n",
3443 // __LINE__,
3444 // preferences->cache_size,
3445 // audio_cache->get_memory_usage(1),
3446 // video_cache->get_memory_usage(1),
3447 // frame_cache->get_memory_usage(),
3448 // wave_cache->get_memory_usage(),
3449 // memory_usage);
3450         frame_cache->age_cache(512);
3451         wave_cache->age_cache(8192);
3452
3453         int64_t memory_usage;
3454         int64_t prev_memory_usage = 0;
3455         int result = 0;
3456         int64_t video_size = (int64_t)(preferences->cache_size * 0.80);
3457         while( !result && prev_memory_usage !=
3458                 (memory_usage=video_cache->get_memory_usage(1)) &&
3459                 memory_usage > video_size ) {
3460                 video_cache->delete_oldest();
3461                 prev_memory_usage = memory_usage;
3462         }
3463
3464         prev_memory_usage = 0;
3465         result = 0;
3466         int64_t audio_size = (int64_t)(preferences->cache_size * 0.20);
3467         while( !result && prev_memory_usage !=
3468                 (memory_usage=audio_cache->get_memory_usage(1)) &&
3469                 memory_usage > audio_size ) {
3470                 audio_cache->delete_oldest();
3471                 prev_memory_usage = memory_usage;
3472         }
3473 }
3474
3475 void MWindow::reset_android_remote()
3476 {
3477         gui->use_android_remote(preferences->android_remote);
3478 }
3479
3480
3481 void MWindow::show_keyframe_gui(Plugin *plugin)
3482 {
3483         keyframe_gui_lock->lock("MWindow::show_keyframe_gui");
3484 // Find existing thread
3485         for( int i=0; i<keyframe_threads->size(); ++i ) {
3486                 int plugin_id = keyframe_threads->get(i)->plugin_id;
3487                 if( plugin_id == plugin->orig_id ) {
3488                         keyframe_threads->get(i)->start_window(plugin, 0);
3489                         keyframe_gui_lock->unlock();
3490                         return;
3491                 }
3492         }
3493
3494 // Find unused thread
3495         for( int i=0; i<keyframe_threads->size(); ++i ) {
3496                 if( keyframe_threads->get(i)->plugin_id < 0 ) {
3497                         keyframe_threads->get(i)->start_window(plugin, 0);
3498                         keyframe_gui_lock->unlock();
3499                         return;
3500                 }
3501         }
3502
3503 // Create new thread
3504         KeyFrameThread *thread = new KeyFrameThread(this);
3505         keyframe_threads->append(thread);
3506         thread->start_window(plugin, 0);
3507         keyframe_gui_lock->unlock();
3508 }
3509
3510
3511 void MWindow::show_plugin(Plugin *plugin)
3512 {
3513         int done = 0;
3514
3515 SET_TRACE
3516 // Remove previously deleted plugin GUIs
3517         dead_plugin_lock->lock("MWindow::show_plugin");
3518         dead_plugins->remove_all_objects();
3519         dead_plugin_lock->unlock();
3520
3521 //printf("MWindow::show_plugin %d\n", __LINE__);
3522 SET_TRACE
3523
3524         plugin_gui_lock->lock("MWindow::show_plugin");
3525         for( int i=0; i<plugin_guis->total; ++i ) {
3526 // Pointer/id comparison
3527                 PluginServer *plugin_gui = plugin_guis->get(i);
3528                 if( plugin_gui->plugin_id == plugin->orig_id ) {
3529                         plugin_gui->raise_window();
3530                         done = 1;
3531                         break;
3532                 }
3533         }
3534 SET_TRACE
3535
3536 //printf("MWindow::show_plugin 1\n");
3537         if( !done && !plugin->track ) {
3538                 printf("MWindow::show_plugin track not defined.\n");
3539                 done = 1;
3540         }
3541         if( !done ) {
3542                 PluginServer *server = scan_plugindb(plugin->title,
3543                         plugin->track->data_type);
3544
3545 //printf("MWindow::show_plugin %p %d\n", server, server->uses_gui);
3546                 if(server && server->uses_gui)
3547                 {
3548                         PluginServer *gui = new PluginServer(*server);
3549                         plugin_guis->append(gui);
3550 // Needs mwindow to do GUI
3551                         gui->set_mwindow(this);
3552                         gui->open_plugin(0, preferences, edl, plugin);
3553                         plugin->show = 1;
3554                         gui->show_gui();
3555                 }
3556         }
3557         plugin_gui_lock->unlock();
3558 // update show/gui_id
3559         sync_parameters(CHANGE_PARAMS);
3560 //printf("MWindow::show_plugin %d\n", __LINE__);
3561 SET_TRACE
3562 //sleep(1);
3563 //printf("MWindow::show_plugin 2\n");
3564 }
3565
3566 void MWindow::hide_plugin(Plugin *plugin, int lock)
3567 {
3568         plugin->show = 0;
3569         return hide_plugin(plugin->orig_id, lock);
3570 }
3571
3572 void MWindow::hide_plugin(int plugin_id, int lock)
3573 {
3574 // Update the toggle
3575         gui->lock_window("MWindow::hide_plugin");
3576         gui->update(0, NORMAL_DRAW, 0, 0, 0, 0, 0);
3577         gui->unlock_window();
3578
3579         if(lock) plugin_gui_lock->lock("MWindow::hide_plugin");
3580         for( int i=0; i<plugin_guis->total; ++i ) {
3581                 PluginServer *plugin_gui = plugin_guis->get(i);
3582                 if( plugin_gui->plugin_id == plugin_id ) {
3583                         plugin_guis->remove(plugin_gui);
3584                         if(lock) plugin_gui_lock->unlock();
3585 // Last command executed in client side close
3586 // Schedule for deletion
3587                         plugin_gui->hide_gui();
3588                         delete_plugin(plugin_gui);
3589 //sleep(1);
3590                         return;
3591                 }
3592         }
3593         if(lock) plugin_gui_lock->unlock();
3594 // update show/gui_id
3595         sync_parameters(CHANGE_PARAMS);
3596 }
3597
3598 void MWindow::delete_plugin(PluginServer *plugin)
3599 {
3600         dead_plugin_lock->lock("MWindow::delete_plugin");
3601         dead_plugins->append(plugin);
3602         dead_plugin_lock->unlock();
3603 }
3604
3605 void MWindow::hide_plugins()
3606 {
3607         plugin_gui_lock->lock("MWindow::hide_plugins 1");
3608         while(plugin_guis->size())
3609         {
3610                 PluginServer *ptr = plugin_guis->get(0);
3611                 plugin_guis->remove(ptr);
3612                 plugin_gui_lock->unlock();
3613 // Last command executed in client side close
3614 // Schedule for deletion
3615                 ptr->hide_gui();
3616                 delete_plugin(ptr);
3617                 plugin_gui_lock->lock("MWindow::hide_plugins 2");
3618         }
3619         plugin_gui_lock->unlock();
3620
3621         hide_keyframe_guis();
3622 }
3623
3624 void MWindow::hide_keyframe_guis()
3625 {
3626         keyframe_gui_lock->lock("MWindow::hide_keyframe_guis");
3627         for(int i = 0; i < keyframe_threads->size(); i++)
3628         {
3629                 keyframe_threads->get(i)->close_window();
3630         }
3631         keyframe_gui_lock->unlock();
3632 }
3633
3634 void MWindow::hide_keyframe_gui(Plugin *plugin)
3635 {
3636         keyframe_gui_lock->lock("MWindow::hide_keyframe_gui");
3637         for( int i = 0; i < keyframe_threads->size(); i++) {
3638                 KeyFrameThread *keyframe_gui = keyframe_threads->get(i);
3639                 if( keyframe_gui->plugin_id == plugin->orig_id ) {
3640                         keyframe_gui->close_window();
3641                         break;
3642                 }
3643         }
3644         keyframe_gui_lock->unlock();
3645 }
3646
3647 int MWindow::get_hash_color(Edit *edit)
3648 {
3649         Indexable *idxbl = edit->asset ?
3650                 (Indexable*)edit->asset : (Indexable*)edit->nested_edl;
3651         if( !idxbl ) return 0;
3652         char path[BCTEXTLEN];
3653 // map proxy colors to unproxy colors
3654         if( edl->session->proxy_state != PROXY_ACTIVE ||
3655             !edit->asset || edit->track->data_type != TRACK_VIDEO ||
3656             ProxyRender::from_proxy_path(path, (Asset*)idxbl, edl->session->proxy_scale) )
3657                 strcpy(path, idxbl->path);
3658         char *cp = strrchr(path, '/');
3659         cp = !cp ? path : cp+1;
3660         uint8_t *bp = (uint8_t*)cp;
3661         int v = 0;
3662         while( *bp ) v += *bp++;
3663         return get_hash_color(v);
3664 }
3665
3666 int MWindow::get_hash_color(int v)
3667 {
3668         int hash = 0x303030;
3669         if( v & 0x01 ) hash ^= 0x000040;
3670         if( v & 0x02 ) hash ^= 0x004000;
3671         if( v & 0x04 ) hash ^= 0x400000;
3672         if( v & 0x08 ) hash ^= 0x080000;
3673         if( v & 0x10 ) hash ^= 0x000800;
3674         if( v & 0x20 ) hash ^= 0x000008;
3675         if( v & 0x40 ) hash ^= 0x404040;
3676         if( v & 0x80 ) hash ^= 0x080808;
3677         return hash;
3678 }
3679
3680 int MWindow::get_group_color(int v)
3681 {
3682         int color = 0x606060;
3683         if( v & 0x01 ) color ^= 0x000080;
3684         if( v & 0x02 ) color ^= 0x008000;
3685         if( v & 0x04 ) color ^= 0x800000;
3686         if( v & 0x08 ) color ^= 0x100000;
3687         if( v & 0x10 ) color ^= 0x001000;
3688         if( v & 0x20 ) color ^= 0x000010;
3689         if( v & 0x40 ) color ^= 0x080808;
3690         if( v & 0x80 ) color ^= 0x909090;
3691         return color;
3692 }
3693
3694 int MWindow::get_title_color(Edit *edit)
3695 {
3696         unsigned color = edit->color & 0xffffff;
3697         unsigned alpha = (~edit->color>>24) & 0xff;
3698         if( !color ) {
3699                 if( edit->group_id )
3700                         color = get_group_color(edit->group_id);
3701                 else if( preferences->autocolor_assets )
3702                         color = get_hash_color(edit);
3703                 else
3704                         return 0;
3705         }
3706         if( alpha == 0xff )
3707                 alpha = session->title_bar_alpha*255;
3708         return color | (~alpha<<24);
3709 }
3710
3711 void MWindow::update_keyframe_guis()
3712 {
3713 // Send new configuration to keyframe GUI's
3714         keyframe_gui_lock->lock("MWindow::update_keyframe_guis");
3715         for( int i=0; i<keyframe_threads->size(); ++i ) {
3716                 KeyFrameThread *keyframe_gui = keyframe_threads->get(i);
3717                 Plugin *plugin = edl->tracks->plugin_exists(keyframe_gui->plugin_id);
3718                 if( plugin )
3719                         keyframe_gui->update_gui(1);
3720                 else
3721                         keyframe_gui->close_window();
3722         }
3723         keyframe_gui_lock->unlock();
3724 }
3725
3726 void MWindow::update_plugin_guis(int do_keyframe_guis)
3727 {
3728 // Send new configuration to plugin GUI's
3729         plugin_gui_lock->lock("MWindow::update_plugin_guis");
3730
3731         for( int i=0; i<plugin_guis->size(); ++i ) {
3732                 PluginServer *plugin_gui = plugin_guis->get(i);
3733                 Plugin *plugin = edl->tracks->plugin_exists(plugin_gui->plugin_id);
3734                 if( plugin && plugin->show )
3735                         plugin_gui->update_gui();
3736                 else {
3737 // Schedule for deletion if no plugin or not shown
3738                         plugin_guis->remove_number(i--);
3739                         plugin_gui->hide_gui();
3740                         delete_plugin(plugin_gui);
3741                 }
3742         }
3743
3744
3745 // Change plugin variable if not visible
3746         Track *track = edl->tracks->first;
3747         for( ; track; track=track->next ) {
3748                 for( int i=0; i < track->plugin_set.size(); ++i ) {
3749                         Plugin *plugin = (Plugin*)track->plugin_set[i]->first;
3750                         for( ; plugin; plugin = (Plugin*)plugin->next ) {
3751                                 int got_it = 0;
3752                                 for( int i=0; i<plugin_guis->size(); ++i ) {
3753                                         PluginServer *server = plugin_guis->get(i);
3754                                         if( server->plugin_id == plugin->orig_id ) {
3755                                                 got_it = 1;
3756                                                 break;
3757                                         }
3758                                 }
3759                                 if( !got_it ) plugin->show = 0;
3760                         }
3761                 }
3762         }
3763
3764         plugin_gui_lock->unlock();
3765         if( do_keyframe_guis )
3766                 update_keyframe_guis();
3767 }
3768
3769 void MWindow::stop_plugin_guis()
3770 {
3771 // Send new configuration to plugin GUI's
3772         plugin_gui_lock->lock("MWindow::stop_plugin_guis");
3773
3774         for( int i=0; i<plugin_guis->size(); ++i ) {
3775                 PluginServer *plugin_gui = plugin_guis->get(i);
3776                 plugin_gui->render_stop();
3777         }
3778         plugin_gui_lock->unlock(); 
3779 }
3780
3781 int MWindow::plugin_gui_open(Plugin *plugin)
3782 {
3783         int gui_id = plugin->gui_id;
3784         if( gui_id < 0 ) return 0;
3785         plugin_gui_lock->lock("MWindow::plugin_gui_open");
3786         PluginServer *plugin_server = plugin_guis->gui_server(gui_id);
3787         int result = plugin_server ? 1 : 0;
3788         plugin_gui_lock->unlock();
3789         return result;
3790 }
3791
3792
3793 void MWindow::render_plugin_gui(void *data, Plugin *plugin)
3794 {
3795         int gui_id = plugin->gui_id;
3796         if( gui_id < 0 ) return;
3797         plugin_gui_lock->lock("MWindow::render_plugin_gui 0");
3798         PluginServer *plugin_server = plugin_guis->gui_server(gui_id);
3799         if( plugin_server )
3800                 plugin_server->render_gui(data);
3801         plugin_gui_lock->unlock();
3802 }
3803
3804 void MWindow::render_plugin_gui(void *data, int size, Plugin *plugin)
3805 {
3806         int gui_id = plugin->gui_id;
3807         if( gui_id < 0 ) return;
3808         plugin_gui_lock->lock("MWindow::render_plugin_gui 1");
3809         PluginServer *plugin_server = plugin_guis->gui_server(gui_id);
3810         if( plugin_server )
3811                 plugin_server->render_gui(data, size);
3812         plugin_gui_lock->unlock();
3813 }
3814
3815 void MWindow::reset_plugin_gui_frames(Plugin *plugin)
3816 {
3817         int gui_id = plugin->gui_id;
3818         if( gui_id < 0 ) return;
3819         plugin_gui_lock->lock("MWindow::reset_plugin_gui_frames");
3820         PluginServer *plugin_server = plugin_guis->gui_server(gui_id);
3821         if( plugin_server )
3822                 plugin_server->reset_plugin_gui_frames();
3823         plugin_gui_lock->unlock();
3824 }
3825
3826 void MWindow::render_plugin_gui_frames(PluginClientFrames *frames, Plugin *plugin)
3827 {
3828         int gui_id = plugin->gui_id;
3829         if( gui_id < 0 ) return;
3830         plugin_gui_lock->lock("MWindow::render_plugin_gui_frames");
3831         PluginServer *plugin_server = plugin_guis->gui_server(gui_id);
3832         if( plugin_server )
3833                 plugin_server->render_plugin_gui_frames(frames);
3834         plugin_gui_lock->unlock();
3835 }
3836
3837 double MWindow::get_tracking_position()
3838 {
3839         return edl->local_session->get_selectionstart(1);
3840 }
3841
3842 int MWindow::get_tracking_direction()
3843 {
3844         return cwindow->playback_engine->get_direction();
3845 }
3846
3847 void MWindow::update_plugin_states()
3848 {
3849         plugin_gui_lock->lock("MWindow::update_plugin_states");
3850         for( int i=0; i<plugin_guis->total; ++i ) {
3851 // Get a plugin GUI
3852                 PluginServer *src_plugingui = plugin_guis->get(i);
3853                 int plugin_id = src_plugingui->plugin_id;
3854                 Plugin *src_plugin = edl->tracks->plugin_exists(plugin_id);
3855 // Doesn't exist anymore
3856                 if( !src_plugin ) {
3857                         hide_plugin(plugin_id, 0);
3858                         --i;
3859                 }
3860         }
3861         plugin_gui_lock->unlock();
3862 }
3863
3864
3865 void MWindow::update_plugin_titles()
3866 {
3867         for( int i=0; i<plugin_guis->total; ++i )
3868                 plugin_guis->get(i)->update_title();
3869 }
3870
3871 int MWindow::asset_to_edl(EDL *new_edl,
3872         Asset *new_asset,
3873         RecordLabels *labels)
3874 {
3875 const int debug = 0;
3876 if(debug) printf("MWindow::asset_to_edl %d new_asset->layers=%d\n",
3877 __LINE__,
3878 new_asset->layers);
3879 // Keep frame rate, sample rate, and output size unchanged.
3880 // These parameters would revert the project if VWindow displayed an asset
3881 // of different size than the project.
3882         if(new_asset->video_data)
3883         {
3884                 new_edl->session->video_tracks = new_asset->layers;
3885         }
3886         else
3887                 new_edl->session->video_tracks = 0;
3888
3889 if(debug) printf("MWindow::asset_to_edl %d\n", __LINE__);
3890
3891
3892
3893
3894
3895         if(new_asset->audio_data)
3896         {
3897                 new_edl->session->audio_tracks = new_asset->channels;
3898         }
3899         else
3900                 new_edl->session->audio_tracks = 0;
3901 //printf("MWindow::asset_to_edl 2 %d %d\n", new_edl->session->video_tracks, new_edl->session->audio_tracks);
3902
3903 if(debug) printf("MWindow::asset_to_edl %d\n", __LINE__);
3904         new_edl->create_default_tracks();
3905 //printf("MWindow::asset_to_edl 2 %d %d\n", new_edl->session->video_tracks, new_edl->session->audio_tracks);
3906 if(debug) printf("MWindow::asset_to_edl %d\n", __LINE__);
3907
3908
3909
3910 //printf("MWindow::asset_to_edl 3\n");
3911         new_edl->insert_asset(new_asset,
3912                 0,
3913                 0,
3914                 0,
3915                 labels);
3916 //printf("MWindow::asset_to_edl 3\n");
3917 if(debug) printf("MWindow::asset_to_edl %d\n", __LINE__);
3918
3919 // Align cursor on frames:: clip the new_edl to the minimum of the last joint frame.
3920         if(edl->session->cursor_on_frames)
3921         {
3922                 double length = new_edl->tracks->total_length();
3923                 double edl_length = new_edl->tracks->total_length_framealigned(edl->session->frame_rate);
3924                 new_edl->tracks->clear(length, edl_length, 1, 1);
3925         }
3926
3927
3928
3929
3930         char string[BCTEXTLEN];
3931         FileSystem fs;
3932         fs.extract_name(string, new_asset->path);
3933 //printf("MWindow::asset_to_edl 3\n");
3934
3935         strcpy(new_edl->local_session->clip_title, string);
3936 //printf("MWindow::asset_to_edl 4 %s\n", string);
3937 if(debug) printf("MWindow::asset_to_edl %d\n", __LINE__);
3938
3939         new_edl->local_session->asset2edl = 1;
3940         return 0;
3941 }
3942
3943
3944 // Reset everything after a load.
3945 void MWindow::update_project(int load_mode)
3946 {
3947         const int debug = 0;
3948
3949         if(debug) PRINT_TRACE
3950         edl->tracks->update_y_pixels(theme);
3951         session->update_clip_number();
3952
3953         if(debug) PRINT_TRACE
3954
3955         if( load_mode == LOADMODE_REPLACE ||
3956             load_mode == LOADMODE_REPLACE_CONCATENATE ) {
3957                 edl->session->timecode_offset = 0;
3958                 gui->close_keyvalue_popup();
3959                 gui->load_panes();
3960         }
3961
3962         gui->update(1, NORMAL_DRAW, 1, 1, 1, 1, 1);
3963         if(debug) PRINT_TRACE
3964         gui->unlock_window();
3965         init_brender();
3966
3967         cwindow->update(0, 0, 1, 1, 1);
3968
3969         if(debug) PRINT_TRACE
3970
3971 // Close all the vwindows
3972         if( load_mode == LOADMODE_REPLACE ||
3973             load_mode == LOADMODE_REPLACE_CONCATENATE ) {
3974                 if(debug) PRINT_TRACE
3975                 int first_vwindow = 0;
3976                 if(session->show_vwindow) first_vwindow = 1;
3977 // Change visible windows to no source
3978                 for(int i = 0; i < first_vwindow && i < vwindows.size(); i++) {
3979                         VWindow *vwindow = vwindows[i];
3980                         if( !vwindow->is_running() ) continue;
3981                         vwindow->change_source(-1);
3982                 }
3983
3984 // Close remaining windows
3985                 for(int i = first_vwindow; i < vwindows.size(); i++) {
3986                         VWindow *vwindow = vwindows[i];
3987                         if( !vwindow->is_running() ) continue;
3988                         vwindow->close_window();
3989                 }
3990                 for( int i=0; i<edl->vwindow_edls.size(); ++i ) {
3991                         VWindow *vwindow = get_viewer(1, -1);
3992                         vwindow->change_source(i);
3993                 }
3994                 if(debug) PRINT_TRACE
3995                 select_zwindow(0);
3996                 close_mixers(0);
3997
3998                 for( int i=0; i<edl->mixers.size(); ++i ) {
3999                         Mixer *mixer = edl->mixers[i];
4000                         if( !mixer->show ) continue;
4001                         ZWindow *zwindow = get_mixer(mixer);
4002                         zwindow->set_title(mixer->title);
4003                         zwindow->start();
4004                 }
4005                 cwindow->gui->canvas->set_zoom(edl, 0);
4006         }
4007         update_vwindow();
4008
4009         if(debug) PRINT_TRACE
4010         cwindow->gui->lock_window("MWindow::update_project 2");
4011         cwindow->gui->timebar->update(0);
4012         Track *track = cwindow->calculate_affected_track();
4013         cwindow->mask_track_id = track ? track->get_id() : -1;
4014         cwindow->gui->tool_panel->raise_tool();
4015         cwindow->gui->update_canvas(0);
4016         cwindow->gui->unlock_window();
4017
4018         if(debug) PRINT_TRACE
4019         cwindow->refresh_frame(CHANGE_ALL);
4020
4021         awindow->gui->async_update_assets();
4022         if(debug) PRINT_TRACE
4023
4024         gui->lock_window("MWindow::update_project");
4025         gui->update_mixers(0, 0);
4026         gui->flush();
4027         if(debug) PRINT_TRACE
4028 }
4029
4030 void MWindow::stack_push(EDL *new_edl, Indexable *idxbl)
4031 {
4032         int got_indexes = 0;
4033         for( int i=0; i<new_edl->nested_edls.size(); ++i ) {
4034                 EDL *nested_edl = new_edl->nested_edls[i];
4035                 mainindexes->add_indexable(nested_edl);
4036                 edl->nested_edls.update_index(nested_edl);
4037                 got_indexes = 1;
4038         }
4039         for( Asset *asset=new_edl->assets->first; asset; asset=asset->next ) {
4040                 mainindexes->add_indexable(asset);
4041                 edl->assets->update(asset);
4042                 got_indexes = 1;
4043         }
4044 // Start examining next batch of index files
4045         if( got_indexes )
4046                 mainindexes->start_build();
4047
4048 // needs gui lock
4049         gui->lock_window("MWindow::stack_push");
4050         if( stack.size() < 9 ) {
4051                 save_backup();
4052                 hide_plugins();
4053                 undo_before();
4054                 StackItem &item = stack.append();
4055                 item.edl = edl;
4056                 item.new_edl = new_edl;
4057                 item.duration = new_edl->tracks->total_length();
4058                 item.undo = undo;
4059                 item.idxbl = idxbl;
4060                 item.mtime = 0;
4061                 if( idxbl && idxbl->is_asset ) {
4062                         struct stat st;
4063                         Asset *asset = (Asset *)idxbl;
4064                         if( asset->format == FILE_REF &&
4065                             !stat(asset->path, &st) )
4066                                 item.mtime = st.st_mtime;
4067                 }
4068                 edl = new_edl;
4069                 edl->add_user();
4070                 strcpy(session->filename, edl->path);
4071                 undo = new MainUndo(this);
4072                 gui->stack_button->update();
4073                 update_project(LOADMODE_REPLACE);
4074         }
4075         gui->unlock_window();
4076         show_plugins();
4077 }
4078
4079 void MWindow::stack_pop()
4080 {
4081         if( !stack.size() ) return;
4082 // writes on config_path/backup%d.xml
4083         save_backup();
4084         hide_plugins();
4085 // already have gui lock
4086         StackItem &item = stack.last();
4087 // session edl replaced, overwrite and save clip data
4088         if( item.new_edl != edl )
4089                 item.new_edl->overwrite_clip(edl);
4090         Indexable *idxbl = item.idxbl;
4091         if( idxbl && idxbl->is_asset && item.mtime ) {
4092                 Asset *asset = (Asset *)idxbl;
4093                 if( asset->format == FILE_REF ) {
4094                         char *path = asset->path;
4095                         struct stat st;
4096                         if( stat(path, &st) || item.mtime == st.st_mtime ) {
4097                                 int cw = xS(250), ch = yS(150), px, py;
4098                                 gui->get_pop_cursor(px, py);
4099                                 px -= cw/2;  py -= ch/2;
4100                                 ConfirmRefWindow confirm(this, path, px, py, cw, ch);
4101                                 confirm.create_objects();
4102                                 int result = confirm.run_window();
4103                                 if( !result ) {
4104                                         FileXML file;
4105                                         item.new_edl->save_xml(&file, path);
4106                                         file.terminate_string();
4107                                         if(file.write_to_file(path))
4108                                                 eprintf(_("Cant write FileREF: %s"), path);
4109                                 }
4110                         }
4111                 }
4112         }
4113         edl->remove_user();
4114         edl = item.edl;
4115         delete undo;
4116         undo = item.undo;
4117         stack.remove();
4118         if( idxbl ) {
4119 // resize the indexable edits if the new_edl duration changed
4120                 double duration = item.new_edl->tracks->total_length();
4121                 double dt = duration - item.duration;
4122                 if( fabs(dt) > 1e-4 )
4123                         edl->tracks->update_idxbl_length(idxbl->id, dt);
4124                 gui->unlock_window();
4125                 gui->resource_thread->close_indexable(idxbl);
4126                 remove_from_caches(idxbl);
4127                 IndexFile::delete_index_files(preferences, idxbl);
4128                 mainindexes->add_indexable(idxbl);
4129                 mainindexes->start_build();
4130                 awindow->gui->async_update_assets();
4131                 gui->lock_window("MWindow::stack_pop");
4132         }
4133         strcpy(session->filename, edl->path);
4134         update_project(LOADMODE_REPLACE);
4135         undo_after(_("open edl"), LOAD_ALL);
4136         show_plugins();
4137         gui->stack_button->update();
4138 }
4139
4140 int MWindow::save(EDL *edl, char *filename, int stat)
4141 {
4142         FileXML file;
4143         edl->save_xml(&file, filename);
4144         file.terminate_string();
4145         if( file.write_to_file(filename) ) {
4146                 eprintf(_("Couldn't open %s"), filename);
4147                 return 1;
4148         }
4149         if( stat ) {
4150                 char string[BCTEXTLEN];
4151                 char *filename = stack.size() ?
4152                         stack[0].edl->path : session->filename;
4153                 sprintf(string, _("\"%s\" %jdC written"),
4154                          filename, file.length());
4155                 gui->lock_window("SaveAs::run");
4156                 gui->show_message(string);
4157                 gui->unlock_window();
4158         }
4159         return 0;
4160 }
4161
4162 int MWindow::save(int save_as)
4163 {
4164         char new_path[BCTEXTLEN];  new_path[0] = 0;
4165         char *path = stack.size() ? stack[0].edl->path : session->filename;
4166         if( save_as || !path[0] ) {
4167                 if( ConfirmSave::get_save_path(this, new_path) )
4168                         return 1;
4169                 if( stack.size() ) {
4170                         strcpy(path, new_path);
4171                         set_titlebar(new_path);
4172                 }
4173                 else
4174                         set_filename(new_path);
4175                 gui->mainmenu->add_load(new_path);
4176                 path = new_path;
4177         }
4178         for( int i=stack.size(); --i>=0;  ) {
4179                 StackItem &item = stack[i];
4180                 Indexable *idxbl = item.idxbl;
4181                 if( !idxbl ) continue;
4182                 if( idxbl->is_asset ) {
4183                         Asset *asset = (Asset *)idxbl;
4184                         if( asset->format == FILE_REF ) {
4185                                 if( save(item.new_edl, asset->path, 0) )
4186                                         return 1;
4187                         }
4188                 }
4189                 else if( item.new_edl != idxbl )
4190                         item.new_edl->overwrite_clip((EDL*)idxbl);
4191         }
4192         EDL *new_edl = stack.size() ? stack[0].edl : edl;
4193         save(new_edl, path, 1);
4194         return 0;
4195 }
4196
4197 void MWindow::show_plugins()
4198 {
4199         for( Track *track=edl->tracks->first; track; track=track->next ) {
4200                 for( int i=0; i<track->plugin_set.size(); ++i ) {
4201                         PluginSet *plugins = track->plugin_set[i];
4202                         Plugin *plugin = plugins->get_first_plugin();
4203                         for( ; plugin; plugin=(Plugin*)plugin->next ) {
4204                                 if( plugin->plugin_type == PLUGIN_STANDALONE &&
4205                                     plugin->show )
4206                                         show_plugin(plugin);
4207                         }
4208                 }
4209         }
4210 }
4211
4212 void MWindow::clip_to_media()
4213 {
4214         if( edl->session->proxy_scale != 1 ) {
4215                 eprintf("Nesting not allowed when proxy scale != 1");
4216                 return;
4217         }
4218         undo_before();
4219         awindow->gui->stop_vicon_drawing();
4220         int clips_total = session->drag_clips->total;
4221         for( int i=0; i<clips_total; ++i ) {
4222                 EDL *clip = session->drag_clips->values[i];
4223                 time_t dt;      time(&dt);
4224                 struct tm dtm;  localtime_r(&dt, &dtm);
4225                 char path[BCTEXTLEN], *cp = path, *ep = cp+sizeof(path)-1;
4226 // path_basename = "Nested_<date>-<time>_<basename>"
4227                 cp += snprintf(cp, ep-cp, _("Nested_%02d%02d%02d-%02d%02d%02d_"),
4228                         dtm.tm_year+1900, dtm.tm_mon+1, dtm.tm_mday,
4229                         dtm.tm_hour, dtm.tm_min, dtm.tm_sec);
4230                 char *bp = strrchr(clip->local_session->clip_title, '/');
4231                 bp = bp ? bp+1 : clip->local_session->clip_title;
4232                 cp += snprintf(cp, ep-cp, "%s", bp);
4233                 EDL *nested = edl->new_nested_clip(clip, path);
4234                 edl->clips.remove(clip);
4235                 clip->remove_user();
4236                 mainindexes->add_indexable(nested);
4237         }
4238         undo_after(_("clip2media"), LOAD_ALL);
4239         mainindexes->start_build();
4240         awindow->gui->async_update_assets();
4241 }
4242
4243 void MWindow::media_to_clip()
4244 {
4245         undo_before();
4246         int assets_total = session->drag_assets->total;
4247         for( int i=0; i<assets_total; ++i ) {
4248                 Indexable *idxbl = session->drag_assets->values[i];
4249                 if( idxbl->is_asset ) {
4250                         eprintf(_("media is not EDL:\n%s"), idxbl->path);
4251                         continue;
4252                 }
4253                 char clip_title[BCSTRLEN];
4254                 int name_ok = 0;
4255                 while( !name_ok ) {
4256                         name_ok = 1;
4257                         sprintf(clip_title, _("Clip %d"), session->clip_number++);
4258                         for( int i=0; name_ok && i<edl->clips.size(); ++i ) {
4259                                 char *title = edl->clips[i]->local_session->clip_title;
4260                                 if( !strcasecmp(clip_title, title) ) name_ok = 0;
4261                         }
4262                 }
4263                 EDL *nested = (EDL *)idxbl;
4264                 EDL *clip = edl->add_clip(nested);
4265                 strcpy(clip->local_session->clip_title, clip_title);
4266                 snprintf(clip->local_session->clip_notes,
4267                         sizeof(clip->local_session->clip_notes),
4268                         _("From: %s"), nested->path);
4269         }
4270         undo_after(_("media2clip"), LOAD_ALL);
4271         awindow->gui->async_update_assets();
4272 }
4273
4274 int MWindow::create_ref(Asset *asset, EDL *ref)
4275 {
4276         asset->format = FILE_REF;
4277         double secs = ref->tracks->total_length();
4278         int audio_channels = ref->session->audio_channels;
4279         asset->audio_data = audio_channels > 0 ? 1 : 0;
4280         asset->channels = audio_channels;
4281         asset->sample_rate = ref->session->sample_rate;
4282         asset->audio_length = audio_channels > 0 && secs > 0 ?
4283                  secs * asset->sample_rate : 0;
4284         strcpy(asset->acodec, _("reference"));
4285
4286         int video_layers = ref->session->video_channels;
4287         asset->video_data = video_layers > 0 ? 1 : 0;
4288         asset->layers = video_layers > 0 ? 1 : 0;
4289         asset->actual_width = ref->session->output_w;
4290         asset->actual_height = ref->session->output_h;
4291         asset->width = asset->actual_width;
4292         asset->height = asset->actual_height;
4293         asset->frame_rate = ref->session->frame_rate;
4294         asset->video_length = video_layers > 0 && secs > 0 ?
4295                 secs * asset->frame_rate : 0;
4296         strcpy(asset->vcodec, _("reference"));
4297         return 0;
4298 }
4299
4300 void MWindow::update_preferences(Preferences *prefs)
4301 {
4302         if( prefs != preferences )
4303                 preferences->copy_from(prefs);
4304         if( cwindow->playback_engine )
4305                 cwindow->playback_engine->update_preferences(prefs);
4306         for(int i = 0; i < vwindows.size(); i++) {
4307                 VWindow *vwindow = vwindows[i];
4308                 if( !vwindow->is_running() ) continue;
4309                 if( vwindow->playback_engine )
4310                         vwindow->playback_engine->update_preferences(prefs);
4311         }
4312         for(int i = 0; i < zwindows.size(); i++) {
4313                 ZWindow *zwindow = zwindows[i];
4314                 if( !zwindow->is_running() ) continue;
4315                 if( zwindow->zgui->playback_engine )
4316                         zwindow->zgui->playback_engine->update_preferences(prefs);
4317         }
4318 }
4319
4320 void MWindow::update_vwindow()
4321 {
4322         for( int i=0; i<vwindows.size(); ++i ) {
4323                 VWindow *vwindow = vwindows[i];
4324                 if( vwindow->is_running() ) {
4325                         vwindow->gui->lock_window("MWindow::update_vwindow");
4326                         vwindow->update(1);
4327                         vwindow->gui->unlock_window();
4328                 }
4329         }
4330 }
4331
4332 void MWindow::remove_indexfile(Indexable *indexable)
4333 {
4334         if( !indexable->is_asset ) return;
4335 // Erase file
4336         IndexFile::delete_index_files(preferences, indexable);
4337 }
4338
4339 void MWindow::rebuild_indices()
4340 {
4341         for(int i = 0; i < session->drag_assets->total; i++)
4342         {
4343                 Indexable *indexable = session->drag_assets->get(i);
4344 //printf("MWindow::rebuild_indices 1 %s\n", indexable->path);
4345                 remove_indexfile(indexable);
4346 // Schedule index build
4347                 indexable->index_state->remove_user();
4348                 indexable->index_state = new IndexState;
4349                 IndexFile::delete_index_files(preferences, indexable);
4350                 if( indexable->is_asset ) {
4351                         Asset *asset = (Asset *)indexable;
4352                         if( asset->format != FILE_PCM ) {
4353                                 asset->format = FILE_UNKNOWN;
4354                                 asset->reset_audio();
4355                         }
4356                         asset->reset_video();
4357                         remove_from_caches(asset);
4358 //                      File file; // re-probe the asset
4359 //                      file.open_file(preferences, asset, 1, 0);
4360                 }
4361                 mainindexes->add_indexable(indexable);
4362         }
4363 // still in render engine
4364         sync_parameters(CHANGE_ALL);
4365         awindow->gui->async_update_assets();
4366         mainindexes->start_build();
4367 }
4368
4369
4370 void MWindow::get_backup_path(char *path, int len)
4371 {
4372         char *cp = path, *ep = cp + len-1;
4373         cp += snprintf(cp, ep-cp, "%s/", File::get_config_path());
4374         int idx = stack.size();
4375         cp += snprintf(cp, ep-cp, idx ? BACKUPn_FILE : BACKUP_FILE, idx);
4376 }
4377
4378 void MWindow::create_timestamped_copy_from_previous_backup(char *previouspath)
4379 {
4380   if (previouspath == NULL) return;
4381   char backup_path[BCTEXTLEN];
4382   backup_path[0] = 0;
4383   time_t now = time(NULL);
4384   struct tm* currenttime = localtime(&now);
4385   snprintf(backup_path, sizeof(backup_path), 
4386       "%s/%s_%d%.2d%.2d_%.2d%.2d%.2d",
4387       File::get_config_path(), BACKUP_FILE1, 
4388       currenttime->tm_year + 1900,
4389       currenttime->tm_mon + 1,
4390       currenttime->tm_mday,
4391       currenttime->tm_hour,
4392       currenttime->tm_min,
4393       currenttime->tm_sec);
4394         rename(previouspath, backup_path);
4395 }
4396
4397 void MWindow::save_backup()
4398 {
4399         FileXML file;
4400         edl->optimize();
4401         edl->set_path(session->filename);
4402
4403         char backup_path[BCTEXTLEN], backup_path1[BCTEXTLEN];
4404         snprintf(backup_path1, sizeof(backup_path1), "%s/%s",
4405                 File::get_config_path(), BACKUP_FILE1);
4406         get_backup_path(backup_path, sizeof(backup_path));
4407         if( preferences->ongoing_backups )
4408                 create_timestamped_copy_from_previous_backup(backup_path1);
4409         rename(backup_path, backup_path1);
4410         edl->save_xml(&file, backup_path);
4411         file.terminate_string();
4412         FileSystem fs;
4413         fs.complete_path(backup_path);
4414
4415         if(file.write_to_file(backup_path)) {
4416                 char string2[256];
4417                 sprintf(string2, _("Couldn't open %s for writing."), backup_path);
4418                 gui->show_message(string2);
4419         }
4420         save_undo_data();
4421 }
4422
4423 void MWindow::load_backup()
4424 {
4425         ArrayList<char*> path_list;
4426         path_list.set_array_delete();
4427         char *out_path;
4428         char backup_path[BCTEXTLEN];
4429         get_backup_path(backup_path, sizeof(backup_path));
4430         FileSystem fs;
4431         fs.complete_path(backup_path);
4432
4433         path_list.append(out_path = new char[strlen(backup_path) + 1]);
4434         strcpy(out_path, backup_path);
4435
4436         load_filenames(&path_list, LOADMODE_REPLACE, LOADMODE_EDL_CLIP, 0);
4437         edl->local_session->clip_title[0] = 0;
4438 // This is unique to backups since the path of the backup is different than the
4439 // path of the project.
4440         set_filename(edl->path);
4441         path_list.remove_all_objects();
4442         save_backup();
4443 }
4444
4445
4446 void MWindow::save_undo_data()
4447 {
4448         if( stack.size() > 0 ) return;
4449         if( !preferences->perpetual_session ) return;
4450         char perpetual_path[BCTEXTLEN];
4451         snprintf(perpetual_path, sizeof(perpetual_path), "%s/%s",
4452                 File::get_config_path(), PERPETUAL_FILE);
4453         FILE *fp = fopen(perpetual_path,"w");
4454         if( !fp ) return;
4455         undo->save(fp);
4456         fclose(fp);
4457 }
4458
4459 void MWindow::load_undo_data()
4460 {
4461         if( stack.size() > 0 ) return;
4462         if( !preferences->perpetual_session ) return;
4463         char perpetual_path[BCTEXTLEN];
4464         snprintf(perpetual_path, sizeof(perpetual_path), "%s/%s",
4465                 File::get_config_path(), PERPETUAL_FILE);
4466         FILE *fp = fopen(perpetual_path,"r");
4467         if( !fp ) return;
4468         undo->load(fp);
4469         undo_before();
4470         undo_after(_("perpetual load"), LOAD_ALL);
4471         fclose(fp);
4472 }
4473
4474 void MWindow::remove_undo_data()
4475 {
4476         if( stack.size() > 0 ) return;
4477         char perpetual_path[BCTEXTLEN];
4478         snprintf(perpetual_path, sizeof(perpetual_path), "%s/%s",
4479                 File::get_config_path(), PERPETUAL_FILE);
4480         ::remove(perpetual_path);
4481 }
4482
4483 int MWindow::copy_target(const char *path, const char *target)
4484 {
4485         int ifd = ::open(path, O_RDONLY);
4486         if( ifd < 0 ) {
4487                 eprintf("Cannot open asset: %s", path);
4488                 return 1;
4489         }
4490         int ret = 0;
4491         int ofd = ::open(target, O_CREAT+O_TRUNC+O_WRONLY, 0777);
4492         if( ofd >= 0 ) {
4493                 struct stat st;
4494                 int64_t total_bytes = !fstat(ifd, &st) ? st.st_size : 0;
4495                 char progress_title[BCTEXTLEN];
4496                 sprintf(progress_title, _("Copying: %s\n"), target);
4497                 BC_ProgressBox progress(-1, -1, progress_title, total_bytes);
4498
4499                 int64_t count = 0, len = -1;
4500                 int bfrsz = 0x100000;
4501                 uint8_t *bfr = new uint8_t[bfrsz];
4502                 while( (len=::read(ifd, bfr, bfrsz)) > 0 ) {
4503                         if( len != ::write(ofd, bfr, len) ) {
4504                                 eprintf("Error writing: %s", target);
4505                                 break;
4506                         }
4507                         if( progress.is_cancelled() ) {
4508                                 ret = 1;
4509                                 break;
4510                         }
4511                         progress.update(count += len, 1);
4512                 }
4513                 delete [] bfr;
4514                 ::close(ofd);
4515
4516                 progress.stop_progress();
4517                 if( len < 0 ) {
4518                         eprintf("Error reading: %s", path);
4519                         ret = 1;
4520                 }
4521         }
4522         else
4523                 eprintf("Cannot create asset target: %s", target);
4524         ::close(ifd);
4525         return ret;
4526 }
4527
4528 int MWindow::link_target(const char *real_path, const char *link_path, int relative)
4529 {
4530         char target[BCTEXTLEN];
4531         if( relative ) {
4532                 const char *bp = real_path, *cp = bp;
4533                 const char *lp = link_path, *np = lp;
4534                 char *tp = target, *ep = tp+sizeof(target)-1, lch;
4535                 while( *lp && *bp && (lch=*lp++) == *bp++ ) {
4536                         if( lch == '/' ) { np = lp;  cp = bp; }
4537                 }
4538                 while( tp<ep && *np ) {
4539                         if( *np++ != '/' ) continue;
4540                         *tp++ = '.';  *tp++ = '.';  *tp++ = '/';
4541                 }
4542                 while( tp<ep && *cp ) *tp++ = *cp++;
4543                 *tp = 0;
4544         }
4545         else
4546                 strcpy(target, real_path);
4547         if( symlink(target, link_path) ) {
4548                 eprintf("Cannot create symlink: %s", link_path);
4549                 return 1;
4550         }
4551         return 0;
4552 }
4553
4554 void MWindow::save_project(const char *dir, int save_mode, int overwrite, int reload)
4555 {
4556         char dir_path[BCTEXTLEN];
4557         strcpy(dir_path, dir);
4558         FileSystem fs;
4559         fs.complete_path(dir_path);
4560
4561         struct stat st;
4562         if( !stat(dir_path, &st) ) {
4563                 if( !S_ISDIR(st.st_mode) ) {
4564                         eprintf("Path exists and is not a directory\n%s", dir_path);
4565                         return;
4566                 }
4567         }
4568         else {
4569                 if( mkdir(dir_path, S_IRWXU | S_IRWXG | S_IRWXO) ) {
4570                         eprintf("Cannot create directory\n%s", dir_path);
4571                         return;
4572                 }
4573         }
4574         char *real_dir = realpath(dir_path, 0);
4575         strcpy(dir_path, real_dir);
4576         free(real_dir);
4577
4578         EDL *save_edl = new EDL;
4579         save_edl->create_objects();
4580         save_edl->copy_all(edl);
4581
4582         char progress_title[BCTEXTLEN];
4583         sprintf(progress_title, _("Saving to %s:\n"), dir);
4584         int total_assets = save_edl->assets->total();
4585         gui->lock_window("MWindow::save_project");
4586         MainProgressBar *progress = mainprogress->start_progress(progress_title, total_assets);
4587         gui->unlock_window();
4588
4589         int ret = 0;
4590         Asset *current = save_edl->assets->first;
4591         for( int i=0; !ret && current; ++i, current=NEXT ) {
4592                 char *path = current->path;
4593                 if( ::stat(path, &st) ) {
4594                         eprintf("Asset not found: %s", path);
4595                         continue;
4596                 }
4597                 char *real_path = realpath(path, 0);
4598                 const char *cp = strrchr(path, '/'), *bp = !cp ? path : cp+1;
4599                 char link_path[BCTEXTLEN];
4600                 snprintf(link_path, sizeof(link_path), "%s/%s", dir_path, bp);
4601                 int skip = 0;
4602                 if( strcmp(real_path, link_path) ) {
4603                         if( !::lstat(link_path, &st) ) {
4604                                 if( overwrite )
4605                                         ::remove(link_path);
4606                                 else
4607                                         skip = 1;
4608                         }
4609                 }
4610                 else {
4611                         eprintf("copy/link to self, skippped: %s", path);
4612                         skip = 1;
4613                 }
4614                 if( !skip ) {
4615                         if( save_mode == SAVE_PROJECT_COPY ) {
4616                                 if( copy_target(real_path, link_path) )
4617                                         ret = 1;
4618                         }
4619                         else {
4620                                 link_target(real_path, link_path,
4621                                         save_mode == SAVE_PROJECT_RELLINK ? 1 : 0);
4622                         }
4623                 }
4624                 free(real_path);
4625                 strcpy(path, link_path);
4626
4627                 if( progress->is_cancelled() ) break;
4628                 progress->update(i);
4629         }
4630
4631         progress->stop_progress();
4632         delete progress;
4633
4634         char *cp = strrchr(dir_path,'/');
4635         char *bp = cp ? cp+1 : dir_path;
4636         char filename[BCTEXTLEN];
4637         snprintf(filename, sizeof(filename), "%s/%s.xml", dir_path, bp);
4638         save_edl->set_path(filename);
4639         FileXML file;
4640         save_edl->save_xml(&file, filename);
4641         file.terminate_string();
4642
4643         if( !file.write_to_file(filename) ) {
4644                 char string[BCTEXTLEN];
4645                 sprintf(string, _("\"%s\" %dC written"), filename, (int)strlen(file.string()));
4646                 gui->lock_window("SaveProject::run 2");
4647                 gui->show_message(string);
4648                 gui->unlock_window();
4649                 gui->mainmenu->add_load(filename);
4650         }
4651         else
4652                 eprintf(_("Couldn't open %s."), filename);
4653
4654         save_edl->remove_user();
4655
4656         if( reload ) {
4657                 gui->lock_window("MWindow::save_project");
4658                 ArrayList<char*> filenames;
4659                 filenames.append(filename);
4660                 load_filenames(&filenames);
4661                 gui->unlock_window();
4662         }
4663 }
4664
4665
4666 static inline int gcd(int m, int n)
4667 {
4668         int r;
4669         if( m < n ) { r = m;  m = n;  n = r; }
4670         while( (r = m % n) != 0 ) { m = n;  n = r; }
4671         return n;
4672 }
4673
4674 int MWindow::create_aspect_ratio(float &w, float &h, int width, int height)
4675 {
4676         w = 1;  h = 1;
4677         double ar;
4678         
4679         if(!width || !height) return 1;
4680         if( width == 720 && (height == 480 || height == 576) ) {
4681                 w = 4;  h = 3;  return 0; // for NTSC and PAL
4682         }
4683         
4684         ar = (double)width / height;
4685 // square-ish pixels
4686         if( EQUIV(ar, 1.0000) ) return 0;
4687         if( EQUIV(ar, 1.3333) ) { w = 4;  h = 3;  return 0; }
4688         if( EQUIV(ar, 1.7777) ) { w = 16; h = 9;  return 0; }
4689         if( EQUIV(ar, 2.1111) ) { w = 19; h = 9;  return 0; }
4690         if( EQUIV(ar, 2.2222) ) { w = 20; h = 9;  return 0; }
4691         if( EQUIV(ar, 2.3333) ) { w = 21; h = 9;  return 0; }
4692         if( EQUIV(ar, 2.37037) ) { w = 64; h = 27;  return 0; }
4693
4694         int ww = width, hh = height;
4695         // numerator, denominator must be under mx
4696         int mx = 255, n = gcd(ww, hh);
4697         if( n > 1 ) { ww /= n; hh /= n; }
4698         // search near height in case extra/missing lines
4699         if( ww >= mx || hh >= mx ) {
4700                 double err = height;  // +/- 2 percent height
4701                 for( int m=2*height/100, i=1; m>0; i=i>0 ? -i : (--m, -i+1) ) {
4702                         int iw = width, ih = height+i;
4703                         if( (n=gcd(iw, ih)) > 1 ) {
4704                                 int u = iw/n;  if( u >= mx ) continue;
4705                                 int v = ih/n;  if( v >= mx ) continue;
4706                                 double r = (double) u/v, er = fabs(ar-r);
4707                                 if( er >= err ) continue;
4708                                 err = er;  ww = u;  hh = v;
4709                         }
4710                 }
4711         }
4712
4713         w = ww;  h = hh;
4714         return 0;
4715 }
4716
4717 void MWindow::reset_caches(int locked)
4718 {
4719         if( locked ) gui->unlock_window();
4720         awindow->gui->stop_vicon_drawing(1);
4721         if( cwindow->playback_engine )
4722                 cwindow->playback_engine->create_cache();
4723         for(int i = 0; i < vwindows.size(); i++) {
4724                 VWindow *vwindow = vwindows[i];
4725                 if( !vwindow->is_running() ) continue;
4726                 if( !vwindow->playback_engine ) continue;
4727                 vwindow->playback_engine->create_cache();
4728         }
4729         gui->lock_window("MWindow::reset_caches");
4730         frame_cache->remove_all();
4731         wave_cache->remove_all();
4732         audio_cache->remove_all();
4733         video_cache->remove_all();
4734         if( !locked ) gui->unlock_window();
4735 }
4736
4737 void MWindow::remove_from_caches(Indexable *idxbl)
4738 {
4739         awindow->gui->stop_vicon_drawing(1);
4740         frame_cache->remove_item(idxbl);
4741         wave_cache->remove_item(idxbl);
4742         if( gui->render_engine &&
4743             gui->render_engine_id == idxbl->id ) {
4744                 delete gui->render_engine;
4745                 gui->render_engine = 0;
4746         }
4747         gui->resource_thread->close_indexable(idxbl);
4748         if( !idxbl->is_asset ) return;
4749         Asset *asset = (Asset *)idxbl;
4750         audio_cache->delete_entry(asset);
4751         video_cache->delete_entry(asset);
4752         if( cwindow->playback_engine && cwindow->playback_engine->audio_cache )
4753                 cwindow->playback_engine->audio_cache->delete_entry(asset);
4754         if( cwindow->playback_engine && cwindow->playback_engine->video_cache )
4755                 cwindow->playback_engine->video_cache->delete_entry(asset);
4756         for(int i = 0; i < vwindows.size(); i++) {
4757                 VWindow *vwindow = vwindows[i];
4758                 if( !vwindow->is_running() ) continue;
4759                 if( !vwindow->playback_engine ) continue;
4760                 if( vwindow->playback_engine->audio_cache )
4761                         vwindow->playback_engine->audio_cache->delete_entry(asset);
4762                 if( vwindow->playback_engine->video_cache )
4763                         vwindow->playback_engine->video_cache->delete_entry(asset);
4764         }
4765         for(int i = 0; i < zwindows.size(); i++) {
4766                 ZWindow *zwindow = zwindows[i];
4767                 if( !zwindow->is_running() ) continue;
4768                 if( zwindow->zgui->playback_engine->audio_cache )
4769                         zwindow->zgui->playback_engine->audio_cache->delete_entry(asset);
4770                 if( zwindow->zgui->playback_engine->video_cache )
4771                         zwindow->zgui->playback_engine->video_cache->delete_entry(asset);
4772         }
4773         awindow->gui->start_vicon_drawing();
4774 }
4775
4776 void MWindow::remove_assets_from_project(int push_undo, int redraw, int delete_indexes,
4777                 ArrayList<Indexable*> *drag_assets, ArrayList<EDL*> *drag_clips)
4778 {
4779         awindow->gui->stop_vicon_drawing(1);
4780
4781 // Remove from VWindow.
4782         if( drag_clips ) {
4783                 for(int i = 0; i < drag_clips->total; i++) {
4784                         for(int j = 0; j < vwindows.size(); j++) {
4785                                 VWindow *vwindow = vwindows[j];
4786                                 if( !vwindow->is_running() ) continue;
4787                                 if(drag_clips->get(i) == vwindow->get_edl()) {
4788                                         vwindow->gui->lock_window("MWindow::remove_assets_from_project 1");
4789                                         vwindow->delete_source(1, 1);
4790                                         vwindow->gui->unlock_window();
4791                                 }
4792                         }
4793                 }
4794         }
4795
4796         if( drag_assets ) {
4797                 for(int i = 0; i < drag_assets->size(); i++) {
4798                         for(int j = 0; j < vwindows.size(); j++) {
4799                                 VWindow *vwindow = vwindows[j];
4800                                 if( !vwindow->is_running() ) continue;
4801                                 if(drag_assets->get(i) == vwindow->get_source()) {
4802                                         vwindow->gui->lock_window("MWindow::remove_assets_from_project 2");
4803                                         vwindow->delete_source(1, 1);
4804                                         vwindow->gui->unlock_window();
4805                                 }
4806                         }
4807                 }
4808
4809                 for(int i = 0; i < drag_assets->total; i++) {
4810                         Indexable *indexable = drag_assets->get(i);
4811                         if(indexable->is_asset) remove_from_caches(indexable);
4812                 }
4813
4814                 if( delete_indexes ) {
4815                         for(int i = 0; i < drag_assets->size(); i++) {
4816                                 Indexable *indexable = drag_assets->get(i);
4817                                 remove_indexfile(indexable);
4818                         }
4819                 }
4820         }
4821
4822 //printf("MWindow::rebuild_indices 1 %s\n", indexable->path);
4823         if(push_undo) undo_before();
4824         if(drag_assets) edl->remove_from_project(drag_assets);
4825         if(drag_clips) edl->remove_from_project(drag_clips);
4826         if(redraw) save_backup();
4827         if(push_undo) undo_after(_("remove assets"), LOAD_ALL);
4828         if(redraw) {
4829                 restart_brender();
4830
4831                 gui->lock_window("MWindow::remove_assets_from_project 3");
4832                 gui->update(1, NORMAL_DRAW, 1, 1, 0, 1, 0);
4833                 gui->unlock_window();
4834
4835         // Removes from playback here
4836                 sync_parameters(CHANGE_ALL);
4837         }
4838
4839         awindow->gui->async_update_assets();
4840 }
4841
4842 void MWindow::remove_assets_from_disk()
4843 {
4844         remove_assets_from_project(1, 1, 1,
4845                 session->drag_assets, session->drag_clips);
4846
4847 // Remove from disk
4848         for(int i = 0; i < session->drag_assets->total; i++)
4849         {
4850                 remove(session->drag_assets->get(i)->path);
4851         }
4852 }
4853
4854 void MWindow::dump_plugins(FILE *fp)
4855 {
4856         if( !plugindb ) return;
4857         for(int i = 0; i < plugindb->total; i++)
4858         {
4859                 fprintf(fp, "type=%d audio=%d video=%d rt=%d multi=%d"
4860                         " synth=%d transition=%d theme=%d %s\n",
4861                         plugindb->get(i)->plugin_type,
4862                         plugindb->get(i)->audio,
4863                         plugindb->get(i)->video,
4864                         plugindb->get(i)->realtime,
4865                         plugindb->get(i)->multichannel,
4866                         plugindb->get(i)->get_synthesis(),
4867                         plugindb->get(i)->transition,
4868                         plugindb->get(i)->theme,
4869                         plugindb->get(i)->title);
4870         }
4871 }
4872
4873 void MWindow::dump_edl(FILE *fp)
4874 {
4875         if( !edl ) return;
4876         edl->dump(fp);
4877 }
4878
4879 void MWindow::dump_undo(FILE *fp)
4880 {
4881         if( !undo ) return;
4882         undo->dump(fp);
4883 }
4884
4885 void MWindow::dump_exe(FILE *fp)
4886 {
4887         char proc_path[BCTEXTLEN], exe_path[BCTEXTLEN];
4888         sprintf(proc_path, "/proc/%d/exe", (int)getpid());
4889
4890         int ret = -1, n = 100;
4891         for( int len; (len=readlink(proc_path, exe_path, sizeof(exe_path)))>0; --n ) {
4892                 exe_path[len] = 0;  strcpy(proc_path, exe_path);
4893                 ret = 0;
4894         }
4895         if( n < 0 || ret < 0 ) { fprintf(fp,"readlink: %m\n"); return; }
4896
4897         struct stat st;
4898         if( stat(proc_path,&st) ) { fprintf(fp,"stat: %m\n"); return; }
4899         fprintf(fp, "path: %s = %9jd bytes\n",proc_path,st.st_size);
4900         struct tm *tm = localtime(&st.st_mtime);
4901         char mtime[256];
4902         strftime(mtime, sizeof(mtime), "%F %T", tm);
4903         fprintf(fp,"mtime: %s\n", mtime);
4904 #if 0
4905 // people hit ctl-c waiting for this
4906         int fd = open(proc_path,O_RDONLY+O_NONBLOCK);
4907         if( fd < 0 ) { fprintf(fp,"open: %m\n"); return; }
4908         uint8_t *bfr = 0;
4909         int64_t bfrsz = 0;
4910         int64_t pagsz = sysconf(_SC_PAGE_SIZE);
4911         int64_t maxsz = 1024*pagsz;
4912         int64_t size = st.st_size, pos = 0;
4913         SHA1 sha1;
4914         while( (bfrsz = size-pos) > 0 ) {
4915                 if( bfrsz > maxsz ) bfrsz = maxsz;
4916                 bfr = (uint8_t *)mmap(NULL, bfrsz, PROT_READ,
4917                         MAP_PRIVATE+MAP_NORESERVE+MAP_POPULATE, fd, pos);
4918                 if( bfr == MAP_FAILED ) break;
4919                 sha1.addBytes(bfr, bfrsz);
4920                 munmap(bfr, bfrsz);
4921                 pos += bfrsz;
4922         }
4923         close(fd);
4924         ret = pos < size ? EIO : 0;
4925         fprintf(fp, "SHA1: ");
4926         uint8_t digest[20];  sha1.computeHash(digest);
4927         for( int i=0; i<20; ++i ) fprintf(fp, "%02x", digest[i]);
4928         if( ret < 0 ) fprintf(fp, " (ret %d)", ret);
4929         if( pos < st.st_size ) fprintf(fp, " (pos %jd)", pos);
4930 #endif
4931         fprintf(fp, "\n");
4932 }
4933
4934 void MWindow::dump_caches(FILE *fp)
4935 {
4936         fprintf(fp, "audio cache: ");
4937         audio_cache->dump(fp);
4938         fprintf(fp, "video cache: ");
4939         video_cache->dump(fp);
4940 }
4941
4942 void MWindow::trap_hook(FILE *fp, void *vp)
4943 {
4944         MWindow *mwindow = (MWindow *)vp;
4945 //      fprintf(fp, "\nPLUGINS:\n");
4946 //      mwindow->dump_plugins(fp);
4947         fprintf(fp, "\nEDL:\n");
4948         mwindow->dump_edl(fp);
4949         fprintf(fp, "\nUNDO:\n");
4950         mwindow->dump_undo(fp);
4951         fprintf(fp, "\nEXE: %s\n", AboutPrefs::build_timestamp);
4952         mwindow->dump_exe(fp);
4953         fprintf(fp, "\nCACHES:\n");
4954         mwindow->dump_caches(fp);
4955 }
4956
4957
4958
4959
4960
4961
4962 int MWindow::save_defaults()
4963 {
4964         gui->save_defaults(defaults);
4965         edl->save_defaults(defaults);
4966         session->save_defaults(defaults);
4967         preferences->save_defaults(defaults);
4968
4969         for(int i = 0; i < plugin_guis->total; i++)
4970         {
4971 // Pointer comparison
4972                 plugin_guis->get(i)->save_defaults();
4973         }
4974         awindow->save_defaults(defaults);
4975
4976         defaults->save();
4977         return 0;
4978 }
4979
4980 int MWindow::run_script(FileXML *script)
4981 {
4982         int result = 0, result2 = 0;
4983         while(!result && !result2)
4984         {
4985                 result = script->read_tag();
4986                 if(!result)
4987                 {
4988                         if(script->tag.title_is("new_project"))
4989                         {
4990 // Run new in immediate mode.
4991 //                              gui->mainmenu->new_project->run_script(script);
4992                         }
4993                         else
4994                         if(script->tag.title_is("record"))
4995                         {
4996 // Run record as a thread.  It is a terminal command.
4997                                 ;
4998 // Will read the complete scipt file without letting record read it if not
4999 // terminated.
5000                                 result2 = 1;
5001                         }
5002                         else
5003                         {
5004                                 printf("MWindow::run_script: Unrecognized command: %s\n",script->tag.get_title() );
5005                         }
5006                 }
5007         }
5008         return result2;
5009 }
5010
5011 // ================================= synchronization
5012
5013
5014 int MWindow::interrupt_indexes()
5015 {
5016         mainprogress->cancelled = 1;
5017         mainindexes->interrupt_build();
5018         return 0;
5019 }
5020
5021
5022
5023 void MWindow::next_time_format()
5024 {
5025         switch( edl->session->time_format ) {
5026         case TIME_HMS:          edl->session->time_format = TIME_HMSF;         break;
5027         case TIME_HMSF:         edl->session->time_format = TIME_TIMECODE;     break;
5028         case TIME_TIMECODE:     edl->session->time_format = TIME_FRAMES;       break;
5029         case TIME_FRAMES:       edl->session->time_format = TIME_SAMPLES;      break;
5030         case TIME_SAMPLES:      edl->session->time_format = TIME_SAMPLES_HEX;  break;
5031         case TIME_SAMPLES_HEX:  edl->session->time_format = TIME_SECONDS;      break;
5032         case TIME_SECONDS:      edl->session->time_format = TIME_FEET_FRAMES;  break;
5033         case TIME_FEET_FRAMES:  edl->session->time_format = TIME_HMS;          break;
5034         }
5035         time_format_common();
5036 }
5037
5038 void MWindow::prev_time_format()
5039 {
5040         switch( edl->session->time_format ) {
5041         case TIME_HMS:          edl->session->time_format = TIME_FEET_FRAMES;  break;
5042         case TIME_HMSF:         edl->session->time_format = TIME_HMS;          break;
5043         case TIME_TIMECODE:     edl->session->time_format = TIME_HMSF;         break;
5044         case TIME_FRAMES:       edl->session->time_format = TIME_TIMECODE;     break;
5045         case TIME_SAMPLES:      edl->session->time_format = TIME_FRAMES;       break;
5046         case TIME_SAMPLES_HEX:  edl->session->time_format = TIME_SAMPLES;      break;
5047         case TIME_SECONDS:      edl->session->time_format = TIME_SAMPLES_HEX;  break;
5048         case TIME_FEET_FRAMES:  edl->session->time_format = TIME_SECONDS;      break;
5049         }
5050
5051         time_format_common();
5052 }
5053
5054 void MWindow::time_format_common()
5055 {
5056         gui->lock_window("MWindow::next_time_format");
5057         gui->redraw_time_dependancies();
5058
5059
5060         char string[BCTEXTLEN], string2[BCTEXTLEN];
5061         sprintf(string, _("Using %s"), Units::print_time_format(edl->session->time_format, string2));
5062         gui->show_message(string);
5063         gui->flush();
5064         gui->unlock_window();
5065 }
5066
5067
5068 int MWindow::set_filename(const char *filename)
5069 {
5070         if( filename != session->filename )
5071                 strcpy(session->filename, filename);
5072         if( filename != edl->path )
5073                 strcpy(edl->path, filename);
5074         return set_titlebar(filename);
5075 }
5076
5077 int MWindow::set_titlebar(const char *filename)
5078 {
5079         if( !gui ) return 0;
5080         if( filename[0] ) {
5081                 FileSystem dir;
5082                 char string[BCTEXTLEN], string2[BCTEXTLEN];
5083                 dir.extract_name(string, filename);
5084                 sprintf(string2, PROGRAM_NAME ": %s", string);
5085                 gui->set_title(string2);
5086         }
5087         else
5088                 gui->set_title(PROGRAM_NAME);
5089         return 0;
5090 }
5091
5092
5093 int MWindow::set_loop_boundaries()
5094 {
5095         double start = edl->local_session->get_selectionstart();
5096         double end = edl->local_session->get_selectionend();
5097
5098         if(start !=
5099                 end)
5100         {
5101                 ;
5102         }
5103         else
5104         if(edl->tracks->total_length())
5105         {
5106                 start = 0;
5107                 end = edl->tracks->total_length();
5108         }
5109         else
5110         {
5111                 start = end = 0;
5112         }
5113
5114         if(edl->local_session->loop_playback && start != end)
5115         {
5116                 edl->local_session->loop_start = start;
5117                 edl->local_session->loop_end = end;
5118         }
5119         return 0;
5120 }
5121
5122
5123
5124
5125
5126
5127
5128 int MWindow::reset_meters()
5129 {
5130         cwindow->gui->lock_window("MWindow::reset_meters 1");
5131         cwindow->gui->meters->reset_meters();
5132         cwindow->gui->unlock_window();
5133
5134         for(int j = 0; j < vwindows.size(); j++) {
5135                 VWindow *vwindow = vwindows[j];
5136                 if( !vwindow->is_running() ) continue;
5137                 vwindow->gui->lock_window("MWindow::reset_meters 2");
5138                 vwindow->gui->meters->reset_meters();
5139                 vwindow->gui->unlock_window();
5140         }
5141
5142         lwindow->gui->lock_window("MWindow::reset_meters 3");
5143         lwindow->gui->panel->reset_meters();
5144         lwindow->gui->unlock_window();
5145
5146         gui->lock_window("MWindow::reset_meters 4");
5147         gui->reset_meters();
5148         gui->unlock_window();
5149         return 0;
5150 }
5151
5152
5153 void MWindow::resync_guis()
5154 {
5155 // Update GUIs
5156         restart_brender();
5157         gui->lock_window("MWindow::resync_guis");
5158         gui->update(1, NORMAL_DRAW, 1, 1, 1, 1, 0);
5159         gui->unlock_window();
5160
5161         cwindow->gui->lock_window("MWindow::resync_guis");
5162         cwindow->gui->resize_event(cwindow->gui->get_w(),
5163                 cwindow->gui->get_h());
5164         int channels = edl->session->audio_channels;
5165         cwindow->gui->meters->set_meters(channels, 1);
5166         cwindow->gui->flush();
5167         cwindow->gui->unlock_window();
5168
5169         for(int i = 0; i < vwindows.size(); i++) {
5170                 VWindow *vwindow = vwindows[i];
5171                 if( !vwindow->is_running() ) continue;
5172                 vwindow->gui->lock_window("MWindow::resync_guis");
5173                 vwindow->gui->resize_event(vwindow->gui->get_w(),
5174                         vwindow->gui->get_h());
5175                 vwindow->gui->meters->set_meters(channels, 1);
5176                 vwindow->gui->flush();
5177                 vwindow->gui->unlock_window();
5178         }
5179
5180         lwindow->gui->lock_window("MWindow::resync_guis");
5181         lwindow->gui->panel->set_meters(channels, 1);
5182         lwindow->gui->flush();
5183         lwindow->gui->unlock_window();
5184 #ifdef GLx4
5185 // Warn user
5186         if(((edl->session->output_w % 4) ||
5187                 (edl->session->output_h % 4)) &&
5188                 edl->session->playback_config->vconfig->driver == PLAYBACK_X11_GL)
5189         {
5190                 MainError::show_error(
5191                         _("This project's dimensions are not multiples of 4 so\n"
5192                         "it can't be rendered by OpenGL."));
5193         }
5194 #endif
5195
5196 // Flash frame
5197         sync_parameters(CHANGE_ALL);
5198 }
5199
5200 int MWindow::select_asset(Asset *asset, int vstream, int astream, int delete_tracks)
5201 {
5202         File *file = new File;
5203         EDLSession *session = edl->session;
5204         double old_framerate = session->frame_rate;
5205         double old_samplerate = session->sample_rate;
5206         int old_auto_keyframes = session->auto_keyframes;
5207         session->auto_keyframes = 0;
5208         int result = file->open_file(preferences, asset, 1, 0);
5209         if( !result && delete_tracks > 0 )
5210                 undo_before();
5211         int video_layers = asset->get_video_layers();
5212         if( !result && asset->video_data && vstream < video_layers ) {
5213                 // try to get asset up to date, may fail
5214                 file->select_video_stream(asset, vstream);
5215                 // either way use what was/is there.
5216                 double framerate = asset->get_frame_rate();
5217                 int width = asset->get_w();
5218                 int height = asset->get_h();
5219 #ifdef GLx4
5220                 // must be multiple of 4 for opengl
5221                 width = (width+3) & ~3;  height = (height+3) & ~3;
5222 #endif
5223                 int driver = session->playback_config->vconfig->driver;
5224                 int color_model = file->get_best_colormodel(asset, driver);
5225 //              color_model = BC_CModels::is_yuv(color_model) ?
5226 //                              BC_CModels::has_alpha(color_model) ? BC_YUVA8888 : BC_YUV888 :
5227 //                      BC_CModels::is_float(color_model) ?
5228 //                              BC_CModels::has_alpha(color_model) ? BC_RGBA_FLOAT : BC_RGB_FLOAT :
5229 //                              BC_CModels::has_alpha(color_model) ? BC_RGBA8888 : BC_RGB888;
5230 // have alpha for now
5231                 color_model = BC_CModels::is_yuv(color_model) ?  BC_YUVA8888 :
5232                         BC_CModels::is_float(color_model) ? BC_RGBA_FLOAT : BC_RGBA8888;
5233                 session->color_model = color_model;
5234                 session->output_w = width;
5235                 session->output_h = height;
5236                 session->frame_rate = framerate;
5237                 session->interlace_mode = asset->interlace_mode;
5238                 // not, asset->actual_width/actual_height
5239                 asset->width = session->output_w;
5240                 asset->height = session->output_h;
5241                 asset->frame_rate = session->frame_rate;
5242                 
5243                 create_aspect_ratio(session->aspect_w, session->aspect_h,
5244                         session->output_w, session->output_h);
5245         float ar = asset->aspect_ratio;
5246         if (ar) {
5247         //printf ("Aspect ratio from asset: %f \n", ar);
5248         if( EQUIV(ar, 1.3333) ) { session->aspect_w = 4;  session->aspect_h = 3;  }
5249         if( EQUIV(ar, 1.7777) ) { session->aspect_w = 16; session->aspect_h = 9;   }
5250         if( EQUIV(ar, 2.1111) ) { session->aspect_w = 19; session->aspect_h = 9;  }
5251         if( EQUIV(ar, 2.2222) ) { session->aspect_w = 20; session->aspect_h = 9;   }
5252         if( EQUIV(ar, 2.3333) ) { session->aspect_w = 21; session->aspect_h = 9;   }
5253         if( EQUIV(ar, 2.370370) ) { session->aspect_w = 64; session->aspect_h = 27; }
5254         }
5255                         
5256                         
5257                 Track *track = edl->tracks->first;
5258                 for( Track *next_track=0; track; track=next_track ) {
5259                         next_track = track->next;
5260                         if( track->data_type != TRACK_VIDEO ) continue;
5261                         if( delete_tracks ) {
5262                                 Edit *edit = track->edits->first;
5263                                 for( Edit *next_edit=0; edit; edit=next_edit ) {
5264                                         next_edit = edit->next;
5265                                         if( edit->channel != vstream ||
5266                                             !edit->asset || !edit->asset->is_asset ||
5267                                             !asset->equivalent(*edit->asset,1,1,edl) )
5268                                                 delete edit;
5269                                 }
5270                         }
5271                         if( track->edits->first ) {
5272                                 track->track_w = edl->session->output_w;
5273                                 track->track_h = edl->session->output_h;
5274                         }
5275                         else if( delete_tracks )
5276                                 edl->tracks->delete_track(track, 0);
5277                 }
5278                 edl->retrack();
5279                 edl->resample(old_framerate, session->frame_rate, TRACK_VIDEO);
5280         }
5281         if( !result && asset->audio_data && asset->channels > 0 ) {
5282                 session->sample_rate = asset->get_sample_rate();
5283                 int64_t channel_mask = 0;
5284                 int astrm = !asset->video_data || vstream >= video_layers ? -1 :
5285                         file->get_audio_for_video(vstream, astream, channel_mask);
5286                 if( astrm >= 0 ) file->select_audio_stream(asset, astrm);
5287                 if( astrm < 0 || !channel_mask ) channel_mask = (1<<asset->channels)-1;
5288                 int channels = 0;
5289                 for( uint64_t mask=channel_mask; mask!=0; mask>>=1 ) channels += mask & 1;
5290                 if( channels < 1 ) channels = 1;
5291                 if( channels > 6 ) channels = 6;
5292                 session->audio_tracks = session->audio_channels = channels;
5293
5294                 int *achannel_positions = preferences->channel_positions[session->audio_channels-1];
5295                 memcpy(&session->achannel_positions, achannel_positions, sizeof(session->achannel_positions));
5296                 remap_audio(MWindow::AUDIO_1_TO_1);
5297
5298                 if( delete_tracks ) {
5299                         Track *track = edl->tracks->first;
5300                         for( Track *next_track=0; track; track=next_track ) {
5301                                 next_track = track->next;
5302                                 if( track->data_type != TRACK_AUDIO ) continue;
5303                                 Edit *edit = track->edits->first;
5304                                         for( Edit *next_edit=0; edit; edit=next_edit ) {
5305                                         next_edit = edit->next;
5306                                         if( !((1<<edit->channel) & channel_mask) ||
5307                                             !edit->asset || !edit->asset->is_asset ||
5308                                             !asset->equivalent(*edit->asset,1,1,edl) )
5309                                                 delete edit;
5310                                 }
5311                                 if( !track->edits->first )
5312                                         edl->tracks->delete_track(track, 0);
5313                         }
5314                 }
5315                 edl->rechannel();
5316                 edl->resample(old_samplerate, session->sample_rate, TRACK_AUDIO);
5317         }
5318         delete file;
5319         session->auto_keyframes = old_auto_keyframes;
5320         if( !result && delete_tracks > 0 ) {
5321                 save_backup();
5322                 undo_after(_("select asset"), LOAD_ALL);
5323         }
5324         resync_guis();
5325         return result;
5326 }
5327
5328 int MWindow::select_asset(int vtrack, int delete_tracks)
5329 {
5330         Track *track = edl->tracks->get(vtrack, TRACK_VIDEO);
5331         if( !track )
5332                 track = edl->tracks->get(vtrack, TRACK_AUDIO);
5333         if( !track ) return 1;
5334         Edit *edit = track->edits->first;
5335         if( !edit ) return 1;
5336         Asset *asset = edit->asset;
5337         if( !asset || !asset->is_asset ) return 1;
5338         return select_asset(asset, edit->channel, 0, delete_tracks);
5339 }
5340
5341 void MWindow::dump_plugindb(FILE *fp)
5342 {
5343         if( !plugindb ) return;
5344         for(int i = 0; i < plugindb->total; i++)
5345                 plugindb->get(i)->dump(fp);
5346 }
5347
5348 FloatAuto* MWindow::get_float_auto(PatchGUI *patch,int idx)
5349 {
5350         Auto *current = 0;
5351         double unit_position = edl->local_session->get_selectionstart(1);
5352         unit_position = patch->track->to_units(unit_position, 0);
5353
5354         FloatAutos *ptr = (FloatAutos*)patch->track->automation->autos[idx];
5355         return (FloatAuto*)ptr->get_prev_auto( (long)unit_position, PLAY_FORWARD, current);
5356 }
5357
5358 IntAuto* MWindow::get_int_auto(PatchGUI *patch,int idx)
5359 {
5360         Auto *current = 0;
5361         double unit_position = edl->local_session->get_selectionstart(1);
5362         unit_position = patch->track->to_units(unit_position, 0);
5363
5364         IntAutos *ptr = (IntAutos*)patch->track->automation->autos[idx];
5365         return (IntAuto*)ptr->get_prev_auto( (long)unit_position, PLAY_FORWARD, current);
5366 }
5367
5368 PanAuto* MWindow::get_pan_auto(PatchGUI *patch)
5369 {
5370         Auto *current = 0;
5371         double unit_position = edl->local_session->get_selectionstart(1);
5372         unit_position = patch->track->to_units(unit_position, 0);
5373
5374         PanAutos *ptr = (PanAutos*)patch->track->automation->autos[AUTOMATION_PAN];
5375         return (PanAuto*)ptr->get_prev_auto( (long)unit_position, PLAY_FORWARD, current);
5376 }
5377
5378 PatchGUI *MWindow::get_patchgui(Track *track)
5379 {
5380         PatchGUI *patchgui = 0;
5381         TimelinePane **panes = gui->pane;
5382         for( int i=0; i<TOTAL_PANES && !patchgui; ++i ) {
5383                 if( !panes[i] ) continue;
5384                 PatchBay *patchbay = panes[i]->patchbay;
5385                 if( !patchbay ) continue;
5386                 for( int j=0; j<patchbay->patches.total && !patchgui; ++j ) {
5387                         if( patchbay->patches.values[j]->track == track )
5388                                 patchgui = patchbay->patches.values[j];
5389                 }
5390         }
5391         return patchgui;
5392 }
5393
5394 int MWindow::get_cpus(int out_w, int out_h)
5395 {
5396         if( !out_w ) out_w = edl->session->output_w;
5397         if( !out_h ) out_h = edl->session->output_h;
5398         int cpus = out_w*out_h/0x80000 + 1;
5399         if( cpus > preferences->processors )
5400                 cpus = preferences->processors;
5401         return cpus;
5402 }
5403 int MWindow::get_cpus()
5404 {
5405         return get_cpus(edl->session->output_w, edl->session->output_h);
5406 }
5407
5408 void MWindow::draw_trackmovement()
5409 {
5410         if( !redraw_tracks )
5411                 redraw_tracks = new DrawTrackMovement(this);
5412         redraw_tracks->start();
5413 }
5414
5415 DrawTrackMovement::DrawTrackMovement(MWindow *mwindow)
5416  : Thread(1, 0, 0)
5417 {
5418         this->mwindow = mwindow;
5419 }
5420 DrawTrackMovement::~DrawTrackMovement()
5421 {
5422         join();
5423 }
5424
5425 void DrawTrackMovement::run()
5426 {
5427         mwindow->gui->lock_window("DrawTrackMovement::run");
5428         mwindow->edl->tracks->update_y_pixels(mwindow->theme);
5429         mwindow->gui->draw_trackmovement();
5430         mwindow->gui->unlock_window();
5431 }
5432
5433
5434 ConfirmRefWindow::ConfirmRefWindow(MWindow *mwindow, char *path,
5435                 int px, int py, int cw, int ch)
5436  : BC_Window(_(PROGRAM_NAME ": Confirm update"), px, py, cw, ch, cw, ch)
5437 {
5438         this->mwindow = mwindow;
5439         this->path = path;
5440 // *** CONTEXT_HELP ***
5441         context_help_set_keyword("File by Reference");
5442 }
5443
5444 ConfirmRefWindow::~ConfirmRefWindow()
5445 {
5446 }
5447
5448 void ConfirmRefWindow::create_objects()
5449 {
5450         lock_window("ConfirmRefWindow::create_objects()");
5451         int x = xS(10), y = yS(10), pad = yS(5);
5452         BC_Title *title;
5453         add_subwindow(title = new BC_Title(x, y, _("FileREF not updated:")));
5454         y += title->get_h() + pad;
5455         BC_TextBox *text_box;
5456         add_subwindow(text_box = new BC_TextBox(x,y, get_w()-2*x, 1, path));
5457         y += text_box->get_h() + 2*pad;
5458         add_subwindow(title = new BC_Title(x, y, _("Save file ref changes?")));
5459         add_subwindow(new BC_OKButton(this));
5460         add_subwindow(new BC_CancelButton(this));
5461         show_window();
5462         unlock_window();
5463 }
5464