3fa755bcdcf898723d0e466b725dd7d4e2157b72
[goodguy/history.git] / cinelerra-5.1 / cinelerra / batchrender.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "asset.h"
23 #include "batchrender.h"
24 #include "bcdisplayinfo.h"
25 #include "bcsignals.h"
26 #include "confirmsave.h"
27 #include "cstrdup.h"
28 #include "bchash.h"
29 #include "edits.h"
30 #include "edit.h"
31 #include "edl.h"
32 #include "edlsession.h"
33 #include "errorbox.h"
34 #include "file.h"
35 #include "filesystem.h"
36 #include "filexml.h"
37 #include "keyframe.h"
38 #include "keys.h"
39 #include "labels.h"
40 #include "language.h"
41 #include "mainerror.h"
42 #include "mainundo.h"
43 #include "mainsession.h"
44 #include "mutex.h"
45 #include "mwindow.h"
46 #include "mwindowgui.h"
47 #include "packagedispatcher.h"
48 #include "packagerenderer.h"
49 #include "plugin.h"
50 #include "pluginset.h"
51 #include "preferences.h"
52 #include "render.h"
53 #include "theme.h"
54 #include "tracks.h"
55 #include "transportque.h"
56 #include "vframe.h"
57
58
59 static const char *list_titles[] =
60 {
61         _("Enabled"),
62         _("Output"),
63         _("EDL"),
64         _("Elapsed")
65 };
66
67 static int list_widths[] =
68 {
69         50,
70         100,
71         200,
72         100
73 };
74
75 BatchRenderMenuItem::BatchRenderMenuItem(MWindow *mwindow)
76  : BC_MenuItem(_("Batch Render..."), _("Shift-B"), 'B')
77 {
78         set_shift(1);
79         this->mwindow = mwindow;
80 }
81
82 int BatchRenderMenuItem::handle_event()
83 {
84         mwindow->batch_render->start();
85         return 1;
86 }
87
88
89
90
91
92
93
94
95 BatchRenderJob::BatchRenderJob(Preferences *preferences)
96 {
97         this->preferences = preferences;
98         asset = new Asset;
99         edl_path[0] = 0;
100         strategy = 0;
101         enabled = 1;
102         elapsed = 0;
103 }
104
105 BatchRenderJob::~BatchRenderJob()
106 {
107         asset->Garbage::remove_user();
108 }
109
110 void BatchRenderJob::copy_from(BatchRenderJob *src)
111 {
112         asset->copy_from(src->asset, 0);
113         strcpy(edl_path, src->edl_path);
114         strategy = src->strategy;
115         enabled = src->enabled;
116         elapsed = 0;
117 }
118
119 void BatchRenderJob::load(FileXML *file)
120 {
121         int result = 0;
122
123         edl_path[0] = 0;
124         file->tag.get_property("EDL_PATH", edl_path);
125         strategy = file->tag.get_property("STRATEGY", strategy);
126         enabled = file->tag.get_property("ENABLED", enabled);
127         elapsed = file->tag.get_property("ELAPSED", elapsed);
128         fix_strategy();
129
130         result = file->read_tag();
131         if(!result)
132         {
133                 if(file->tag.title_is("ASSET"))
134                 {
135                         file->tag.get_property("SRC", asset->path);
136                         asset->read(file, 0);
137 // The compression parameters are stored in the defaults to reduce
138 // coding maintenance.  The defaults must now be stuffed into the XML for
139 // unique storage.
140                         BC_Hash defaults;
141                         defaults.load_string(file->read_text());
142                         asset->load_defaults(&defaults,
143                                 "",
144                                 0,
145                                 1,
146                                 0,
147                                 0,
148                                 0);
149                 }
150         }
151 }
152
153 void BatchRenderJob::save(FileXML *file)
154 {
155         file->tag.set_property("EDL_PATH", edl_path);
156         file->tag.set_property("STRATEGY", strategy);
157         file->tag.set_property("ENABLED", enabled);
158         file->tag.set_property("ELAPSED", elapsed);
159         file->append_tag();
160         file->append_newline();
161         asset->write(file,
162                 0,
163                 "");
164
165 // The compression parameters are stored in the defaults to reduce
166 // coding maintenance.  The defaults must now be stuffed into the XML for
167 // unique storage.
168         BC_Hash defaults;
169         asset->save_defaults(&defaults,
170                 "",
171                 0,
172                 1,
173                 0,
174                 0,
175                 0);
176         char *string;
177         defaults.save_string(string);
178         file->append_text(string);
179         free(string);
180         file->tag.set_title("/JOB");
181         file->append_tag();
182         file->append_newline();
183 }
184
185 void BatchRenderJob::fix_strategy()
186 {
187         strategy = Render::fix_strategy(strategy, preferences->use_renderfarm);
188 }
189
190
191
192
193
194
195
196
197
198
199 BatchRenderThread::BatchRenderThread(MWindow *mwindow)
200  : BC_DialogThread()
201 {
202         this->mwindow = mwindow;
203         current_job = 0;
204         rendering_job = -1;
205         is_rendering = 0;
206         default_job = 0;
207         boot_defaults = 0;
208         preferences = 0;
209         warn = 1;
210         render = 0;
211         file_entries = 0;
212 }
213
214 BatchRenderThread::BatchRenderThread()
215  : BC_DialogThread()
216 {
217         mwindow = 0;
218         current_job = 0;
219         rendering_job = -1;
220         is_rendering = 0;
221         default_job = 0;
222         boot_defaults = 0;
223         preferences = 0;
224         warn = 1;
225         render = 0;
226         file_entries = 0;
227 }
228
229 BatchRenderThread::~BatchRenderThread()
230 {
231         close_window();
232         delete boot_defaults;
233         delete preferences;
234         delete render;
235         if( file_entries ) {
236                 file_entries->remove_all_objects();
237                 delete file_entries;
238         }
239 }
240
241 void BatchRenderThread::reset(int warn)
242 {
243         if( warn ) this->warn = 1;
244         current_job = 0;
245         rendering_job = -1;
246         delete default_job;  default_job = 0;
247         jobs.remove_all_objects();
248         if(file_entries) {
249                 file_entries->remove_all_objects();
250                 delete file_entries;  file_entries = 0;
251         }
252 }
253
254 void BatchRenderThread::handle_close_event(int result)
255 {
256 // Save settings
257         char path[BCTEXTLEN];
258         path[0] = 0;
259         save_jobs(path);
260         save_defaults(mwindow->defaults);
261         reset();
262 }
263
264 BC_Window* BatchRenderThread::new_gui()
265 {
266         current_start = 0.0;
267         current_end = 0.0;
268         default_job = new BatchRenderJob(mwindow->preferences);
269
270
271         if(!file_entries)
272         {
273                 file_entries = new ArrayList<BC_ListBoxItem*>;
274                 FileSystem fs;
275                 char string[BCTEXTLEN];
276         // Load current directory
277                 fs.update(getcwd(string, BCTEXTLEN));
278                 for(int i = 0; i < fs.total_files(); i++)
279                 {
280                         file_entries->append(
281                                 new BC_ListBoxItem(
282                                         fs.get_entry(i)->get_name()));
283                 }
284         }
285
286         char path[BCTEXTLEN];
287         path[0] = 0;
288         load_jobs(path, mwindow->preferences);
289         load_defaults(mwindow->defaults);
290         this->gui = new BatchRenderGUI(mwindow,
291                 this,
292                 mwindow->session->batchrender_x,
293                 mwindow->session->batchrender_y,
294                 mwindow->session->batchrender_w,
295                 mwindow->session->batchrender_h);
296         this->gui->create_objects();
297         return this->gui;
298 }
299
300
301 void BatchRenderThread::load_jobs(char *path, Preferences *preferences)
302 {
303         FileXML file;
304         int result = 0;
305
306         jobs.remove_all_objects();
307         if(path[0])
308                 file.read_from_file(path);
309         else
310                 file.read_from_file(create_path(path));
311
312         while(!result)
313         {
314                 if(!(result = file.read_tag()))
315                 {
316                         if(file.tag.title_is("JOBS"))
317                         {
318                                 warn = file.tag.get_property("WARN", 1);
319                         }
320                         else if(file.tag.title_is("JOB"))
321                         {
322                                 BatchRenderJob *job;
323                                 jobs.append(job = new BatchRenderJob(preferences));
324                                 job->load(&file);
325                         }
326                 }
327         }
328 }
329
330 void BatchRenderThread::save_jobs(char *path)
331 {
332         FileXML file;
333         file.tag.set_title("JOBS");
334         file.tag.set_property("WARN", warn);
335         file.append_tag();
336         file.append_newline();
337
338         for(int i = 0; i < jobs.total; i++)
339         {
340                 file.tag.set_title("JOB");
341                 jobs.values[i]->save(&file);
342         }
343
344         if(path[0])
345                 file.write_to_file(path);
346         else
347                 file.write_to_file(create_path(path));
348 }
349
350 void BatchRenderThread::load_defaults(BC_Hash *defaults)
351 {
352         if(default_job)
353         {
354                 default_job->asset->load_defaults(defaults,
355                         "BATCHRENDER_", 1, 1, 1, 1, 1);
356                 default_job->fix_strategy();
357         }
358
359         for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
360         {
361                 char string[BCTEXTLEN];
362                 sprintf(string, "BATCHRENDER_COLUMN%d", i);
363                 column_width[i] = defaults->get(string, list_widths[i]);
364         }
365 }
366
367 void BatchRenderThread::save_defaults(BC_Hash *defaults)
368 {
369         if(default_job)
370         {
371                 default_job->asset->save_defaults(defaults,
372                         "BATCHRENDER_", 1, 1, 1, 1, 1);
373                 defaults->update("BATCHRENDER_STRATEGY", default_job->strategy);
374         }
375         for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
376         {
377                 char string[BCTEXTLEN];
378                 sprintf(string, "BATCHRENDER_COLUMN%d", i);
379                 defaults->update(string, column_width[i]);
380         }
381 //      defaults->update("BATCHRENDER_JOB", current_job);
382         if(mwindow)
383                 mwindow->save_defaults();
384         else
385                 defaults->save();
386 }
387
388 char* BatchRenderThread::create_path(char *string)
389 {
390         FileSystem fs;
391         sprintf(string, "%s/", File::get_config_path());
392         fs.complete_path(string);
393         strcat(string, BATCH_PATH);
394         return string;
395 }
396
397 void BatchRenderThread::new_job()
398 {
399         BatchRenderJob *result = new BatchRenderJob(mwindow->preferences);
400         result->copy_from(get_current_job());
401         jobs.append(result);
402         current_job = jobs.total - 1;
403         gui->create_list(1);
404         gui->change_job();
405 }
406
407 void BatchRenderThread::delete_job()
408 {
409         if(current_job < jobs.total && current_job >= 0)
410         {
411                 jobs.remove_object_number(current_job);
412                 if(current_job > 0) current_job--;
413                 gui->create_list(1);
414                 gui->change_job();
415         }
416 }
417
418 void BatchRenderThread::use_current_edl()
419 {
420 // printf("BatchRenderThread::use_current_edl %d %p %s\n",
421 // __LINE__,
422 // mwindow->edl->path,
423 // mwindow->edl->path);
424
425         strcpy(get_current_edl(), mwindow->edl->path);
426         gui->create_list(1);
427         gui->edl_path_text->update(get_current_edl());
428 }
429
430 void BatchRenderThread::update_selected_edl()
431 {
432         FileXML xml_file;
433         char *path = get_current_edl();
434         EDL *edl = mwindow->edl;
435         edl->save_xml(&xml_file, path, 0, 0);
436         xml_file.terminate_string();
437         if( xml_file.write_to_file(path) ) {
438                 char msg[BCTEXTLEN];
439                 sprintf(msg, _("Unable to save: %s"), path);
440                 MainError::show_error(msg);
441         }
442 }
443
444 BatchRenderJob* BatchRenderThread::get_current_job()
445 {
446         BatchRenderJob *result;
447         if(current_job >= jobs.total || current_job < 0)
448         {
449                 result = default_job;
450         }
451         else
452         {
453                 result = jobs.values[current_job];
454         }
455         return result;
456 }
457
458
459 Asset* BatchRenderThread::get_current_asset()
460 {
461         return get_current_job()->asset;
462 }
463
464 char* BatchRenderThread::get_current_edl()
465 {
466         return get_current_job()->edl_path;
467 }
468
469
470 // Test EDL files for existence
471 int BatchRenderThread::test_edl_files()
472 {
473         int not_equiv = 0, ret = 0;
474         const char *path = 0;
475
476         for( int i=0; !ret && i<jobs.size(); ++i ) {
477                 if( !jobs.values[i]->enabled ) continue;
478                 const char *path = jobs.values[i]->edl_path;
479                 int is_script = *path == '@' ? 1 : 0;
480                 if( is_script ) ++path;
481                 FILE *fp = fopen(path, "r");
482                 if( fp ) {
483                         if( warn && mwindow && !is_script ) {
484                                 fseek(fp, 0, SEEK_END);
485                                 int64_t sz = ftell(fp);
486                                 fseek(fp, 0, SEEK_SET);
487                                 char *bfr = new char[sz+1];
488                                 int64_t len = fread(bfr, 1, sz+1, fp);
489                                 if( len == sz ) {
490                                         FileXML file;  file.set_shared_input(bfr, len);
491                                         EDL *edl = new EDL; edl->create_objects();
492                                         edl->load_xml(&file, LOAD_ALL);
493                                         double pos = edl->equivalent_output(mwindow->edl);
494                                         if( pos >= 0 ) ++not_equiv;
495                                         edl->remove_user();
496                                 }
497                                 else
498                                         ret = 1;
499                                 delete [] bfr;
500                         }
501                         fclose(fp);
502                 }
503                 else
504                         ret = 1;
505         }
506
507         if( ret ) {
508                 char string[BCTEXTLEN];
509                 sprintf(string, _("EDL %s not found.\n"), path);
510                 if( mwindow ) {
511                         ErrorBox error_box(_(PROGRAM_NAME ": Error"),
512                                 mwindow->gui->get_abs_cursor_x(1),
513                                 mwindow->gui->get_abs_cursor_y(1));
514                         error_box.create_objects(string);
515                         error_box.run_window();
516                         gui->button_enable();
517                 }
518                 else {
519                         fprintf(stderr, "%s", string);
520                 }
521                 is_rendering = 0;
522         }
523         else if( warn && mwindow && not_equiv > 0 ) {
524                 fprintf(stderr, _("%d job EDLs do not match session edl\n"), not_equiv);
525                 char string[BCTEXTLEN], *sp = string;
526                 sp += sprintf(sp, _("%d job EDLs do not match session edl\n"),not_equiv);
527                 sp += sprintf(sp, _("press cancel to abandon batch render"));
528                 mwindow->show_warning(&warn, string);
529                 if( mwindow->wait_warning() ) {
530                         gui->button_enable();
531                         is_rendering = 0;
532                         ret = 1;
533                 }
534                 gui->warning->update(warn);
535         }
536
537         return ret;
538 }
539
540 void BatchRenderThread::calculate_dest_paths(ArrayList<char*> *paths,
541         Preferences *preferences)
542 {
543         for(int i = 0; i < jobs.total; i++)
544         {
545                 BatchRenderJob *job = jobs.values[i];
546                 if(job->enabled && *job->edl_path != '@')
547                 {
548                         PackageDispatcher *packages = new PackageDispatcher;
549
550 // Load EDL
551                         TransportCommand *command = new TransportCommand;
552                         FileXML *file = new FileXML;
553                         file->read_from_file(job->edl_path);
554
555 // Use command to calculate range.
556                         command->command = NORMAL_FWD;
557                         command->get_edl()->load_xml(file,
558                                 LOAD_ALL);
559                         command->change_type = CHANGE_ALL;
560                         command->set_playback_range();
561                         command->playback_range_adjust_inout();
562
563 // Create test packages
564                         packages->create_packages(mwindow,
565                                 command->get_edl(),
566                                 preferences,
567                                 job->strategy,
568                                 job->asset,
569                                 command->start_position,
570                                 command->end_position,
571                                 0);
572
573 // Append output paths allocated to total
574                         packages->get_package_paths(paths);
575
576 // Delete package harness
577                         delete packages;
578                         delete command;
579                         delete file;
580                 }
581         }
582 }
583
584
585 void BatchRenderThread::start_rendering(char *config_path,
586         char *batch_path)
587 {
588         BC_Hash *boot_defaults;
589         Preferences *preferences;
590         Render *render;
591         BC_Signals *signals = new BC_Signals;
592         // XXX the above stuff is leaked,
593 //PRINT_TRACE
594 // Initialize stuff which MWindow does.
595         signals->initialize();
596         MWindow::init_defaults(boot_defaults, config_path);
597         load_defaults(boot_defaults);
598         preferences = new Preferences;
599         preferences->load_defaults(boot_defaults);
600         MWindow::init_plugins(0, preferences);
601         char font_path[BCTEXTLEN];
602         strcpy(font_path, preferences->plugin_dir);
603         strcat(font_path, "/" FONT_SEARCHPATH);
604         BC_Resources::init_fontconfig(font_path);
605         BC_WindowBase::get_resources()->vframe_shm = 1;
606
607 //PRINT_TRACE
608         load_jobs(batch_path, preferences);
609         save_jobs(batch_path);
610         save_defaults(boot_defaults);
611
612 //PRINT_TRACE
613 // Test EDL files for existence
614         if(test_edl_files()) return;
615
616 //PRINT_TRACE
617
618 // Predict all destination paths
619         ArrayList<char*> paths;
620         paths.set_array_delete();
621         calculate_dest_paths(&paths, preferences);
622
623 //PRINT_TRACE
624         int result = ConfirmSave::test_files(0, &paths);
625         paths.remove_all_objects();
626 // Abort on any existing file because it's so hard to set this up.
627         if(result) return;
628
629 //PRINT_TRACE
630         render = new Render(0);
631 //PRINT_TRACE
632         render->start_batches(&jobs,
633                 boot_defaults,
634                 preferences);
635 //PRINT_TRACE
636 }
637
638 void BatchRenderThread::start_rendering()
639 {
640         if(is_rendering) return;
641
642         is_rendering = 1;
643         char path[BCTEXTLEN];
644         path[0] = 0;
645         save_jobs(path);
646         save_defaults(mwindow->defaults);
647         gui->button_disable();
648
649 // Test EDL files for existence
650         if(test_edl_files()) return;
651
652 // Predict all destination paths
653         ArrayList<char*> paths;
654         calculate_dest_paths(&paths,
655                 mwindow->preferences);
656
657 // Test destination files for overwrite
658         int result = ConfirmSave::test_files(mwindow, &paths);
659         paths.remove_all_objects();
660
661 // User cancelled
662         if(result)
663         {
664                 is_rendering = 0;
665                 gui->button_enable();
666                 return;
667         }
668
669         mwindow->render->start_batches(&jobs);
670 }
671
672 void BatchRenderThread::stop_rendering()
673 {
674         if(!is_rendering) return;
675         mwindow->render->stop_operation();
676         is_rendering = 0;
677 }
678
679 void BatchRenderThread::update_active(int number)
680 {
681         gui->lock_window("BatchRenderThread::update_active");
682         if(number >= 0)
683         {
684                 current_job = number;
685                 rendering_job = number;
686         }
687         else
688         {
689                 rendering_job = -1;
690                 is_rendering = 0;
691         }
692         gui->create_list(1);
693         gui->unlock_window();
694 }
695
696 void BatchRenderThread::update_done(int number,
697         int create_list,
698         double elapsed_time)
699 {
700         gui->lock_window("BatchRenderThread::update_done");
701         if(number < 0)
702         {
703                 gui->button_enable();
704         }
705         else
706         {
707                 jobs.values[number]->enabled = 0;
708                 jobs.values[number]->elapsed = elapsed_time;
709                 if(create_list) gui->create_list(1);
710         }
711         gui->unlock_window();
712 }
713
714 void BatchRenderThread::move_batch(int src, int dst)
715 {
716         BatchRenderJob *src_job = jobs.values[src];
717         if(dst < 0) dst = jobs.total - 1;
718
719         if(dst != src)
720         {
721                 for(int i = src; i < jobs.total - 1; i++)
722                         jobs.values[i] = jobs.values[i + 1];
723 //              if(dst > src) dst--;
724                 for(int i = jobs.total - 1; i > dst; i--)
725                         jobs.values[i] = jobs.values[i - 1];
726                 jobs.values[dst] = src_job;
727                 gui->create_list(1);
728         }
729 }
730
731
732
733
734
735
736
737 BatchRenderGUI::BatchRenderGUI(MWindow *mwindow,
738         BatchRenderThread *thread, int x, int y, int w, int h)
739  : BC_Window(_(PROGRAM_NAME ": Batch Render"),
740         x, y, w, h, 50, 50, 1, 0, 1)
741 {
742         this->mwindow = mwindow;
743         this->thread = thread;
744 }
745
746 BatchRenderGUI::~BatchRenderGUI()
747 {
748         lock_window("BatchRenderGUI::~BatchRenderGUI");
749         delete format_tools;
750         unlock_window();
751 }
752
753
754 void BatchRenderGUI::create_objects()
755 {
756         lock_window("BatchRenderGUI::create_objects");
757         mwindow->theme->get_batchrender_sizes(this, get_w(), get_h());
758         create_list(0);
759
760         int x = mwindow->theme->batchrender_x1;
761         int y = 5;
762         int x1 = x, x2 = get_w()/2 + 10; // mwindow->theme->batchrender_x2;
763         int y1 = 5, y2 = 5;
764
765 // output file
766         add_subwindow(output_path_title = new BC_Title(x1, y1, _("Output path:")));
767         y1 += output_path_title->get_h() + mwindow->theme->widget_border;
768
769         format_tools = new BatchFormat(mwindow, this, thread->get_current_asset());
770         format_tools->set_w(get_w() / 2);
771         format_tools->create_objects(x1, y1, 1, 1, 1, 1, 0, 1, 0, 0,
772                         &thread->get_current_job()->strategy, 0); 
773
774 // input EDL
775         add_subwindow(edl_path_title = new BC_Title(x2, y2, _("EDL Path:")));
776         y2 += edl_path_title->get_h() + mwindow->theme->widget_border;
777         
778         x = x2;  y = y2;
779         add_subwindow(edl_path_text = new BatchRenderEDLPath( thread,
780                 x, y, get_w()-x - 40, thread->get_current_edl())); 
781         x =  x2 + edl_path_text->get_w();
782         add_subwindow(edl_path_browse = new BrowseButton(
783                 mwindow, this, edl_path_text, x, y, thread->get_current_edl(),
784                 _("Input EDL"), _("Select an EDL to load:"), 0));
785         y2 = y + edl_path_browse->get_h() + mwindow->theme->widget_border;
786
787         x = x2;  y = y2;
788         add_subwindow(update_selected_edl = new BatchRenderUpdateEDL(thread, x, y));
789         y += update_selected_edl->get_h() + mwindow->theme->widget_border;
790         add_subwindow(use_current_edl = new BatchRenderCurrentEDL(thread, x, y));
791         y += use_current_edl->get_h() + mwindow->theme->widget_border;
792         if( !mwindow->edl || !mwindow->edl->path[0] ) use_current_edl->disable();
793         add_subwindow(new_batch = new BatchRenderNew(thread, x, y));
794         x += new_batch->get_w() + mwindow->theme->widget_border;
795         add_subwindow(delete_batch = new BatchRenderDelete(thread, x, y));
796         x = x2;  y += delete_batch->get_h() + mwindow->theme->widget_border;
797         add_subwindow(savelist_batch = new BatchRenderSaveList(thread, x, y));
798         x += savelist_batch->get_w() + mwindow->theme->widget_border;
799         add_subwindow(loadlist_batch = new BatchRenderLoadList(thread, x, y));
800         y += loadlist_batch->get_h() + mwindow->theme->widget_border;
801         add_subwindow(warning = new BatchRenderWarning(thread, x2, y));
802         y2 = y + warning->get_h() + mwindow->theme->widget_border;
803         if( y2 > y1 ) y1 = y2;
804         x = mwindow->theme->batchrender_x1, y = y1;
805
806         add_subwindow(list_title = new BC_Title(x, y, _("Batches to render:")));
807         y += list_title->get_h() + mwindow->theme->widget_border;
808         y1 = get_h();
809         y1 -= 15 + BC_GenericButton::calculate_h() + mwindow->theme->widget_border;
810         add_subwindow(batch_list = new BatchRenderList(thread, x, y,
811                 get_w() - x - 10, y1 - y));
812         y += batch_list->get_h() + mwindow->theme->widget_border;
813
814         add_subwindow(start_button = new BatchRenderStart(thread, x, y));
815         x = get_w() / 2 - BC_GenericButton::calculate_w(this, _("Stop")) / 2;
816         add_subwindow(stop_button = new BatchRenderStop(thread, x, y));
817         x = get_w() - BC_GenericButton::calculate_w(this, _("Close")) - 10;
818         add_subwindow(cancel_button = new BatchRenderCancel(thread, x, y));
819
820         show_window(1);
821         unlock_window();
822 }
823
824 void BatchRenderGUI::button_disable()
825 {
826         new_batch->disable();
827         delete_batch->disable();
828         use_current_edl->disable();
829         update_selected_edl->disable();
830 }
831
832 void BatchRenderGUI::button_enable()
833 {
834         new_batch->enable();
835         delete_batch->enable();
836         if( mwindow->edl && mwindow->edl->path[0] )
837                 use_current_edl->enable();
838         update_selected_edl->enable();
839 }
840
841 int BatchRenderGUI::resize_event(int w, int h)
842 {
843         mwindow->session->batchrender_w = w;
844         mwindow->session->batchrender_h = h;
845         mwindow->theme->get_batchrender_sizes(this, w, h);
846
847         int x = mwindow->theme->batchrender_x1;
848         int y = 5;
849         int x1 = x, x2 = get_w()/2 + 10; // mwindow->theme->batchrender_x2;
850         int y1 = 5, y2 = 5;
851
852 // output file
853         output_path_title->reposition_window(x1, y1);
854         y1 += output_path_title->get_h() + mwindow->theme->widget_border;
855         format_tools->reposition_window(x1, y1);
856
857 // input EDL
858         x = x2, y = y2;
859         edl_path_title->reposition_window(x, y);
860         y += edl_path_title->get_h() + mwindow->theme->widget_border;
861         edl_path_text->reposition_window(x, y, w - x - 40);
862         x += edl_path_text->get_w();
863         edl_path_browse->reposition_window(x, y);
864         y2 = y + edl_path_browse->get_h() + mwindow->theme->widget_border;
865
866         x = x2;  y = y2;
867         update_selected_edl->reposition_window(x, y);
868         y += update_selected_edl->get_h() + mwindow->theme->widget_border;
869         use_current_edl->reposition_window(x, y);
870         y += use_current_edl->get_h() + mwindow->theme->widget_border;
871         new_batch->reposition_window(x, y);
872         x += new_batch->get_w() + mwindow->theme->widget_border;
873         delete_batch->reposition_window(x, y);
874
875         x = x2;  y += delete_batch->get_h() + mwindow->theme->widget_border;
876         savelist_batch->reposition_window(x, y);
877         x += savelist_batch->get_w() + mwindow->theme->widget_border;
878         loadlist_batch->reposition_window(x, y);
879         y += loadlist_batch->get_h() + mwindow->theme->widget_border;
880
881         y1 = 15 + BC_GenericButton::calculate_h() + mwindow->theme->widget_border;
882         y2 = get_h() - y1 - batch_list->get_h();
883         y2 -= list_title->get_h() + mwindow->theme->widget_border;
884
885         x = mwindow->theme->batchrender_x1;  y = y2;
886         list_title->reposition_window(x, y);
887         y += list_title->get_h() + mwindow->theme->widget_border;
888         batch_list->reposition_window(x, y, w - x - 10, h - y - y1);
889         y += batch_list->get_h() + mwindow->theme->widget_border;
890
891         start_button->reposition_window(x, y);
892         x = w / 2 - stop_button->get_w() / 2;
893         stop_button->reposition_window(x, y);
894         x = w - cancel_button->get_w() - 10;
895         cancel_button->reposition_window(x, y);
896         return 1;
897 }
898
899 int BatchRenderGUI::translation_event()
900 {
901         mwindow->session->batchrender_x = get_x();
902         mwindow->session->batchrender_y = get_y();
903         return 1;
904 }
905
906 int BatchRenderGUI::close_event()
907 {
908 // Stop batch rendering
909         unlock_window();
910         thread->stop_rendering();
911         lock_window("BatchRenderGUI::close_event");
912         set_done(1);
913         return 1;
914 }
915
916 void BatchRenderGUI::create_list(int update_widget)
917 {
918         for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
919         {
920                 list_columns[i].remove_all_objects();
921         }
922
923         for(int i = 0; i < thread->jobs.total; i++)
924         {
925                 BatchRenderJob *job = thread->jobs.values[i];
926                 char string[BCTEXTLEN];
927                 BC_ListBoxItem *enabled = new BC_ListBoxItem(job->enabled ?
928                         (char*)"X" :
929                         (char*)" ");
930                 BC_ListBoxItem *item1 = new BC_ListBoxItem(job->asset->path);
931                 BC_ListBoxItem *item2 = new BC_ListBoxItem(job->edl_path);
932                 BC_ListBoxItem *item3;
933                 if(job->elapsed)
934                         item3 = new BC_ListBoxItem(
935                                 Units::totext(string,
936                                         job->elapsed,
937                                         TIME_HMS2));
938                 else
939                         item3 = new BC_ListBoxItem(_("Unknown"));
940                 list_columns[0].append(enabled);
941                 list_columns[1].append(item1);
942                 list_columns[2].append(item2);
943                 list_columns[3].append(item3);
944                 if(i == thread->current_job)
945                 {
946                         enabled->set_selected(1);
947                         item1->set_selected(1);
948                         item2->set_selected(1);
949                         item3->set_selected(1);
950                 }
951                 if(i == thread->rendering_job)
952                 {
953                         enabled->set_color(RED);
954                         item1->set_color(RED);
955                         item2->set_color(RED);
956                         item3->set_color(RED);
957                 }
958         }
959
960         if(update_widget)
961         {
962                 batch_list->update(list_columns,
963                                                 list_titles,
964                                                 thread->column_width,
965                                                 BATCHRENDER_COLUMNS,
966                                                 batch_list->get_xposition(),
967                                                 batch_list->get_yposition(),
968                                                 batch_list->get_highlighted_item(),  // Flat index of item cursor is over
969                                                 1,     // set all autoplace flags to 1
970                                                 1);
971         }
972 }
973
974 void BatchRenderGUI::change_job()
975 {
976         BatchRenderJob *job = thread->get_current_job();
977         format_tools->update(job->asset, &job->strategy);
978         edl_path_text->update(job->edl_path);
979 }
980
981
982
983
984
985
986
987
988 BatchFormat::BatchFormat(MWindow *mwindow,
989                         BatchRenderGUI *gui,
990                         Asset *asset)
991  : FormatTools(mwindow, gui, asset)
992 {
993         this->gui = gui;
994         this->mwindow = mwindow;
995 }
996
997 BatchFormat::~BatchFormat()
998 {
999 }
1000
1001
1002 int BatchFormat::handle_event()
1003 {
1004         gui->create_list(1);
1005         return 1;
1006 }
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018 BatchRenderEDLPath::BatchRenderEDLPath(BatchRenderThread *thread,
1019         int x,
1020         int y,
1021         int w,
1022         char *text)
1023  : BC_TextBox(x,
1024                 y,
1025                 w,
1026                 1,
1027                 text)
1028 {
1029         this->thread = thread;
1030 }
1031
1032
1033 int BatchRenderEDLPath::handle_event()
1034 {
1035 // Suggestions
1036         calculate_suggestions(thread->file_entries);
1037
1038         strcpy(thread->get_current_edl(), get_text());
1039         thread->gui->create_list(1);
1040         return 1;
1041 }
1042
1043
1044
1045
1046
1047
1048 BatchRenderNew::BatchRenderNew(BatchRenderThread *thread,
1049         int x,
1050         int y)
1051  : BC_GenericButton(x, y, _("New"))
1052 {
1053         this->thread = thread;
1054 }
1055
1056 int BatchRenderNew::handle_event()
1057 {
1058         thread->new_job();
1059         return 1;
1060 }
1061
1062 BatchRenderDelete::BatchRenderDelete(BatchRenderThread *thread,
1063         int x,
1064         int y)
1065  : BC_GenericButton(x, y, _("Delete"))
1066 {
1067         this->thread = thread;
1068 }
1069
1070 int BatchRenderDelete::handle_event()
1071 {
1072         thread->delete_job();
1073         return 1;
1074 }
1075
1076
1077
1078 BatchRenderSaveList::BatchRenderSaveList(BatchRenderThread *thread,
1079         int x,
1080         int y)
1081  : BC_GenericButton(x, y, _("Save List"))
1082 {
1083         this->thread = thread;
1084         set_tooltip(_("Save a Batch Render List"));
1085         gui = 0;
1086         startup_lock = new Mutex("BatchRenderSaveList::startup_lock");
1087 }
1088
1089 BatchRenderSaveList::~BatchRenderSaveList()
1090 {
1091         startup_lock->lock("BatchRenderSaveList::~BrowseButton");
1092         if(gui)
1093         {
1094                 gui->lock_window();
1095                 gui->set_done(1);
1096                 gui->unlock_window();
1097         }
1098         startup_lock->unlock();
1099         Thread::join();
1100         delete startup_lock;
1101 }
1102
1103 int BatchRenderSaveList::handle_event()
1104 {
1105         if(Thread::running())
1106         {
1107                 if(gui)
1108                 {
1109                         gui->lock_window();
1110                         gui->raise_window();
1111                         gui->unlock_window();
1112                 }
1113                 return 1;
1114         }
1115         startup_lock->lock("BatchRenderSaveList::handle_event 1");
1116         Thread::start();
1117         startup_lock->lock("BatchRenderSaveList::handle_event 2");
1118         startup_lock->unlock();
1119         return 1;
1120 }
1121
1122 void BatchRenderSaveList::run()
1123 {
1124         char default_path[BCTEXTLEN];
1125         sprintf(default_path, "~");
1126         BC_FileBox filewindow(100,
1127                               100,
1128                               this->thread->mwindow->defaults->get("DEFAULT_BATCHLOADPATH", default_path),
1129                               _("Save Batch Render List"),
1130                               _("Enter a Batch Render filename to save as:"),
1131                               0,
1132                               0,
1133                               0,
1134                               0);
1135
1136         gui = &filewindow;
1137
1138         startup_lock->unlock();
1139         filewindow.create_objects();
1140
1141         int result2 = filewindow.run_window();
1142
1143         if(!result2)
1144         {
1145                 this->thread->save_jobs(filewindow.get_submitted_path());
1146                 this->thread->mwindow->defaults->update("DEFAULT_BATCHLOADPATH", filewindow.get_submitted_path());
1147         }
1148
1149         this->thread->gui->flush();
1150         startup_lock->lock("BatchRenderLoadList::run");
1151         gui = 0;
1152         startup_lock->unlock();
1153 }
1154
1155 int BatchRenderSaveList::keypress_event() {
1156         if (get_keypress() == 's' ||
1157             get_keypress() == 'S') return handle_event();
1158         return 0;
1159 }
1160
1161
1162
1163
1164 BatchRenderLoadList::BatchRenderLoadList(BatchRenderThread *thread,
1165         int x,
1166         int y)
1167   : BC_GenericButton(x, y, _("Load List")),
1168     Thread()
1169 {
1170         this->thread = thread;
1171         set_tooltip(_("Load a previously saved Batch Render List"));
1172         gui = 0;
1173         startup_lock = new Mutex("BatchRenderLoadList::startup_lock");
1174 }
1175
1176 BatchRenderLoadList::~BatchRenderLoadList()
1177 {
1178         startup_lock->lock("BatchRenderLoadList::~BrowseButton");
1179         if(gui)
1180         {
1181                 gui->lock_window();
1182                 gui->set_done(1);
1183                 gui->unlock_window();
1184         }
1185         startup_lock->unlock();
1186         Thread::join();
1187         delete startup_lock;
1188 }
1189
1190 int BatchRenderLoadList::handle_event()
1191 {
1192         if(Thread::running())
1193         {
1194                 if(gui)
1195                 {
1196                         gui->lock_window();
1197                         gui->raise_window();
1198                         gui->unlock_window();
1199                 }
1200                 return 1;
1201         }
1202         startup_lock->lock("BatchRenderLoadList::handle_event 1");
1203         Thread::start();
1204         startup_lock->lock("BatchRenderLoadList::handle_event 2");
1205         startup_lock->unlock();
1206         return 1;
1207 }
1208
1209 void BatchRenderLoadList::run()
1210 {
1211         char default_path[BCTEXTLEN];
1212         sprintf(default_path, "~");
1213         BC_FileBox filewindow(100,
1214                               100,
1215                               this->thread->mwindow->defaults->get("DEFAULT_BATCHLOADPATH", default_path),
1216                               _("Load Batch Render List"),
1217                               _("Enter a Batch Render filename to load from:"),
1218                               0,
1219                               0,
1220                               0,
1221                               0);
1222
1223         gui = &filewindow;
1224
1225         startup_lock->unlock();
1226         filewindow.create_objects();
1227
1228         int result2 = filewindow.run_window();
1229
1230         if(!result2)
1231         {
1232                 this->thread->load_jobs(filewindow.get_submitted_path(),this->thread->mwindow->preferences);
1233                 this->thread->gui->create_list(1);
1234                 this->thread->mwindow->defaults->update("DEFAULT_BATCHLOADPATH", filewindow.get_submitted_path());
1235         }
1236
1237         this->thread->gui->flush();
1238         startup_lock->lock("BatchRenderLoadList::run");
1239         gui = 0;
1240         startup_lock->unlock();
1241 }
1242
1243 int BatchRenderLoadList::keypress_event() {
1244         if (get_keypress() == 'o' ||
1245             get_keypress() == 'O') return handle_event();
1246         return 0;
1247 }
1248
1249 BatchRenderCurrentEDL::BatchRenderCurrentEDL(BatchRenderThread *thread,
1250         int x,
1251         int y)
1252  : BC_GenericButton(x, y, _("Use Current EDL"))
1253 {
1254         this->thread = thread;
1255 }
1256
1257 int BatchRenderCurrentEDL::handle_event()
1258 {
1259         thread->use_current_edl();
1260         return 1;
1261 }
1262
1263 BatchRenderUpdateEDL::BatchRenderUpdateEDL(BatchRenderThread *thread,
1264         int x,
1265         int y)
1266  : BC_GenericButton(x, y, _("Save to EDL Path"))
1267 {
1268         this->thread = thread;
1269 }
1270
1271 int BatchRenderUpdateEDL::handle_event()
1272 {
1273         thread->update_selected_edl();
1274         return 1;
1275 }
1276
1277
1278
1279
1280 BatchRenderList::BatchRenderList(BatchRenderThread *thread,
1281         int x,
1282         int y,
1283         int w,
1284         int h)
1285  : BC_ListBox(x,
1286         y,
1287         w,
1288         h,
1289         LISTBOX_TEXT,
1290         thread->gui->list_columns,
1291         list_titles,
1292         thread->column_width,
1293         BATCHRENDER_COLUMNS,
1294         0,
1295         0,
1296         LISTBOX_SINGLE,
1297         ICON_LEFT,
1298         1)
1299 {
1300         this->thread = thread;
1301         dragging_item = 0;
1302         set_process_drag(0);
1303 }
1304
1305 int BatchRenderList::handle_event()
1306 {
1307         return 1;
1308 }
1309
1310 int BatchRenderList::selection_changed()
1311 {
1312         thread->current_job = get_selection_number(0, 0);
1313         thread->gui->change_job();
1314         if(get_cursor_x() < thread->column_width[0])
1315         {
1316                 BatchRenderJob *job = thread->get_current_job();
1317                 job->enabled = !job->enabled;
1318                 thread->gui->create_list(1);
1319         }
1320         return 1;
1321 }
1322
1323 int BatchRenderList::column_resize_event()
1324 {
1325         for(int i = 0; i < BATCHRENDER_COLUMNS; i++)
1326         {
1327                 thread->column_width[i] = get_column_width(i);
1328         }
1329         return 1;
1330 }
1331
1332 int BatchRenderList::drag_start_event()
1333 {
1334         if(BC_ListBox::drag_start_event())
1335         {
1336                 dragging_item = 1;
1337                 return 1;
1338         }
1339
1340         return 0;
1341 }
1342
1343 int BatchRenderList::drag_motion_event()
1344 {
1345         if(BC_ListBox::drag_motion_event())
1346         {
1347                 return 1;
1348         }
1349         return 0;
1350 }
1351
1352 int BatchRenderList::drag_stop_event()
1353 {
1354         if(dragging_item)
1355         {
1356                 int src = get_selection_number(0, 0);
1357                 int dst = get_highlighted_item();
1358                 if(src != dst)
1359                 {
1360                         thread->move_batch(src, dst);
1361                 }
1362                 BC_ListBox::drag_stop_event();
1363         }
1364         return 0;
1365 }
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379 BatchRenderStart::BatchRenderStart(BatchRenderThread *thread,
1380         int x,
1381         int y)
1382  : BC_GenericButton(x,
1383         y,
1384         _("Start"))
1385 {
1386         this->thread = thread;
1387 }
1388
1389 int BatchRenderStart::handle_event()
1390 {
1391         thread->start_rendering();
1392         return 1;
1393 }
1394
1395 BatchRenderStop::BatchRenderStop(BatchRenderThread *thread,
1396         int x,
1397         int y)
1398  : BC_GenericButton(x,
1399         y,
1400         _("Stop"))
1401 {
1402         this->thread = thread;
1403 }
1404
1405 int BatchRenderStop::handle_event()
1406 {
1407         unlock_window();
1408         thread->stop_rendering();
1409         lock_window("BatchRenderStop::handle_event");
1410         return 1;
1411 }
1412
1413
1414 BatchRenderWarning::BatchRenderWarning(BatchRenderThread *thread, int x, int y)
1415  : BC_CheckBox(x, y, thread->warn, _("warn if jobs/session mismatched"))
1416 {
1417         this->thread = thread;
1418 }
1419
1420 int BatchRenderWarning::handle_event()
1421 {
1422         thread->warn = get_value();
1423         return 1;
1424 }
1425
1426
1427 BatchRenderCancel::BatchRenderCancel(BatchRenderThread *thread,
1428         int x,
1429         int y)
1430  : BC_GenericButton(x,
1431         y,
1432         _("Close"))
1433 {
1434         this->thread = thread;
1435 }
1436
1437 int BatchRenderCancel::handle_event()
1438 {
1439         unlock_window();
1440         thread->stop_rendering();
1441         lock_window("BatchRenderCancel::handle_event");
1442         thread->gui->set_done(1);
1443         return 1;
1444 }
1445
1446 int BatchRenderCancel::keypress_event()
1447 {
1448         if(get_keypress() == ESC)
1449         {
1450                 unlock_window();
1451                 thread->stop_rendering();
1452                 lock_window("BatchRenderCancel::keypress_event");
1453                 thread->gui->set_done(1);
1454                 return 1;
1455         }
1456         return 0;
1457 }
1458