Batch Render menu help key using xdg-open + 8 more translations by RafaMar
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / batchrender.C
index e8e5fc33a863b6448a0c8c991b34b002397dda2b..9e4dbc3cdfe6ecf92eac4d69d80d318d524b04ac 100644 (file)
 #include "file.h"
 #include "filesystem.h"
 #include "filexml.h"
+#include "indexable.h"
 #include "keyframe.h"
 #include "keys.h"
 #include "labels.h"
 #include "language.h"
+#include "localsession.h"
 #include "mainerror.h"
 #include "mainundo.h"
 #include "mainsession.h"
 #include "transportque.h"
 #include "vframe.h"
 
-// Farmed is not present if not preferences->use_renderfarm
+#include "dvdcreate.h"
+#include "bdcreate.h"
+
+#include <stdlib.h>
+
 int BatchRenderThread::column_widths[] = { 42, 42, 42, 222, 222, 150 };
 const char *BatchRenderThread::column_titles[] = {
        N_("Enabled"), N_("Labeled"), N_("Farmed"), N_("Output"), N_("EDL"), N_("Elapsed")
@@ -70,13 +76,14 @@ BatchRenderMenuItem::BatchRenderMenuItem(MWindow *mwindow)
 
 int BatchRenderMenuItem::handle_event()
 {
-       mwindow->batch_render->start();
+       mwindow->batch_render->start(1, 1);
        return 1;
 }
 
-
-BatchRenderJob::BatchRenderJob(Preferences *preferences, int labeled, int farmed)
+BatchRenderJob::BatchRenderJob(const char *tag,
+               Preferences *preferences, int labeled, int farmed)
 {
+       this->tag = tag;
        this->preferences = preferences;
        this->labeled = labeled;
        this->farmed = farmed >= 0 ? farmed : preferences->use_renderfarm;
@@ -86,6 +93,11 @@ BatchRenderJob::BatchRenderJob(Preferences *preferences, int labeled, int farmed
        elapsed = 0;
 }
 
+BatchRenderJob::BatchRenderJob(Preferences *preferences, int labeled, int farmed)
+ : BatchRenderJob("JOB", preferences, labeled, farmed)
+{
+}
+
 BatchRenderJob::~BatchRenderJob()
 {
        asset->Garbage::remove_user();
@@ -101,13 +113,22 @@ void BatchRenderJob::copy_from(BatchRenderJob *src)
        elapsed = 0;
 }
 
+BatchRenderJob *BatchRenderJob::copy()
+{
+       BatchRenderJob *t = new BatchRenderJob(tag, preferences, labeled, farmed);
+       t->copy_from(this);
+       return t;
+}
+
 void BatchRenderJob::load(FileXML *file)
 {
        int result = 0;
+       char job_title[BCSTRLEN];
+       strcpy(job_title, file->tag.title);
 
        enabled = file->tag.get_property("ENABLED", enabled);
        farmed = file->tag.get_property("FARMED", farmed);
-       labeled = file->tag.get_property("STRATEGY", labeled);
+       labeled = file->tag.get_property("LABELED", labeled);
        edl_path[0] = 0;
        file->tag.get_property("EDL_PATH", edl_path);
        elapsed = file->tag.get_property("ELAPSED", elapsed);
@@ -121,7 +142,7 @@ void BatchRenderJob::load(FileXML *file)
 // coding maintenance.  The defaults must now be stuffed into the XML for
 // unique storage.
                        BC_Hash defaults;
-                       defaults.load_string(file->read_text());
+                       defaults.load_string(file->read_text(job_title));
                        asset->load_defaults(&defaults,
                                "", 0, 1, 0, 0, 0);
                }
@@ -130,6 +151,8 @@ void BatchRenderJob::load(FileXML *file)
 
 void BatchRenderJob::save(FileXML *file)
 {
+       char end_tag[BCSTRLEN];  end_tag[0] = '/';
+       strcpy(&end_tag[1], file->tag.get_title());
        file->tag.set_property("ENABLED", enabled);
        file->tag.set_property("FARMED", farmed);
        file->tag.set_property("LABELED", labeled);
@@ -148,16 +171,21 @@ void BatchRenderJob::save(FileXML *file)
        defaults.save_string(string);
        file->append_text(string);
        free(string);
-       file->tag.set_title("/JOB");
+       file->tag.set_title(end_tag);
        file->append_tag();
        file->append_newline();
 }
 
+char *BatchRenderJob::create_script(EDL *edl, ArrayList<Indexable *> *idxbls)
+{
+       return 0;
+}
+
 int BatchRenderJob::get_strategy()
 {
-// if set, overrides farmed, labeled
-       int use_renderfarm = farmed && preferences->use_renderfarm ? 1 : 0;
-       return Render::get_strategy(use_renderfarm, labeled);
+       int range = File::is_image_render(asset->format) ?
+               RANGE_1FRAME : RANGE_SELECTION;
+       return Render::get_strategy(farmed, labeled, range);
 }
 
 
@@ -171,24 +199,11 @@ BatchRenderThread::BatchRenderThread(MWindow *mwindow)
        default_job = 0;
        boot_defaults = 0;
        preferences = 0;
-       warn = 1;
-       render = 0;
-       batch_path[0] = 0;
-}
-
-BatchRenderThread::BatchRenderThread()
- : BC_DialogThread()
-{
-       mwindow = 0;
-       current_job = 0;
-       rendering_job = -1;
-       is_rendering = 0;
-       default_job = 0;
-       boot_defaults = 0;
-       preferences = 0;
-       warn = 1;
+       warn = 0;
        render = 0;
        batch_path[0] = 0;
+       do_farmed = 0;
+       do_labeled = 0;
 }
 
 BatchRenderThread::~BatchRenderThread()
@@ -196,6 +211,7 @@ BatchRenderThread::~BatchRenderThread()
        close_window();
        delete boot_defaults;
        delete preferences;
+       delete default_job;
        delete render;
 }
 
