Bluray enhancements by Andrew
[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(preferences);
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 // In batch mode there is no mwindow, so init_plugins is called with
601 // mwindow* = NULL.
602         MWindow::init_plugins(0, preferences);
603         MWindow::init_ladspa_plugins(0, preferences);  
604         char font_path[BCTEXTLEN];
605         strcpy(font_path, preferences->plugin_dir);
606         strcat(font_path, "/" FONT_SEARCHPATH);
607         BC_Resources::init_fontconfig(font_path);
608         BC_WindowBase::get_resources()->vframe_shm = 1;
609
610 //PRINT_TRACE
611         strcpy(this->batch_path, batch_path);
612         load_jobs(batch_path, preferences);
613         save_jobs(batch_path);
614         save_defaults(boot_defaults);
615
616 //PRINT_TRACE
617 // Test EDL files for existence
618         if( test_edl_files() ) return;
619
620 //PRINT_TRACE
621
622 // Predict all destination paths
623         ArrayList<char*> paths;
624         paths.set_array_delete();
625         calculate_dest_paths(&paths, preferences);
626
627 //PRINT_TRACE
628         int result = ConfirmSave::test_files(0, &paths);
629         paths.remove_all_objects();
630 // Abort on any existing file because it's so hard to set this up.
631         if( result ) return;
632
633 //PRINT_TRACE
634         render = new Render(0);
635 //PRINT_TRACE
636         render->start_batches(&jobs, boot_defaults, preferences);
637 //PRINT_TRACE
638 }
639
640 void BatchRenderThread::start_rendering()
641 {
642         if( is_rendering ) return;
643         is_rendering = 1;
644
645         save_jobs(batch_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                 is_rendering = 0;
664                 gui->button_enable();
665                 return;
666         }
667
668         mwindow->render->start_batches(&jobs);
669 }
670
671 void BatchRenderThread::stop_rendering()
672 {
673         if( !is_rendering ) return;
674         mwindow->render->stop_operation();
675         is_rendering = 0;
676 }
677
678 void BatchRenderThread::update_active(int number)
679 {
680         gui->lock_window("BatchRenderThread::update_active");
681         if( number >= 0 ) {
682                 current_job = number;
683                 rendering_job = number;
684         }
685         else {
686                 rendering_job = -1;
687                 is_rendering = 0;
688         }
689         gui->create_list(1);
690         gui->unlock_window();
691 }
692
693 void BatchRenderThread::update_done(int number,
694         int create_list,
695         double elapsed_time)
696 {
697         gui->lock_window("BatchRenderThread::update_done");
698         if( number < 0 ) {
699                 gui->button_enable();
700         }
701         else {
702                 jobs.values[number]->enabled = 0;
703                 jobs.values[number]->elapsed = elapsed_time;
704                 if( create_list ) gui->create_list(1);
705         }
706         gui->unlock_window();
707 }
708
709 void BatchRenderThread::move_batch(int src, int dst)
710 {
711         BatchRenderJob *src_job = jobs.values[src];
712         if( dst < 0 ) dst = jobs.total - 1;
713
714         if( dst != src ) {
715                 for( int i = src; i < jobs.total - 1; i++ )
716                         jobs.values[i] = jobs.values[i + 1];
717 //              if( dst > src ) dst--;
718                 for( int i = jobs.total - 1; i > dst; i-- )
719                         jobs.values[i] = jobs.values[i - 1];
720                 jobs.values[dst] = src_job;
721                 gui->create_list(1);
722         }
723 }
724
725 void BatchRenderThread::trap_hook(FILE *fp, void *vp)
726 {
727         MWindow *mwindow = ((BatchRenderThread *)vp)->mwindow;
728         fprintf(fp, "\nEDL:\n");
729         mwindow->dump_edl(fp);
730         fprintf(fp, "\nUNDO:\n");
731         mwindow->dump_undo(fp);
732         fprintf(fp, "\nEXE:\n");
733         mwindow->dump_exe(fp);
734 }
735
736
737
738
739
740 BatchRenderGUI::BatchRenderGUI(MWindow *mwindow,
741         BatchRenderThread *thread, int x, int y, int w, int h)
742  : BC_Window(_(PROGRAM_NAME ": Batch Render"),
743         x, y, w, h, xS(730), yS(400), 1, 0, 1)
744 {
745         this->mwindow = mwindow;
746         this->thread = thread;
747         use_renderfarm = 0;
748 // *** CONTEXT_HELP ***
749         context_help_set_keyword("Batch Rendering");
750 }
751
752 BatchRenderGUI::~BatchRenderGUI()
753 {
754         lock_window("BatchRenderGUI::~BatchRenderGUI");
755         loadlist_batch->stop();
756         savelist_batch->stop();
757         delete format_tools;
758         unlock_window();
759 }
760
761
762 void BatchRenderGUI::create_objects()
763 {
764         int xs10 = xS(10), xs30 = xS(30), xs40 = xS(40);
765         int ys5 = yS(5), ys10 = yS(10), ys15 = yS(15);
766         lock_window("BatchRenderGUI::create_objects");
767         mwindow->theme->get_batchrender_sizes(this, get_w(), get_h());
768         create_list(0);
769
770         int x = mwindow->theme->batchrender_x1;
771         int y = ys5;
772         int x1 = x, x2 = get_w()/2 + xs30; // mwindow->theme->batchrender_x2;
773         int y1 = ys5, y2 = ys5;
774
775 // output file
776         add_subwindow(output_path_title = new BC_Title(x1, y1, _("Output path:")));
777         y1 += output_path_title->get_h() + mwindow->theme->widget_border;
778
779         format_tools = new BatchFormat(mwindow, this, thread->get_current_asset());
780         format_tools->set_w(x2 - xs40);
781         BatchRenderJob *current_job = thread->get_current_job();
782         format_tools->create_objects(x1, y1, 1, 1, 1, 1, 0, 1, 0, 0,
783                         thread->do_labeled ? &current_job->labeled : 0, 0);
784         if( thread->do_labeled < 0 )
785                 format_tools->labeled_files->disable();
786         if( thread->do_farmed ) {
787                 use_renderfarm = new BatchRenderUseFarm(thread, x1, y1,
788                         &current_job->farmed);
789                 add_subwindow(use_renderfarm);
790                 y1 += use_renderfarm->get_h() + ys10;
791                 if( thread->do_farmed < 0 )
792                         use_renderfarm->disable();
793         }
794
795 // input EDL
796         add_subwindow(edl_path_title = new BC_Title(x2, y2, _("EDL Path:")));
797         y2 += edl_path_title->get_h() + mwindow->theme->widget_border;
798
799         x = x2;  y = y2;
800         add_subwindow(edl_path_text = new BatchRenderEDLPath( thread,
801                 x, y, get_w()-x - xs40, thread->get_current_edl()));
802         x =  x2 + edl_path_text->get_w();
803         add_subwindow(edl_path_browse = new BrowseButton(
804                 mwindow->theme, this, edl_path_text, x, y, thread->get_current_edl(),
805                 _("Input EDL"), _("Select an EDL to load:"), 0));
806         y2 = y + edl_path_browse->get_h() + mwindow->theme->widget_border;
807
808         x = x2;  y = y2;
809         if (mwindow->preferences->unsafe_gui) {
810         add_subwindow(update_selected_edl = new BatchRenderUpdateEDL(thread, x, y));
811         y += update_selected_edl->get_h() + mwindow->theme->widget_border;
812         }
813         add_subwindow(use_current_edl = new BatchRenderCurrentEDL(thread, x, y));
814         y += use_current_edl->get_h() + mwindow->theme->widget_border;
815         if( !mwindow->edl || !mwindow->edl->path[0] ) use_current_edl->disable();
816         add_subwindow(new_batch = new BatchRenderNew(thread, x, y));
817         x += new_batch->get_w() + mwindow->theme->widget_border;
818         add_subwindow(delete_batch = new BatchRenderDelete(thread, x, y));
819         x = x2;  y += delete_batch->get_h() + mwindow->theme->widget_border;
820         add_subwindow(savelist_batch = new BatchRenderSaveList(thread, x, y));
821         x += savelist_batch->get_w() + mwindow->theme->widget_border;
822         add_subwindow(loadlist_batch = new BatchRenderLoadList(thread, x, y));
823         y += loadlist_batch->get_h() + mwindow->theme->widget_border;
824         if (mwindow->preferences->unsafe_gui) {
825         add_subwindow(warning = new BatchRenderWarning(thread, x2, y));
826         y2 = y + warning->get_h() + mwindow->theme->widget_border;
827         }
828         if( y2 > y1 ) y1 = y2;
829         x = mwindow->theme->batchrender_x1, y = y1;
830
831         add_subwindow(list_title = new BC_Title(x, y, _("Batches to render:")));
832         x1 = x + list_title->get_w() + mwindow->theme->widget_border;;
833         add_subwindow(batch_path = new BC_Title(x1, y, thread->batch_path, MEDIUMFONT));
834         y += list_title->get_h() + mwindow->theme->widget_border;
835         y1 = get_h();
836         y1 -= ys15 + BC_GenericButton::calculate_h() + mwindow->theme->widget_border;
837         add_subwindow(batch_list = new BatchRenderList(thread, x, y,
838                 get_w() - x - xs10, y1 - y));
839         y += batch_list->get_h() + mwindow->theme->widget_border;
840
841         add_subwindow(start_button = new BatchRenderStart(thread, x, y));
842         x = get_w() / 3 - BC_GenericButton::calculate_w(this, _("Stop")) / 3;
843         add_subwindow(stop_button = new BatchRenderStop(thread, x, y));
844         x = get_w() / 1.5 - BC_GenericButton::calculate_w(this, _("?")) / 1.5;
845         add_subwindow(help_button = new BatchRenderHelp(thread, x, y));
846         x = get_w() - BC_GenericButton::calculate_w(this, _("Close")) - xs10;
847         add_subwindow(cancel_button = new BatchRenderCancel(thread, x, y));
848
849         show_window(1);
850         unlock_window();
851 }
852
853 void BatchRenderGUI::button_disable()
854 {
855         new_batch->disable();
856         delete_batch->disable();
857         use_current_edl->disable();
858         if (mwindow->preferences->unsafe_gui)
859         update_selected_edl->disable();
860 }
861
862 void BatchRenderGUI::button_enable()
863 {
864         new_batch->enable();
865         delete_batch->enable();
866         if( mwindow->edl && mwindow->edl->path[0] )
867                 use_current_edl->enable();
868         if (mwindow->preferences->unsafe_gui)
869         update_selected_edl->enable();
870 }
871
872 int BatchRenderGUI::resize_event(int w, int h)
873 {
874         int xs10 = xS(10), xs40 = xS(40);
875         int ys5 = yS(5), ys15 = yS(15);
876         mwindow->session->batchrender_w = w;
877         mwindow->session->batchrender_h = h;
878         mwindow->theme->get_batchrender_sizes(this, w, h);
879
880         int x = mwindow->theme->batchrender_x1;
881         int y = ys5;
882         int x1 = x, x2 = get_w()/2 + xs10; // mwindow->theme->batchrender_x2;
883         int y1 = ys5, y2 = ys5;
884
885 // output file
886         output_path_title->reposition_window(x1, y1);
887         y1 += output_path_title->get_h() + mwindow->theme->widget_border;
888         format_tools->reposition_window(x1, y1);
889         if( thread->do_farmed )
890                 use_renderfarm->reposition_window(x1, y1);
891 // input EDL
892         x = x2, y = y2;
893         edl_path_title->reposition_window(x, y);
894         y += edl_path_title->get_h() + mwindow->theme->widget_border;
895         edl_path_text->reposition_window(x, y, w - x - xs40);
896         x += edl_path_text->get_w();
897         edl_path_browse->reposition_window(x, y);
898         y2 = y + edl_path_browse->get_h() + mwindow->theme->widget_border;
899
900         x = x2;  y = y2;
901         if (mwindow->preferences->unsafe_gui) {
902         update_selected_edl->reposition_window(x, y);
903         y += update_selected_edl->get_h() + mwindow->theme->widget_border;
904         }
905         use_current_edl->reposition_window(x, y);
906         y += use_current_edl->get_h() + mwindow->theme->widget_border;
907         new_batch->reposition_window(x, y);
908         x += new_batch->get_w() + mwindow->theme->widget_border;
909         delete_batch->reposition_window(x, y);
910
911         x = x2;  y += delete_batch->get_h() + mwindow->theme->widget_border;
912         savelist_batch->reposition_window(x, y);
913         x += savelist_batch->get_w() + mwindow->theme->widget_border;
914         loadlist_batch->reposition_window(x, y);
915         y += loadlist_batch->get_h() + mwindow->theme->widget_border;
916         if (mwindow->preferences->unsafe_gui) {
917         warning->reposition_window(x2, y);
918         }
919         y1 = ys15 + BC_GenericButton::calculate_h() + mwindow->theme->widget_border;
920         y2 = get_h() - y1 - batch_list->get_h();
921         y2 -= list_title->get_h() + mwindow->theme->widget_border;
922
923         x = mwindow->theme->batchrender_x1;  y = y2;
924         list_title->reposition_window(x, y);
925         y += list_title->get_h() + mwindow->theme->widget_border;
926         batch_list->reposition_window(x, y, w - x - xs10, h - y - y1);
927         y += batch_list->get_h() + mwindow->theme->widget_border;
928
929         start_button->reposition_window(x, y);
930         x = w / 3 - stop_button->get_w() / 3;
931         stop_button->reposition_window(x, y);
932         x = w - cancel_button->get_w() - xs10;
933         cancel_button->reposition_window(x, y);
934         x = w / 1.5 - help_button->get_w() / 1.5;
935         help_button->reposition_window(x, y);
936         return 1;
937 }
938
939 int BatchRenderGUI::translation_event()
940 {
941         mwindow->session->batchrender_x = get_x();
942         mwindow->session->batchrender_y = get_y();
943         return 1;
944 }
945
946 int BatchRenderGUI::close_event()
947 {
948 // Stop batch rendering
949         unlock_window();
950         thread->stop_rendering();
951         lock_window("BatchRenderGUI::close_event");
952         set_done(1);
953         return 1;
954 }
955
956 void BatchRenderGUI::create_list(int update_widget)
957 {
958         for( int i = 0; i < BATCHRENDER_COLUMNS; i++ ) {
959                 list_items[i].remove_all_objects();
960         }
961
962         const char **column_titles = BatchRenderThread::column_titles;
963         list_columns = 0;
964         list_titles[list_columns] = _(column_titles[ENABLED_COL]);
965         list_width[list_columns++] = thread->list_width[ENABLED_COL];
966         if( thread->do_labeled > 0 ) {
967                 list_titles[list_columns] = _(column_titles[LABELED_COL]);
968                 list_width[list_columns++] = thread->list_width[LABELED_COL];
969         }
970         if( thread->do_farmed > 0 ) {
971                 list_titles[list_columns] = _(column_titles[FARMED_COL]);
972                 list_width[list_columns++] = thread->list_width[FARMED_COL];
973         }
974         list_titles[list_columns] = _(column_titles[OUTPUT_COL]);
975         list_width[list_columns++] = thread->list_width[OUTPUT_COL];
976         list_titles[list_columns] = _(column_titles[EDL_COL]);
977         list_width[list_columns++] = thread->list_width[EDL_COL];
978         list_titles[list_columns] = _(column_titles[ELAPSED_COL]);
979         list_width[list_columns++] = thread->list_width[ELAPSED_COL];
980
981         for( int i = 0; i < thread->jobs.total; i++ ) {
982                 BatchRenderJob *job = thread->jobs.values[i];
983                 char string[BCTEXTLEN];
984                 BC_ListBoxItem *enabled = new BC_ListBoxItem(job->enabled ? "X" : " ");
985                 BC_ListBoxItem *labeled = thread->do_labeled > 0 ?
986                         new BC_ListBoxItem(job->labeled ? "X" : " ") : 0;
987                 BC_ListBoxItem *farmed  = thread->do_farmed > 0 ?
988                         new BC_ListBoxItem(job->farmed  ? "X" : " ") : 0;
989                 BC_ListBoxItem *out_path = new BC_ListBoxItem(job->asset->path);
990                 BC_ListBoxItem *edl_path = new BC_ListBoxItem(job->edl_path);
991                 BC_ListBoxItem *elapsed = new BC_ListBoxItem(!job->elapsed ? _("Unknown") :
992                         Units::totext(string, job->elapsed, TIME_HMS2));
993                 int col = 0;
994                 list_items[col++].append(enabled);
995                 if( labeled ) list_items[col++].append(labeled);
996                 if( farmed ) list_items[col++].append(farmed);
997                 list_items[col++].append(out_path);
998                 list_items[col++].append(edl_path);
999                 list_items[col].append(elapsed);
1000                 if( i == thread->current_job ) {
1001                         enabled->set_selected(1);
1002                         if( labeled ) labeled->set_selected(1);
1003                         if( farmed ) farmed->set_selected(1);
1004                         out_path->set_selected(1);
1005                         edl_path->set_selected(1);
1006                         elapsed->set_selected(1);
1007                 }
1008                 if( i == thread->rendering_job ) {
1009                         enabled->set_color(RED);
1010                         if( labeled ) labeled->set_color(RED);
1011                         if( farmed ) farmed->set_color(RED);
1012                         out_path->set_color(RED);
1013                         edl_path->set_color(RED);
1014                         elapsed->set_color(RED);
1015                 }
1016         }
1017
1018         if( update_widget ) {
1019                 batch_list->update(list_items, list_titles, list_width, list_columns,
1020                         batch_list->get_xposition(), batch_list->get_yposition(),
1021                         batch_list->get_highlighted_item(), 1, 1);
1022         }
1023 }
1024
1025 void BatchRenderGUI::change_job()
1026 {
1027         BatchRenderJob *job = thread->get_current_job();
1028         format_tools->update(job->asset, thread->do_labeled ? &job->labeled : 0);
1029         if( thread->do_farmed ) use_renderfarm->update(&job->farmed);
1030         edl_path_text->update(job->edl_path);
1031 }
1032
1033
1034 BatchFormat::BatchFormat(MWindow *mwindow, BatchRenderGUI *gui, Asset *asset)
1035  : FormatTools(mwindow, gui, asset)
1036 {
1037         this->gui = gui;
1038         this->mwindow = mwindow;
1039 }
1040
1041 BatchFormat::~BatchFormat()
1042 {
1043 }
1044
1045
1046 int BatchFormat::handle_event()
1047 {
1048         gui->create_list(1);
1049         return 1;
1050 }
1051
1052 BatchRenderEDLPath::BatchRenderEDLPath(BatchRenderThread *thread,
1053         int x, int y, int w, char *text)
1054  : BC_TextBox(x, y, w, 1, text)
1055 {
1056         this->thread = thread;
1057 }
1058
1059
1060 int BatchRenderEDLPath::handle_event()
1061 {
1062         calculate_suggestions();
1063         strcpy(thread->get_current_edl(), get_text());
1064         thread->gui->create_list(1);
1065         return 1;
1066 }
1067
1068 BatchRenderNew::BatchRenderNew(BatchRenderThread *thread,
1069         int x,
1070         int y)
1071  : BC_GenericButton(x, y, _("New"))
1072 {
1073         this->thread = thread;
1074         set_tooltip(_("Create a new batch"));
1075 }
1076
1077 int BatchRenderNew::handle_event()
1078 {
1079         thread->new_job();
1080         return 1;
1081 }
1082
1083 BatchRenderDelete::BatchRenderDelete(BatchRenderThread *thread, int x, int y)
1084  : BC_GenericButton(x, y, _("Delete"))
1085 {
1086         this->thread = thread;
1087         set_tooltip(_("Delete loaded (highlighted) batch"));
1088 }
1089
1090 int BatchRenderDelete::handle_event()
1091 {
1092         thread->delete_job();
1093         return 1;
1094 }
1095
1096
1097
1098 BatchRenderSaveList::BatchRenderSaveList(BatchRenderThread *thread, int x, int y)
1099  : BC_GenericButton(x, y, _("Save Jobs"))
1100 {
1101         this->thread = thread;
1102         set_tooltip(_("Save a Batch Render List"));
1103         gui = 0;
1104         startup_lock = new Mutex("BatchRenderSaveList::startup_lock");
1105 }
1106
1107 BatchRenderSaveList::~BatchRenderSaveList()
1108 {
1109         stop();
1110         delete startup_lock;
1111 }
1112
1113 void BatchRenderSaveList::stop()
1114 {
1115         startup_lock->lock("BatchRenderSaveList::~BrowseButton");
1116         if( gui ) gui->set_done(1);
1117         startup_lock->unlock();
1118         Thread::join();
1119 }
1120
1121 int BatchRenderSaveList::handle_event()
1122 {
1123         if( Thread::running() ) {
1124                 if( gui ) {
1125                         gui->lock_window();
1126                         gui->raise_window();
1127                         gui->unlock_window();
1128                 }
1129                 return 1;
1130         }
1131         startup_lock->lock("BatchRenderSaveList::handle_event 1");
1132         Thread::start();
1133         startup_lock->lock("BatchRenderSaveList::handle_event 2");
1134         startup_lock->unlock();
1135         return 1;
1136 }
1137
1138 void BatchRenderSaveList::run()
1139 {
1140         char default_path[BCTEXTLEN];
1141         sprintf(default_path, "~");
1142         thread->mwindow->defaults->get("DEFAULT_BATCHLOADPATH", default_path);
1143         BC_FileBox filewindow(xS(100), yS(100), default_path,
1144                         _("Save Batch Render List"),
1145                         _("Enter a Batch Render filename to save as:"),
1146                         0, 0, 0, 0);
1147         gui = &filewindow;
1148
1149         startup_lock->unlock();
1150         filewindow.create_objects();
1151
1152         int result2 = filewindow.run_window();
1153         if( !result2 ) {
1154                 strcpy(thread->batch_path, filewindow.get_submitted_path());
1155                 thread->gui->lock_window("BatchRenderSaveList::run");
1156                 thread->gui->batch_path->update(thread->batch_path);
1157                 thread->gui->unlock_window();
1158                 thread->mwindow->defaults->update("DEFAULT_BATCHLOADPATH", thread->batch_path);
1159                 thread->save_jobs(thread->batch_path);
1160         }
1161
1162         startup_lock->lock("BatchRenderLoadList::run");
1163         gui = 0;
1164         startup_lock->unlock();
1165 }
1166
1167 int BatchRenderSaveList::keypress_event() {
1168         if( get_keypress() == 's' ||
1169             get_keypress() == 'S' ) return handle_event();
1170         return context_help_check_and_show();
1171 }
1172
1173
1174 BatchRenderLoadList::BatchRenderLoadList(BatchRenderThread *thread,
1175         int x,
1176         int y)
1177   : BC_GenericButton(x, y, _("Load Jobs")),
1178     Thread()
1179 {
1180         this->thread = thread;
1181         set_tooltip(_("Load a previously saved Batch Render List"));
1182         gui = 0;
1183         startup_lock = new Mutex("BatchRenderLoadList::startup_lock");
1184 }
1185
1186 BatchRenderLoadList::~BatchRenderLoadList()
1187 {
1188         stop();
1189         delete startup_lock;
1190 }
1191
1192 void BatchRenderLoadList::stop()
1193 {
1194         startup_lock->lock("BatchRenderLoadList::~BrowseButton");
1195         if( gui ) gui->set_done(1);
1196         startup_lock->unlock();
1197         Thread::join();
1198 }
1199
1200 int BatchRenderLoadList::handle_event()
1201 {
1202         if( Thread::running() ) {
1203                 if( gui ) {
1204                         gui->lock_window();
1205                         gui->raise_window();
1206                         gui->unlock_window();
1207                 }
1208                 return 1;
1209         }
1210         startup_lock->lock("BatchRenderLoadList::handle_event 1");
1211         Thread::start();
1212         startup_lock->lock("BatchRenderLoadList::handle_event 2");
1213         startup_lock->unlock();
1214         return 1;
1215 }
1216
1217 void BatchRenderLoadList::run()
1218 {
1219         char default_path[BCTEXTLEN];
1220         sprintf(default_path, "~");
1221         thread->mwindow->defaults->get("DEFAULT_BATCHLOADPATH", default_path);
1222         BC_FileBox filewindow(xS(100), yS(100), default_path, _("Load Batch Render List"),
1223                         _("Enter a Batch Render filename to load from:"),
1224                         0, 0, 0, 0);
1225         gui = &filewindow;
1226
1227         startup_lock->unlock();
1228         filewindow.create_objects();
1229
1230         int result2 = filewindow.run_window();
1231         if( !result2 ) {
1232                 thread->gui->lock_window("BatchRenderLoadList::run");
1233                 strcpy(thread->batch_path, filewindow.get_submitted_path());
1234                 thread->gui->batch_path->update(thread->batch_path);
1235                 thread->mwindow->defaults->update("DEFAULT_BATCHLOADPATH", thread->batch_path);
1236                 thread->load_jobs(thread->batch_path, thread->mwindow->preferences);
1237                 thread->gui->create_list(1);
1238                 thread->current_job = 0;
1239                 thread->gui->change_job();
1240                 thread->gui->unlock_window();
1241         }
1242
1243         startup_lock->lock("BatchRenderLoadList::run");
1244         gui = 0;
1245         startup_lock->unlock();
1246 }
1247
1248 int BatchRenderLoadList::keypress_event() {
1249         if( get_keypress() == 'o' ||
1250             get_keypress() == 'O' ) return handle_event();
1251         return context_help_check_and_show();
1252 }
1253
1254 BatchRenderCurrentEDL::BatchRenderCurrentEDL(BatchRenderThread *thread,
1255         int x,
1256         int y)
1257  : BC_GenericButton(x, y, _("Use Current EDL"))
1258 {
1259         this->thread = thread;
1260         set_tooltip(_("Replaces highlighted batch job with session currently on timeline. File on disk NOT changed!"));
1261 }
1262
1263 int BatchRenderCurrentEDL::handle_event()
1264 {
1265         thread->use_current_edl();
1266         return 1;
1267 }
1268
1269 BatchRenderUpdateEDL::BatchRenderUpdateEDL(BatchRenderThread *thread,
1270         int x,
1271         int y)
1272  : BC_GenericButton(x, y, _("Save to EDL Path"))
1273 {
1274         this->thread = thread;
1275         set_tooltip(_("WARNING - saves to loaded (highlighted) EDL, *overwrites* highlighted project!"));
1276 }
1277
1278 int BatchRenderUpdateEDL::handle_event()
1279 {
1280         thread->update_selected_edl();
1281         return 1;
1282 }
1283
1284
1285 BatchRenderList::BatchRenderList(BatchRenderThread *thread,
1286         int x, int y, int w, int h)
1287  : BC_ListBox(x, y, w, h, LISTBOX_TEXT, thread->gui->list_items,
1288         thread->gui->list_titles, thread->gui->list_width, thread->gui->list_columns,
1289         0, 0, LISTBOX_SINGLE, ICON_LEFT, 1)
1290 {
1291         this->thread = thread;
1292         dragging_item = 0;
1293         set_process_drag(0);
1294 }
1295
1296 int BatchRenderList::handle_event()
1297 {
1298         return 1;
1299 }
1300
1301 int BatchRenderList::selection_changed()
1302 {
1303         thread->current_job = get_selection_number(0, 0);
1304         thread->gui->change_job();
1305         int cursor_x = get_cursor_x();
1306         BatchRenderJob *job = thread->get_current_job();
1307         int col_x = 0, changed = 1;
1308         if( cursor_x < (col_x += thread->list_width[ENABLED_COL]) )
1309                 job->enabled = !job->enabled;
1310         else if( thread->do_labeled > 0 &&
1311                  cursor_x < (col_x += thread->list_width[LABELED_COL]) )
1312                 job->labeled = job->edl_path[0] != '@' ? !job->labeled : 0;
1313         else if( thread->do_farmed > 0 &&
1314                  cursor_x < (col_x += thread->list_width[FARMED_COL]) )
1315                 job->farmed = job->edl_path[0] != '@' ? !job->farmed : 0;
1316         else
1317                 changed = 0;
1318         if( changed ) {
1319                 thread->gui->create_list(1);
1320                 thread->gui->change_job();
1321         }
1322         return 1;
1323 }
1324
1325 int BatchRenderList::column_resize_event()
1326 {
1327         int col = 0;
1328         thread->list_width[ENABLED_COL] = get_column_width(col++);
1329         if( thread->do_labeled > 0 )
1330                 thread->list_width[LABELED_COL] = get_column_width(col++);
1331         if( thread->do_farmed > 0 )
1332                 thread->list_width[FARMED_COL] = get_column_width(col++);
1333         thread->list_width[OUTPUT_COL] = get_column_width(col++);
1334         thread->list_width[EDL_COL] = get_column_width(col++);
1335         thread->list_width[ELAPSED_COL] = get_column_width(col);
1336         return 1;
1337 }
1338
1339 int BatchRenderList::drag_start_event()
1340 {
1341         if( BC_ListBox::drag_start_event() ) {
1342                 dragging_item = 1;
1343                 return 1;
1344         }
1345
1346         return 0;
1347 }
1348
1349 int BatchRenderList::drag_motion_event()
1350 {
1351         if( BC_ListBox::drag_motion_event() ) {
1352                 return 1;
1353         }
1354         return 0;
1355 }
1356
1357 int BatchRenderList::drag_stop_event()
1358 {
1359         if( dragging_item ) {
1360                 int src = get_selection_number(0, 0);
1361                 int dst = get_highlighted_item();
1362                 if( src != dst ) {
1363                         thread->move_batch(src, dst);
1364                 }
1365                 BC_ListBox::drag_stop_event();
1366                 dragging_item = 0;
1367         }
1368         return 0;
1369 }
1370
1371
1372
1373 BatchRenderStart::BatchRenderStart(BatchRenderThread *thread, int x, int y)
1374  : BC_GenericButton(x, y, _("Start"))
1375 {
1376         this->thread = thread;
1377         set_tooltip(_("Start batch rendering"));
1378 }
1379
1380 int BatchRenderStart::handle_event()
1381 {
1382         thread->start_rendering();
1383         return 1;
1384 }
1385
1386 BatchRenderStop::BatchRenderStop(BatchRenderThread *thread, int x, int y)
1387  : BC_GenericButton(x, y, _("Stop"))
1388 {
1389         this->thread = thread;
1390         set_tooltip(_("Stops currently active batch rendering"));
1391 }
1392
1393 int BatchRenderStop::handle_event()
1394 {
1395         unlock_window();
1396         thread->stop_rendering();
1397         lock_window("BatchRenderStop::handle_event");
1398         return 1;
1399 }
1400
1401
1402 BatchRenderWarning::BatchRenderWarning(BatchRenderThread *thread, int x, int y)
1403  : BC_CheckBox(x, y, thread->warn, _("warn if jobs/session mismatched"))
1404 {
1405         this->thread = thread;
1406         set_tooltip(_("Prevents rendering if loaded session and batch job(s) differ"));
1407 }
1408
1409 int BatchRenderWarning::handle_event()
1410 {
1411         thread->warn = get_value();
1412         return 1;
1413 }
1414
1415 BatchRenderCancel::BatchRenderCancel(BatchRenderThread *thread, int x, int y)
1416  : BC_GenericButton(x, y, _("Close"))
1417 {
1418         this->thread = thread;
1419         set_tooltip(_("Closes this window"));
1420 }
1421
1422 int BatchRenderCancel::handle_event()
1423 {
1424         unlock_window();
1425         thread->stop_rendering();
1426         lock_window("BatchRenderCancel::handle_event");
1427         thread->gui->set_done(1);
1428         return 1;
1429 }
1430
1431 int BatchRenderCancel::keypress_event()
1432 {
1433         if( get_keypress() == ESC ) {
1434                 unlock_window();
1435                 thread->stop_rendering();
1436                 lock_window("BatchRenderCancel::keypress_event");
1437                 thread->gui->set_done(1);
1438                 return 1;
1439         }
1440         return context_help_check_and_show();
1441 }
1442
1443 //new help button
1444 BatchRenderHelp::BatchRenderHelp(BatchRenderThread *thread, int x, int y)
1445  : BC_GenericButton(x, y, _("?"))
1446 {
1447         this->thread = thread;
1448         set_tooltip(_("Open quick start guide in default browser"));
1449 }
1450
1451 int BatchRenderHelp::handle_event()
1452 {
1453         system("xdg-open file://$CIN_DAT/doc/help_br_index.html");
1454         return 0;
1455 }
1456
1457 BatchRenderUseFarm::BatchRenderUseFarm(BatchRenderThread *thread, int x, int y, int *output)
1458  : BC_CheckBox(x, y, *output, _("Use render farm"))
1459 {
1460         this->thread = thread;
1461         this->output = output;
1462 }
1463
1464 int BatchRenderUseFarm::handle_event()
1465 {
1466         *output = get_value();
1467         thread->gui->create_list(1);
1468         return 1;
1469 }
1470
1471 void BatchRenderUseFarm::update(int *output)
1472 {
1473         this->output = output;
1474         BC_CheckBox::update(*output);
1475 }
1476