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