@@ -211,6 +227,13 @@ void BatchRenderThread::reset(const char *path)
        jobs.remove_all_objects();
 }
 
+void BatchRenderThread::start(int do_farmed, int do_labeled)
+{
+       this->do_farmed = do_farmed;
+       this->do_labeled = do_labeled;
+       BC_DialogThread::start();
+}
+
 void BatchRenderThread::handle_close_event(int result)
 {
 // Save settings
@@ -223,7 +246,7 @@ BC_Window* BatchRenderThread::new_gui()
 {
        current_start = 0.0;
        current_end = 0.0;
-       default_job = new BatchRenderJob(mwindow->preferences);
+       default_job = new BatchRenderJob(mwindow->preferences, 0, -1);
        load_jobs(batch_path, mwindow->preferences);
        load_defaults(mwindow->defaults);
        this->gui = new BatchRenderGUI(mwindow, this,
@@ -247,10 +270,23 @@ void BatchRenderThread::load_jobs(char *path, Preferences *preferences)
        while( !result ) {
                if( !(result = file.read_tag()) ) {
                        if( file.tag.title_is("JOBS") ) {
+                               if (mwindow && mwindow->preferences->unsafe_gui)
                                warn = file.tag.get_property("WARN", 1);
+                               if (mwindow && !mwindow->preferences->unsafe_gui)
+                               warn = 0;
                        }
                        else if( file.tag.title_is("JOB") ) {
-                               BatchRenderJob *job =  new BatchRenderJob(preferences);
+                               BatchRenderJob *job =  new BatchRenderJob(preferences, 0,0);
+                               jobs.append(job);
+                               job->load(&file);
+                       }
+                       else if( file.tag.title_is("DVD_JOB") ) {
+                               DVD_BatchRenderJob *job =  new DVD_BatchRenderJob(preferences, 0,0,0,0);
+                               jobs.append(job);
+                               job->load(&file);
+                       }
+                       else if( file.tag.title_is("BD_JOB") ) {
+                               BD_BatchRenderJob *job =  new BD_BatchRenderJob(preferences, 0,0);
                                jobs.append(job);
                                job->load(&file);
                        }
@@ -262,13 +298,14 @@ void BatchRenderThread::save_jobs(char *path)
 {
        FileXML file;
        file.tag.set_title("JOBS");
+       if (mwindow && mwindow->preferences->unsafe_gui)
        file.tag.set_property("WARN", warn);
        file.append_tag();
        file.append_newline();
 
        for( int i = 0; i < jobs.total; i++ ) {
-               file.tag.set_title("JOB");
-               jobs.values[i]->save(&file);
+               file.tag.set_title(jobs[i]->tag);
+               jobs[i]->save(&file);
        }
        file.tag.set_title("/JOBS");
        file.append_tag();
@@ -289,7 +326,7 @@ void BatchRenderThread::load_defaults(BC_Hash *defaults)
        for( int i = 0; i < BATCHRENDER_COLUMNS; i++ ) {
                char string[BCTEXTLEN];
                sprintf(string, "BATCHRENDER_COLUMN%d", i);
-               list_width[i] = defaults->get(string, column_widths[i]);
+               list_width[i] = defaults->get(string, xS(column_widths[i]));
        }
 }
 
@@ -322,8 +359,7 @@ char* BatchRenderThread::create_path(char *string)
 
 void BatchRenderThread::new_job()
 {
-       BatchRenderJob *result = new BatchRenderJob(mwindow->preferences);
-       result->copy_from(get_current_job());
+       BatchRenderJob *result = get_current_job()->copy();
        jobs.append(result);
        current_job = jobs.total - 1;
        gui->create_list(1);
@@ -357,6 +393,9 @@ void BatchRenderThread::update_selected_edl()
         FileXML xml_file;
        char *path = get_current_edl();
        EDL *edl = mwindow->edl;
+       // result = 1 if user cancelled operation
+       int result = ConfirmSave::test_file(mwindow, path);
+       if (!result) {
         edl->save_xml(&xml_file, path);
         xml_file.terminate_string();
         if( xml_file.write_to_file(path) ) {
@@ -364,6 +403,7 @@ void BatchRenderThread::update_selected_edl()
                sprintf(msg, _("Unable to save: %s"), path);
                MainError::show_error(msg);
        }
+       }
 }
 
 BatchRenderJob* BatchRenderThread::get_current_job()
@@ -383,38 +423,75 @@ char* BatchRenderThread::get_current_edl()
        return get_current_job()->edl_path;
 }
 
+int BatchRenderThread::test_errmsg(BatchRenderWarnJobs &err_jobs, const char *msg, int *warn)
+{
+       int count = err_jobs.size();
+       if( !count ) return 0;
+       fprintf(stderr, msg, count);
+       char string[BCTEXTLEN], *sp = string, *ep = sp+sizeof(string)-1;
+       sp += snprintf(sp,ep-sp, msg,count);
+       for( int i=0; i<count; ++i ) {
+               int no = err_jobs[i].no;
+               const char *path = err_jobs[i].path;
+               fprintf(stderr, "%d: %s\n", no, path);
+               sp += snprintf(sp,ep-sp, "%d: %s\n", no, path);
+       }
+       sp += snprintf(sp,ep-sp, _("press cancel to abandon batch render"));
+       mwindow->show_warning(warn, string);
+       if( mwindow->wait_warning() ) {
+               gui->button_enable();
+       }
+       return 1;
+}
 
 // Test EDL files for existence
 int BatchRenderThread::test_edl_files()
 {
-       int not_equiv = 0, ret = 0;
+       int ret = 0;
        const char *path = 0;
+       BatchRenderWarnJobs not_equiv;
+       BatchRenderWarnJobs empty_jobs;
+       BatchRenderWarnJobs no_labels;
+       BatchRenderWarnJobs no_rendering;
 
        for( int i=0; !ret && i<jobs.size(); ++i ) {
                if( !jobs.values[i]->enabled ) continue;
-               path = jobs.values[i]->edl_path;
+               path = jobs[i]->edl_path;
                int is_script = *path == '@' ? 1 : 0;
                if( is_script ) ++path;
                FILE *fp = fopen(path, "r");
                if( fp ) {
-                       if( warn && mwindow && !is_script ) {
-                               fseek(fp, 0, SEEK_END);
-                               int64_t sz = ftell(fp);
-                               fseek(fp, 0, SEEK_SET);
-                               char *bfr = new char[sz+1];
-                               int64_t len = fread(bfr, 1, sz+1, fp);
-                               if( len == sz ) {
-                                       FileXML file;
-                                       XMLBuffer data(bfr, len, 0);
-                                       file.set_shared_input(&data);
+                       if( mwindow && !is_script ) {
+                               char *bfr = 0;  size_t sz = 0;
+                               struct stat st;
+                               if( !fstat(fileno(fp), &st) ) {
+                                       sz = st.st_size;
+                                       bfr = new char[sz+1];
+                                       if( fread(bfr, 1, sz+1, fp) != sz )
+                                               ret = 1;
+                                       else
+                                               bfr[sz] = 0;
+                               }
+                               if( !ret ) {
                                        EDL *edl = new EDL; edl->create_objects();
-                                       edl->load_xml(&file, LOAD_ALL);
+                                       XMLBuffer data(bfr, sz, 0);
+                                       { FileXML file;
+                                       file.set_shared_input(&data);
+                                       edl->load_xml(&file, LOAD_ALL); }
                                        double pos = edl->equivalent_output(mwindow->edl);
-                                       if( pos >= 0 ) ++not_equiv;
+                                       if( pos >= 0 )
+                                               not_equiv.add(i+1, path);
+                                       double length = edl->tracks->total_playable_length();
+                                       double start = edl->local_session->get_selectionstart(1);
+                                       if( start >= length )
+                                               empty_jobs.add(i+1, path);
+                                       if( jobs[i]->labeled && !edl->labels->first)
+                                               no_labels.add(i+1,path);
+                                       Asset *asset = jobs[i]->asset;
+                                       if( !asset->audio_data && !asset->video_data )
+                                               no_rendering.add(i+1,path);
                                        edl->remove_user();
                                }
-                               else
-                                       ret = 1;
                                delete [] bfr;
                        }
                        fclose(fp);
@@ -438,21 +515,21 @@ int BatchRenderThread::test_edl_files()
                        fprintf(stderr, "%s", string);
                }
                is_rendering = 0;
-       }
-       else if( warn && mwindow && not_equiv > 0 ) {
-               fprintf(stderr, _("%d job EDLs do not match session edl\n"), not_equiv);
-               char string[BCTEXTLEN], *sp = string;
-               sp += sprintf(sp, _("%d job EDLs do not match session edl\n"),not_equiv);
-               sp += sprintf(sp, _("press cancel to abandon batch render"));
-               mwindow->show_warning(&warn, string);
-               if( mwindow->wait_warning() ) {
-                       gui->button_enable();
-                       is_rendering = 0;
-                       ret = 1;
-               }
-               gui->warning->update(warn);
+               ret = 1;
        }
 
+       if( !ret && warn && mwindow ) {
+               ret = test_errmsg(not_equiv, _("%d job EDLs do not match session edl\n"), &warn);
+               if( !warn ) gui->warning->update(0);
+       }
+       if( !ret && mwindow )
+               ret = test_errmsg(empty_jobs, _("%d job EDLs begin position beyond end of media\n"), 0);
+       if( !ret && mwindow )
+               ret = test_errmsg(no_rendering, _("%d job EDLs no audio or video in render asset format\n"), 0);
+       if( !ret && mwindow )
+               ret = test_errmsg(no_labels, _("%d job EDLs Create new file at labels checked, but no labels\n"), 0);
+       if( ret )
+               is_rendering = 0;
        return ret;
 }
 
@@ -478,17 +555,13 @@ void BatchRenderThread::calculate_dest_paths(ArrayList<char*> *paths,
                        command->playback_range_adjust_inout();
 
 // Create test packages
-                       packages->create_packages(mwindow,
-                               command->get_edl(),
-                               preferences,
-                               job->get_strategy(),
-                               job->asset,
-                               command->start_position,
-                               command->end_position,
-                               0);
+                       int result = packages->create_packages(mwindow, command->get_edl(),
+                               preferences, job->get_strategy(), job->asset,
+                               command->start_position, command->end_position, 0);
+                       if( !result )
+                               packages->get_package_paths(paths);
 
 // Append output paths allocated to total
-                       packages->get_package_paths(paths);
 
 // Delete package harness
                        delete packages;
@@ -665,7 +738,7 @@ void BatchRenderThread::trap_hook(FILE *fp, void *vp)
 BatchRenderGUI::BatchRenderGUI(MWindow *mwindow,
        BatchRenderThread *thread, int x, int y, int w, int h)
  : BC_Window(_(PROGRAM_NAME ": Batch Render"),
-       x, y, w, h, 730, 400, 1, 0, 1)
+       x, y, w, h, xS(730), yS(400), 1, 0, 1)
 {
        this->mwindow = mwindow;
        this->thread = thread;
@@ -684,37 +757,44 @@ BatchRenderGUI::~BatchRenderGUI()
 
 void BatchRenderGUI::create_objects()
 {
+       int xs10 = xS(10), xs30 = xS(30), xs40 = xS(40);
+       int ys5 = yS(5), ys10 = yS(10), ys15 = yS(15);
        lock_window("BatchRenderGUI::create_objects");
        mwindow->theme->get_batchrender_sizes(this, get_w(), get_h());
        create_list(0);
 
        int x = mwindow->theme->batchrender_x1;
-       int y = 5;
-       int x1 = x, x2 = get_w()/2 + 10; // mwindow->theme->batchrender_x2;
-       int y1 = 5, y2 = 5;
+       int y = ys5;
+       int x1 = x, x2 = get_w()/2 + xs30; // mwindow->theme->batchrender_x2;
+       int y1 = ys5, y2 = ys5;
 
 // output file
        add_subwindow(output_path_title = new BC_Title(x1, y1, _("Output path:")));
        y1 += output_path_title->get_h() + mwindow->theme->widget_border;
 
        format_tools = new BatchFormat(mwindow, this, thread->get_current_asset());
-       format_tools->set_w(get_w() / 2);
+       format_tools->set_w(x2 - xs40);
        BatchRenderJob *current_job = thread->get_current_job();
        format_tools->create_objects(x1, y1, 1, 1, 1, 1, 0, 1, 0, 0,
-                       &current_job->labeled, 0);
-       if( mwindow->preferences->use_renderfarm ) {
+                       thread->do_labeled ? &current_job->labeled : 0, 0);
+       if( thread->do_labeled < 0 )
+               format_tools->labeled_files->disable();
+       if( thread->do_farmed ) {
                use_renderfarm = new BatchRenderUseFarm(thread, x1, y1,
                        &current_job->farmed);
                add_subwindow(use_renderfarm);
-               y1 += use_renderfarm->get_h() + 10;
+               y1 += use_renderfarm->get_h() + ys10;
+               if( thread->do_farmed < 0 )
+                       use_renderfarm->disable();
        }
+
 // input EDL
        add_subwindow(edl_path_title = new BC_Title(x2, y2, _("EDL Path:")));
        y2 += edl_path_title->get_h() + mwindow->theme->widget_border;
 
        x = x2;  y = y2;
        add_subwindow(edl_path_text = new BatchRenderEDLPath( thread,
-               x, y, get_w()-x - 40, thread->get_current_edl()));
+               x, y, get_w()-x - xs40, thread->get_current_edl()));
        x =  x2 + edl_path_text->get_w();
        add_subwindow(edl_path_browse = new BrowseButton(
                mwindow->theme, this, edl_path_text, x, y, thread->get_current_edl(),
@@ -722,8 +802,10 @@ void BatchRenderGUI::create_objects()
        y2 = y + edl_path_browse->get_h() + mwindow->theme->widget_border;
 
        x = x2;  y = y2;
+       if (mwindow->preferences->unsafe_gui) {
        add_subwindow(update_selected_edl = new BatchRenderUpdateEDL(thread, x, y));
        y += update_selected_edl->get_h() + mwindow->theme->widget_border;
+       }
        add_subwindow(use_current_edl = new BatchRenderCurrentEDL(thread, x, y));
        y += use_current_edl->get_h() + mwindow->theme->widget_border;
        if( !mwindow->edl || !mwindow->edl->path[0] ) use_current_edl->disable();
@@ -735,8 +817,10 @@ void BatchRenderGUI::create_objects()
        x += savelist_batch->get_w() + mwindow->theme->widget_border;
        add_subwindow(loadlist_batch = new BatchRenderLoadList(thread, x, y));
        y += loadlist_batch->get_h() + mwindow->theme->widget_border;
+       if (mwindow->preferences->unsafe_gui) {
        add_subwindow(warning = new BatchRenderWarning(thread, x2, y));
        y2 = y + warning->get_h() + mwindow->theme->widget_border;
+       }
        if( y2 > y1 ) y1 = y2;
        x = mwindow->theme->batchrender_x1, y = y1;
 
@@ -745,15 +829,17 @@ void BatchRenderGUI::create_objects()
        add_subwindow(batch_path = new BC_Title(x1, y, thread->batch_path, MEDIUMFONT));
        y += list_title->get_h() + mwindow->theme->widget_border;
        y1 = get_h();
-       y1 -= 15 + BC_GenericButton::calculate_h() + mwindow->theme->widget_border;
+       y1 -= ys15 + BC_GenericButton::calculate_h() + mwindow->theme->widget_border;
        add_subwindow(batch_list = new BatchRenderList(thread, x, y,
-               get_w() - x - 10, y1 - y));
+               get_w() - x - xs10, y1 - y));
        y += batch_list->get_h() + mwindow->theme->widget_border;
 
        add_subwindow(start_button = new BatchRenderStart(thread, x, y));
-       x = get_w() / 2 - BC_GenericButton::calculate_w(this, _("Stop")) / 2;
+       x = get_w() / 3 - BC_GenericButton::calculate_w(this, _("Stop")) / 3;
        add_subwindow(stop_button = new BatchRenderStop(thread, x, y));
-       x = get_w() - BC_GenericButton::calculate_w(this, _("Close")) - 10;
+       x = get_w() / 1.5 - BC_GenericButton::calculate_w(this, _("?")) / 1.5;
+       add_subwindow(help_button = new BatchRenderHelp(thread, x, y));
+       x = get_w() - BC_GenericButton::calculate_w(this, _("Close")) - xs10;
        add_subwindow(cancel_button = new BatchRenderCancel(thread, x, y));
 
        show_window(1);
@@ -765,6 +851,7 @@ void BatchRenderGUI::button_disable()
        new_batch->disable();
        delete_batch->disable();
        use_current_edl->disable();
+       if (mwindow->preferences->unsafe_gui)
        update_selected_edl->disable();
 }
 
@@ -774,38 +861,43 @@ void BatchRenderGUI::button_enable()
        delete_batch->enable();
        if( mwindow->edl && mwindow->edl->path[0] )
                use_current_edl->enable();
+       if (mwindow->preferences->unsafe_gui)
        update_selected_edl->enable();
 }
 
 int BatchRenderGUI::resize_event(int w, int h)
 {
+       int xs10 = xS(10), xs40 = xS(40);
+       int ys5 = yS(5), ys15 = yS(15);
        mwindow->session->batchrender_w = w;
        mwindow->session->batchrender_h = h;
        mwindow->theme->get_batchrender_sizes(this, w, h);
 
        int x = mwindow->theme->batchrender_x1;
-       int y = 5;
-       int x1 = x, x2 = get_w()/2 + 10; // mwindow->theme->batchrender_x2;
-       int y1 = 5, y2 = 5;
+       int y = ys5;
+       int x1 = x, x2 = get_w()/2 + xs10; // mwindow->theme->batchrender_x2;
+       int y1 = ys5, y2 = ys5;
 
 // output file
        output_path_title->reposition_window(x1, y1);
        y1 += output_path_title->get_h() + mwindow->theme->widget_border;
        format_tools->reposition_window(x1, y1);
-       if( use_renderfarm )
+       if( thread->do_farmed )
                use_renderfarm->reposition_window(x1, y1);
 // input EDL
        x = x2, y = y2;
        edl_path_title->reposition_window(x, y);
        y += edl_path_title->get_h() + mwindow->theme->widget_border;
-       edl_path_text->reposition_window(x, y, w - x - 40);
+       edl_path_text->reposition_window(x, y, w - x - xs40);
        x += edl_path_text->get_w();
        edl_path_browse->reposition_window(x, y);
        y2 = y + edl_path_browse->get_h() + mwindow->theme->widget_border;
 
        x = x2;  y = y2;
+       if (mwindow->preferences->unsafe_gui) {
        update_selected_edl->reposition_window(x, y);
        y += update_selected_edl->get_h() + mwindow->theme->widget_border;
+       }
        use_current_edl->reposition_window(x, y);
        y += use_current_edl->get_h() + mwindow->theme->widget_border;
        new_batch->reposition_window(x, y);
@@ -817,23 +909,26 @@ int BatchRenderGUI::resize_event(int w, int h)
        x += savelist_batch->get_w() + mwindow->theme->widget_border;
        loadlist_batch->reposition_window(x, y);
        y += loadlist_batch->get_h() + mwindow->theme->widget_border;
+       if (mwindow->preferences->unsafe_gui) {
        warning->reposition_window(x2, y);
-
-       y1 = 15 + BC_GenericButton::calculate_h() + mwindow->theme->widget_border;
+       }
+       y1 = ys15 + BC_GenericButton::calculate_h() + mwindow->theme->widget_border;
        y2 = get_h() - y1 - batch_list->get_h();
        y2 -= list_title->get_h() + mwindow->theme->widget_border;
 
        x = mwindow->theme->batchrender_x1;  y = y2;
        list_title->reposition_window(x, y);
        y += list_title->get_h() + mwindow->theme->widget_border;
-       batch_list->reposition_window(x, y, w - x - 10, h - y - y1);
+       batch_list->reposition_window(x, y, w - x - xs10, h - y - y1);
        y += batch_list->get_h() + mwindow->theme->widget_border;
 
        start_button->reposition_window(x, y);
-       x = w / 2 - stop_button->get_w() / 2;
+       x = w / 3 - stop_button->get_w() / 3;
        stop_button->reposition_window(x, y);
-       x = w - cancel_button->get_w() - 10;
+       x = w - cancel_button->get_w() - xs10;
        cancel_button->reposition_window(x, y);
+       x = w / 1.5 - help_button->get_w() / 1.5;
+       help_button->reposition_window(x, y);
        return 1;
 }
 
@@ -864,9 +959,11 @@ void BatchRenderGUI::create_list(int update_widget)
        list_columns = 0;
        list_titles[list_columns] = _(column_titles[ENABLED_COL]);
        list_width[list_columns++] = thread->list_width[ENABLED_COL];
-       list_titles[list_columns] = _(column_titles[LABELED_COL]);
-       list_width[list_columns++] = thread->list_width[LABELED_COL];
-       if( mwindow->preferences->use_renderfarm ) {
+       if( thread->do_labeled > 0 ) {
+               list_titles[list_columns] = _(column_titles[LABELED_COL]);
+               list_width[list_columns++] = thread->list_width[LABELED_COL];
+       }
+       if( thread->do_farmed > 0 ) {
                list_titles[list_columns] = _(column_titles[FARMED_COL]);
                list_width[list_columns++] = thread->list_width[FARMED_COL];
        }
@@ -881,23 +978,24 @@ void BatchRenderGUI::create_list(int update_widget)
                BatchRenderJob *job = thread->jobs.values[i];
                char string[BCTEXTLEN];
                BC_ListBoxItem *enabled = new BC_ListBoxItem(job->enabled ? "X" : " ");
-               BC_ListBoxItem *labeled = new BC_ListBoxItem(job->labeled ? "X" : " ");
-               BC_ListBoxItem *farmed  = !mwindow->preferences->use_renderfarm ? 0 :
-                       new BC_ListBoxItem(job->farmed  ? "X" : " ");
+               BC_ListBoxItem *labeled = thread->do_labeled > 0 ?
+                       new BC_ListBoxItem(job->labeled ? "X" : " ") : 0;
+               BC_ListBoxItem *farmed  = thread->do_farmed > 0 ?
+                       new BC_ListBoxItem(job->farmed  ? "X" : " ") : 0;
                BC_ListBoxItem *out_path = new BC_ListBoxItem(job->asset->path);
                BC_ListBoxItem *edl_path = new BC_ListBoxItem(job->edl_path);
                BC_ListBoxItem *elapsed = new BC_ListBoxItem(!job->elapsed ? _("Unknown") :
                        Units::totext(string, job->elapsed, TIME_HMS2));
                int col = 0;
                list_items[col++].append(enabled);
-               list_items[col++].append(labeled);
+               if( labeled ) list_items[col++].append(labeled);
                if( farmed ) list_items[col++].append(farmed);
                list_items[col++].append(out_path);
                list_items[col++].append(edl_path);
                list_items[col].append(elapsed);
                if( i == thread->current_job ) {
                        enabled->set_selected(1);
-                       labeled->set_selected(1);
+                       if( labeled ) labeled->set_selected(1);
                        if( farmed ) farmed->set_selected(1);
                        out_path->set_selected(1);
                        edl_path->set_selected(1);
@@ -905,7 +1003,7 @@ void BatchRenderGUI::create_list(int update_widget)
                }
                if( i == thread->rendering_job ) {
                        enabled->set_color(RED);
-                       labeled->set_color(RED);
+                       if( labeled ) labeled->set_color(RED);
                        if( farmed ) farmed->set_color(RED);
                        out_path->set_color(RED);
                        edl_path->set_color(RED);
@@ -923,8 +1021,8 @@ void BatchRenderGUI::create_list(int update_widget)
 void BatchRenderGUI::change_job()
 {
        BatchRenderJob *job = thread->get_current_job();
-       format_tools->update(job->asset, &job->labeled);
-       if( use_renderfarm ) use_renderfarm->update(&job->farmed);
+       format_tools->update(job->asset, thread->do_labeled ? &job->labeled : 0);
+       if( thread->do_farmed ) use_renderfarm->update(&job->farmed);
        edl_path_text->update(job->edl_path);
 }
 
@@ -969,6 +1067,7 @@ BatchRenderNew::BatchRenderNew(BatchRenderThread *thread,
  : BC_GenericButton(x, y, _("New"))
 {
        this->thread = thread;
+       set_tooltip(_("Create a new batch"));
 }
 
 int BatchRenderNew::handle_event()
@@ -981,6 +1080,7 @@ BatchRenderDelete::BatchRenderDelete(BatchRenderThread *thread, int x, int y)
  : BC_GenericButton(x, y, _("Delete"))
 {
        this->thread = thread;
+       set_tooltip(_("Delete loaded (highlighted) batch"));
 }
 
 int BatchRenderDelete::handle_event()
@@ -1036,7 +1136,8 @@ void BatchRenderSaveList::run()
        char default_path[BCTEXTLEN];
        sprintf(default_path, "~");
        thread->mwindow->defaults->get("DEFAULT_BATCHLOADPATH", default_path);
-       BC_FileBox filewindow(100, 100, default_path, _("Save Batch Render List"),
+       BC_FileBox filewindow(xS(100), yS(100), default_path,
+                       _("Save Batch Render List"),
                        _("Enter a Batch Render filename to save as:"),
                        0, 0, 0, 0);
        gui = &filewindow;
@@ -1047,7 +1148,9 @@ void BatchRenderSaveList::run()
        int result2 = filewindow.run_window();
        if( !result2 ) {
                strcpy(thread->batch_path, filewindow.get_submitted_path());
+               thread->gui->lock_window("BatchRenderSaveList::run");
                thread->gui->batch_path->update(thread->batch_path);
+               thread->gui->unlock_window();
                thread->mwindow->defaults->update("DEFAULT_BATCHLOADPATH", thread->batch_path);
                thread->save_jobs(thread->batch_path);
        }
@@ -1112,7 +1215,7 @@ void BatchRenderLoadList::run()
        char default_path[BCTEXTLEN];
        sprintf(default_path, "~");
        thread->mwindow->defaults->get("DEFAULT_BATCHLOADPATH", default_path);
-       BC_FileBox filewindow(100, 100, default_path, _("Load Batch Render List"),
+       BC_FileBox filewindow(xS(100), yS(100), default_path, _("Load Batch Render List"),
                        _("Enter a Batch Render filename to load from:"),
                        0, 0, 0, 0);
        gui = &filewindow;
@@ -1122,6 +1225,7 @@ void BatchRenderLoadList::run()
 
        int result2 = filewindow.run_window();
        if( !result2 ) {
+               thread->gui->lock_window("BatchRenderLoadList::run");
                strcpy(thread->batch_path, filewindow.get_submitted_path());
                thread->gui->batch_path->update(thread->batch_path);
                thread->mwindow->defaults->update("DEFAULT_BATCHLOADPATH", thread->batch_path);
@@ -1129,6 +1233,7 @@ void BatchRenderLoadList::run()
                thread->gui->create_list(1);
                thread->current_job = 0;
                thread->gui->change_job();
+               thread->gui->unlock_window();
        }
 
        startup_lock->lock("BatchRenderLoadList::run");
@@ -1148,6 +1253,7 @@ BatchRenderCurrentEDL::BatchRenderCurrentEDL(BatchRenderThread *thread,
  : BC_GenericButton(x, y, _("Use Current EDL"))
 {
        this->thread = thread;
+       set_tooltip(_("Replaces highlighted batch job with session currently on timeline. File on disk NOT changed!"));
 }
 
 int BatchRenderCurrentEDL::handle_event()
@@ -1162,6 +1268,7 @@ BatchRenderUpdateEDL::BatchRenderUpdateEDL(BatchRenderThread *thread,
  : BC_GenericButton(x, y, _("Save to EDL Path"))
 {
        this->thread = thread;
+       set_tooltip(_("WARNING - saves to loaded (highlighted) EDL, *overwrites* highlighted project!"));
 }
 
 int BatchRenderUpdateEDL::handle_event()
@@ -1196,9 +1303,10 @@ int BatchRenderList::selection_changed()
        int col_x = 0, changed = 1;
        if( cursor_x < (col_x += thread->list_width[ENABLED_COL]) )
                job->enabled = !job->enabled;
-       else if( cursor_x < (col_x += thread->list_width[LABELED_COL]) )
+       else if( thread->do_labeled > 0 &&
+                cursor_x < (col_x += thread->list_width[LABELED_COL]) )
                job->labeled = job->edl_path[0] != '@' ? !job->labeled : 0;
-       else if( thread->gui->use_renderfarm &&
+       else if( thread->do_farmed > 0 &&
                 cursor_x < (col_x += thread->list_width[FARMED_COL]) )
                job->farmed = job->edl_path[0] != '@' ? !job->farmed : 0;
        else
@@ -1214,8 +1322,9 @@ int BatchRenderList::column_resize_event()
 {
        int col = 0;
        thread->list_width[ENABLED_COL] = get_column_width(col++);
-       thread->list_width[LABELED_COL] = get_column_width(col++);
-       if( thread->gui->use_renderfarm )
+       if( thread->do_labeled > 0 )
+               thread->list_width[LABELED_COL] = get_column_width(col++);
+       if( thread->do_farmed > 0 )
                thread->list_width[FARMED_COL] = get_column_width(col++);
        thread->list_width[OUTPUT_COL] = get_column_width(col++);
        thread->list_width[EDL_COL] = get_column_width(col++);
@@ -1261,6 +1370,7 @@ BatchRenderStart::BatchRenderStart(BatchRenderThread *thread, int x, int y)
  : BC_GenericButton(x, y, _("Start"))
 {
        this->thread = thread;
+       set_tooltip(_("Start batch rendering"));
 }
 
 int BatchRenderStart::handle_event()
@@ -1273,6 +1383,7 @@ BatchRenderStop::BatchRenderStop(BatchRenderThread *thread, int x, int y)
  : BC_GenericButton(x, y, _("Stop"))
 {
        this->thread = thread;
+       set_tooltip(_("Stops currently active batch rendering"));
 }
 
 int BatchRenderStop::handle_event()
@@ -1288,6 +1399,7 @@ BatchRenderWarning::BatchRenderWarning(BatchRenderThread *thread, int x, int y)
  : BC_CheckBox(x, y, thread->warn, _("warn if jobs/session mismatched"))
 {
        this->thread = thread;
+       set_tooltip(_("Prevents rendering if loaded session and batch job(s) differ"));
 }
 
 int BatchRenderWarning::handle_event()
@@ -1300,6 +1412,7 @@ BatchRenderCancel::BatchRenderCancel(BatchRenderThread *thread, int x, int y)
  : BC_GenericButton(x, y, _("Close"))
 {
        this->thread = thread;
+       set_tooltip(_("Closes this window"));
 }
 
 int BatchRenderCancel::handle_event()
@@ -1323,6 +1436,20 @@ int BatchRenderCancel::keypress_event()
        return 0;
 }
 
+//new help button
+BatchRenderHelp::BatchRenderHelp(BatchRenderThread *thread, int x, int y)
+ : BC_GenericButton(x, y, _("?"))
+{
+       this->thread = thread;
+       set_tooltip(_("Open quick start guide in default browser"));
+}
+
+int BatchRenderHelp::handle_event()
+{
+       system("xdg-open file://$CIN_DAT/doc/help_br_index.html");
+       return 0;
+}
+
 BatchRenderUseFarm::BatchRenderUseFarm(BatchRenderThread *thread, int x, int y, int *output)
  : BC_CheckBox(x, y, *output, _("Use render farm"))
 {