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