]> de.git.xonotic.org Git - xonotic/xonotic.git/blobdiff - misc/tools/NexuizDemoRecorder/main/src/main/java/com/nexuiz/demorecorder/application/DemoRecorderApplication.java
fix lots of CRLFs
[xonotic/xonotic.git] / misc / tools / NexuizDemoRecorder / main / src / main / java / com / nexuiz / demorecorder / application / DemoRecorderApplication.java
index d6717c6e5163a67b1569f04ccda95818a3c3539c..de5da57b7ff403ddb0b38a41e62ab8123370e405 100644 (file)
-package com.nexuiz.demorecorder.application;\r
-\r
-import java.io.File;\r
-import java.io.FileInputStream;\r
-import java.io.FileNotFoundException;\r
-import java.io.FileOutputStream;\r
-import java.io.IOException;\r
-import java.io.ObjectInputStream;\r
-import java.io.ObjectOutputStream;\r
-import java.net.MalformedURLException;\r
-import java.net.URL;\r
-import java.net.URLClassLoader;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-import java.util.Properties;\r
-import java.util.ServiceLoader;\r
-import java.util.concurrent.CopyOnWriteArrayList;\r
-\r
-import com.nexuiz.demorecorder.application.jobs.EncoderJob;\r
-import com.nexuiz.demorecorder.application.jobs.RecordJob;\r
-import com.nexuiz.demorecorder.application.jobs.RecordsDoneJob;\r
-import com.nexuiz.demorecorder.application.plugins.EncoderPlugin;\r
-import com.nexuiz.demorecorder.ui.DemoRecorderUI;\r
-\r
-public class DemoRecorderApplication {\r
-       \r
-       public static class Preferences {\r
-               public static final String OVERWRITE_VIDEO_FILE = "Overwrite final video destination file if it exists";\r
-               public static final String DISABLE_RENDERING = "Disable rendering while fast-forwarding";\r
-               public static final String DISABLE_SOUND = "Disable sound while fast-forwarding";\r
-               public static final String FFW_SPEED_FIRST_STAGE = "Fast-forward speed (first stage)";\r
-               public static final String FFW_SPEED_SECOND_STAGE = "Fast-forward speed (second stage)";\r
-               public static final String DO_NOT_DELETE_CUT_DEMOS = "Do not delete cut demos";\r
-               public static final String JOB_NAME_APPEND_DUPLICATE = "Append this suffix to job-name when duplicating jobs";\r
-               \r
-               public static final String[] PREFERENCES_ORDER = {\r
-                       OVERWRITE_VIDEO_FILE,\r
-                       DISABLE_RENDERING,\r
-                       DISABLE_SOUND,\r
-                       FFW_SPEED_FIRST_STAGE,\r
-                       FFW_SPEED_SECOND_STAGE,\r
-                       DO_NOT_DELETE_CUT_DEMOS,\r
-                       JOB_NAME_APPEND_DUPLICATE\r
-               };\r
-       }\r
-       \r
-       public static final String PREFERENCES_DIRNAME = "settings";\r
-       public static final String LOGS_DIRNAME = "logs";\r
-       public static final String PLUGINS_DIRNAME = "plugins";\r
-       public static final String APP_PREFERENCES_FILENAME = "app_preferences.xml";\r
-       public static final String JOBQUEUE_FILENAME = "jobs.dat";\r
-       \r
-       public static final int STATE_WORKING = 0;\r
-       public static final int STATE_IDLE = 1;\r
-       \r
-       private RecorderJobPoolExecutor poolExecutor;\r
-       private List<RecordJob> jobs;\r
-       private NDRPreferences preferences = null;\r
-       private List<DemoRecorderUI> registeredUserInterfaces;\r
-       private List<EncoderPlugin> encoderPlugins;\r
-       private int state = STATE_IDLE;\r
-       \r
-       public DemoRecorderApplication() {\r
-               poolExecutor = new RecorderJobPoolExecutor();\r
-               jobs = new CopyOnWriteArrayList<RecordJob>();\r
-               this.registeredUserInterfaces = new ArrayList<DemoRecorderUI>();\r
-               this.encoderPlugins = new ArrayList<EncoderPlugin>();\r
-               this.getPreferences();\r
-               this.loadPlugins();\r
-               this.configurePlugins();\r
-               this.loadJobQueue();\r
-       }\r
-       \r
-       public void setPreference(String category, String preference, boolean value) {\r
-               this.preferences.setProperty(category, preference, String.valueOf(value));\r
-       }\r
-       \r
-       public void setPreference(String category, String preference, int value) {\r
-               this.preferences.setProperty(category, preference, String.valueOf(value));\r
-       }\r
-       \r
-       public void setPreference(String category, String preference, String value) {\r
-               this.preferences.setProperty(category, preference, value);\r
-       }\r
-       \r
-       public NDRPreferences getPreferences() {\r
-               if (this.preferences == null) {\r
-                       this.preferences = new NDRPreferences();\r
-                       this.createPreferenceDefaultValues();\r
-                       File preferencesFile = DemoRecorderUtils.computeLocalFile(PREFERENCES_DIRNAME, APP_PREFERENCES_FILENAME);\r
-                       if (preferencesFile.exists()) {\r
-                               FileInputStream fis = null;\r
-                               try {\r
-                                       fis = new FileInputStream(preferencesFile);\r
-                                       this.preferences.loadFromXML(fis);\r
-                               } catch (Exception e) {\r
-                                       DemoRecorderUtils.showNonCriticalErrorDialog("Could not load the application preferences file!", e, true);\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               return this.preferences;\r
-       }\r
-       \r
-       private void createPreferenceDefaultValues() {\r
-               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.OVERWRITE_VIDEO_FILE, "false");\r
-               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DISABLE_RENDERING, "true");\r
-               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DISABLE_SOUND, "true");\r
-               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.FFW_SPEED_FIRST_STAGE, "100");\r
-               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.FFW_SPEED_SECOND_STAGE, "10");\r
-               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DO_NOT_DELETE_CUT_DEMOS, "false");\r
-               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.JOB_NAME_APPEND_DUPLICATE, " duplicate");\r
-       }\r
-       \r
-       public void savePreferences() {\r
-               File preferencesFile = DemoRecorderUtils.computeLocalFile(PREFERENCES_DIRNAME, APP_PREFERENCES_FILENAME);\r
-               if (!preferencesFile.exists()) {\r
-                       try {\r
-                               preferencesFile.createNewFile();\r
-                       } catch (IOException e) {\r
-                               File parentDir = preferencesFile.getParentFile();\r
-                               if (!parentDir.exists()) {\r
-                                       try {\r
-                                               if (parentDir.mkdirs() == true) {\r
-                                                       try {\r
-                                                               preferencesFile.createNewFile();\r
-                                                       } catch (Exception ex) {}\r
-                                               }\r
-                                       } catch (Exception ex) {}\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               if (!preferencesFile.exists()) {\r
-                       DemoRecorderException ex = new DemoRecorderException("Could not create the preferences file " + preferencesFile.getAbsolutePath());\r
-                       DemoRecorderUtils.showNonCriticalErrorDialog(ex);\r
-                       return;\r
-               }\r
-               \r
-               FileOutputStream fos;\r
-               try {\r
-                       fos = new FileOutputStream(preferencesFile);\r
-               } catch (FileNotFoundException e) {\r
-                       DemoRecorderUtils.showNonCriticalErrorDialog("Could not create the preferences file " + preferencesFile.getAbsolutePath() + ". Unsufficient rights?", e, true);\r
-                       return;\r
-               }\r
-               try {\r
-                       this.preferences.storeToXML(fos, null);\r
-               } catch (IOException e) {\r
-                       DemoRecorderUtils.showNonCriticalErrorDialog("Could not create the preferences file " + preferencesFile.getAbsolutePath(), e, true);\r
-               }\r
-       }\r
-       \r
-       public List<RecordJob> getRecordJobs() {\r
-               return new ArrayList<RecordJob>(this.jobs);\r
-       }\r
-       \r
-       public void startRecording() {\r
-               if (this.state != STATE_WORKING) {\r
-                       this.state = STATE_WORKING;\r
-                       \r
-                       for (RecordJob currentJob : this.jobs) {\r
-                               if (currentJob.getState() == RecordJob.State.WAITING) {\r
-                                       this.poolExecutor.runJob(currentJob);\r
-                               }\r
-                       }\r
-                       \r
-                       //notify ourself when job is done\r
-                       this.poolExecutor.runJob(new RecordsDoneJob(this));\r
-               }\r
-       }\r
-       \r
-       public void recordSelectedJobs(List<RecordJob> jobList) {\r
-               if (this.state == STATE_IDLE) {\r
-                       this.state = STATE_WORKING;\r
-                       for (RecordJob currentJob : jobList) {\r
-                               if (currentJob.getState() == RecordJob.State.WAITING) {\r
-                                       this.poolExecutor.runJob(currentJob);\r
-                               }\r
-                       }\r
-                       \r
-                       //notify ourself when job is done\r
-                       this.poolExecutor.runJob(new RecordsDoneJob(this));\r
-               }\r
-       }\r
-       \r
-       public void executePluginForSelectedJobs(EncoderPlugin plugin, List<RecordJob> jobList) {\r
-               if (this.state == STATE_IDLE) {\r
-                       this.state = STATE_WORKING;\r
-                       for (RecordJob currentJob : jobList) {\r
-                               if (currentJob.getState() == RecordJob.State.DONE) {\r
-                                       this.poolExecutor.runJob(new EncoderJob(currentJob, plugin));\r
-                               }\r
-                       }\r
-                       \r
-                       //notify ourself when job is done\r
-                       this.poolExecutor.runJob(new RecordsDoneJob(this));\r
-               }\r
-       }\r
-       \r
-       public void notifyAllJobsDone() {\r
-               this.state = STATE_IDLE;\r
-               \r
-               //notify all UIs\r
-               for (DemoRecorderUI currentUI : this.registeredUserInterfaces) {\r
-                       currentUI.recordingFinished();\r
-               }\r
-       }\r
-       \r
-       public synchronized void stopRecording() {\r
-               if (this.state == STATE_WORKING) {\r
-                       //clear the queue of the threadpoolexecutor and add the GUI/applayer notify job again\r
-                       this.poolExecutor.clearUnfinishedJobs();\r
-                       this.poolExecutor.runJob(new RecordsDoneJob(this));\r
-               }\r
-       }\r
-       \r
-       public RecordJob createRecordJob(\r
-               String name,\r
-               File enginePath,\r
-               String engineParameters,\r
-               File demoFile,\r
-               String relativeDemoPath,\r
-               File dpVideoPath,\r
-               File videoDestination,\r
-               String executeBeforeCap,\r
-               String executeAfterCap,\r
-               float startSecond,\r
-               float endSecond\r
-       ) {\r
-               int jobIndex = -1;\r
-               if (name == null || name.equals("")) {\r
-                       //we don't have a name, so use a generic one \r
-                       jobIndex = this.getNewJobIndex();\r
-                       name = "Job " + jobIndex;\r
-               } else {\r
-                       //just use the name and keep jobIndex at -1. Jobs with real names don't need an index\r
-               }\r
-               \r
-               \r
-               \r
-               RecordJob newJob = new RecordJob(\r
-                       this,\r
-                       name,\r
-                       jobIndex,\r
-                       enginePath,\r
-                       engineParameters,\r
-                       demoFile,\r
-                       relativeDemoPath,\r
-                       dpVideoPath,\r
-                       videoDestination,\r
-                       executeBeforeCap,\r
-                       executeAfterCap,\r
-                       startSecond,\r
-                       endSecond\r
-               );\r
-               this.jobs.add(newJob);\r
-               this.fireUserInterfaceUpdate(newJob);\r
-               \r
-               return newJob;\r
-       }\r
-       \r
-       public synchronized boolean deleteRecordJob(RecordJob job) {\r
-               if (!this.jobs.contains(job)) {\r
-                       return false;\r
-               }\r
-               \r
-               //don't delete jobs that are scheduled for execution\r
-               if (this.poolExecutor.getJobList().contains(job)) {\r
-                       return false;\r
-               }\r
-               \r
-               this.jobs.remove(job);\r
-               return true;\r
-       }\r
-       \r
-       public void addUserInterfaceListener(DemoRecorderUI ui) {\r
-               this.registeredUserInterfaces.add(ui);\r
-       }\r
-       \r
-       /**\r
-        * Makes sure that all registered user interfaces can update their view/display.\r
-        * @param job either a job that's new to the UI, or one the UI already knows but of which details changed\r
-        */\r
-       public void fireUserInterfaceUpdate(RecordJob job) {\r
-               for (DemoRecorderUI ui : this.registeredUserInterfaces) {\r
-                       ui.RecordJobPropertiesChange(job);\r
-               }\r
-       }\r
-       \r
-       public int getNewJobIndex() {\r
-               int jobIndex;\r
-               if (this.jobs.size() == 0) {\r
-                       jobIndex = 1;\r
-               } else {\r
-                       int greatestIndex = -1;\r
-                       for (RecordJob j : this.jobs) {\r
-                               if (j.getJobIndex() > greatestIndex) {\r
-                                       greatestIndex = j.getJobIndex();\r
-                               }\r
-                       }\r
-                       if (greatestIndex == -1) {\r
-                               jobIndex = 1;\r
-                       } else {\r
-                               jobIndex = greatestIndex + 1;\r
-                       }\r
-               }\r
-               \r
-               return jobIndex;\r
-       }\r
-       \r
-       private void loadJobQueue() {\r
-               File defaultFile = DemoRecorderUtils.computeLocalFile(PREFERENCES_DIRNAME, JOBQUEUE_FILENAME);\r
-               this.loadJobQueue(defaultFile, true);\r
-       }\r
-       \r
-       /**\r
-        * Loads the jobs from the given file path. If override is enabled, the previous\r
-        * job list will be overwritten with the newly loaded list. Otherwise the loaded jobs\r
-        * are added to the already existing list.\r
-        * @param path\r
-        * @param override\r
-        * @return the number of jobs loaded from the file\r
-        */\r
-       @SuppressWarnings("unchecked")\r
-       public int loadJobQueue(File path, boolean override) {\r
-               if (!path.exists()) {\r
-                       return 0;\r
-               }\r
-               \r
-               try {\r
-                       FileInputStream fin = new FileInputStream(path);\r
-                       ObjectInputStream ois = new ObjectInputStream(fin);\r
-                       List<RecordJob> newList = (List<RecordJob>) ois.readObject();\r
-                       for (RecordJob currentJob : newList) {\r
-                               currentJob.setAppLayer(this);\r
-                       }\r
-                       if (override) {\r
-                               this.jobs = newList;\r
-                       } else {\r
-                               this.jobs.addAll(newList);\r
-                       }\r
-                       return newList.size();\r
-               } catch (Exception e) {\r
-                       DemoRecorderUtils.showNonCriticalErrorDialog("Could not load the job queue file " + path.getAbsolutePath(), e, true);\r
-                       return 0;\r
-               }\r
-       }\r
-       \r
-       public void saveJobQueue() {\r
-               File defaultFile = DemoRecorderUtils.computeLocalFile(PREFERENCES_DIRNAME, JOBQUEUE_FILENAME);\r
-               this.saveJobQueue(defaultFile);\r
-       }\r
-       \r
-       public void saveJobQueue(File path) {\r
-               if (!path.exists()) {\r
-                       try {\r
-                               path.createNewFile();\r
-                       } catch (IOException e) {\r
-                               File parentDir = path.getParentFile();\r
-                               if (!parentDir.exists()) {\r
-                                       try {\r
-                                               if (parentDir.mkdirs() == true) {\r
-                                                       try {\r
-                                                               path.createNewFile();\r
-                                                       } catch (Exception ex) {}\r
-                                               }\r
-                                       } catch (Exception ex) {}\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               String exceptionMessage = "Could not save the job queue file " + path.getAbsolutePath();\r
-               \r
-               if (!path.exists()) {\r
-                       DemoRecorderException ex = new DemoRecorderException(exceptionMessage);\r
-                       DemoRecorderUtils.showNonCriticalErrorDialog(ex);\r
-                       return;\r
-               }\r
-               \r
-               //make sure that for the next start of the program the state is set to waiting again\r
-               for (RecordJob job : this.jobs) {\r
-                       if (job.getState() == RecordJob.State.PROCESSING) {\r
-                               job.setState(RecordJob.State.WAITING);\r
-                       }\r
-                       job.setAppLayer(null); //we don't want to serialize the app layer!\r
-               }\r
-               \r
-               try {\r
-                       FileOutputStream fout = new FileOutputStream(path);\r
-                       ObjectOutputStream oos = new ObjectOutputStream(fout);\r
-                       oos.writeObject(this.jobs);\r
-                       oos.close();\r
-               } catch (Exception e) {\r
-                       DemoRecorderUtils.showNonCriticalErrorDialog(exceptionMessage, e, true);\r
-               }\r
-               \r
-               //we sometimes also save the jobqueue and don't exit the program, so restore the applayer again\r
-               for (RecordJob job : this.jobs) {\r
-                       job.setAppLayer(this);\r
-               }\r
-       }\r
-       \r
-       public void shutDown() {\r
-               this.poolExecutor.shutDown();\r
-               this.savePreferences();\r
-               this.saveJobQueue();\r
-       }\r
-       \r
-       public int getState() {\r
-               return this.state;\r
-       }\r
-       \r
-       private void loadPlugins() {\r
-               File pluginDir = DemoRecorderUtils.computeLocalFile(PLUGINS_DIRNAME, "");\r
-\r
-               if (!pluginDir.exists()) {\r
-                       pluginDir.mkdir();\r
-               }\r
-\r
-               File[] jarFiles = pluginDir.listFiles();\r
-\r
-               List<URL> urlList = new ArrayList<URL>();\r
-               for (File f : jarFiles) {\r
-                       try {\r
-                               urlList.add(f.toURI().toURL());\r
-                       } catch (MalformedURLException ex) {}\r
-               }\r
-               ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();\r
-               URL[] urls = new URL[urlList.size()];\r
-               urls = urlList.toArray(urls);\r
-               URLClassLoader classLoader = new URLClassLoader(urls, parentLoader);\r
-               \r
-               ServiceLoader<EncoderPlugin> loader = ServiceLoader.load(EncoderPlugin.class, classLoader);\r
-               for (EncoderPlugin implementation : loader) {\r
-                       this.encoderPlugins.add(implementation);\r
-               }\r
-       }\r
-       \r
-       private void configurePlugins() {\r
-               for (EncoderPlugin plugin : this.encoderPlugins) {\r
-                       plugin.setApplicationLayer(this);\r
-                       Properties pluginPreferences = plugin.getGlobalPreferences();\r
-                       for (Object preference : pluginPreferences.keySet()) {\r
-                               String preferenceString = (String) preference;\r
-                               \r
-                               if (this.preferences.getProperty(plugin.getName(), preferenceString) == null) {\r
-                                       String defaultValue = pluginPreferences.getProperty(preferenceString);\r
-                                       this.preferences.setProperty(plugin.getName(), preferenceString, defaultValue);\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       public List<EncoderPlugin> getEncoderPlugins() {\r
-               return encoderPlugins;\r
-       }\r
-}\r
+package com.nexuiz.demorecorder.application;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.ServiceLoader;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.nexuiz.demorecorder.application.jobs.EncoderJob;
+import com.nexuiz.demorecorder.application.jobs.RecordJob;
+import com.nexuiz.demorecorder.application.jobs.RecordsDoneJob;
+import com.nexuiz.demorecorder.application.plugins.EncoderPlugin;
+import com.nexuiz.demorecorder.ui.DemoRecorderUI;
+
+public class DemoRecorderApplication {
+       
+       public static class Preferences {
+               public static final String OVERWRITE_VIDEO_FILE = "Overwrite final video destination file if it exists";
+               public static final String DISABLE_RENDERING = "Disable rendering while fast-forwarding";
+               public static final String DISABLE_SOUND = "Disable sound while fast-forwarding";
+               public static final String FFW_SPEED_FIRST_STAGE = "Fast-forward speed (first stage)";
+               public static final String FFW_SPEED_SECOND_STAGE = "Fast-forward speed (second stage)";
+               public static final String DO_NOT_DELETE_CUT_DEMOS = "Do not delete cut demos";
+               public static final String JOB_NAME_APPEND_DUPLICATE = "Append this suffix to job-name when duplicating jobs";
+               
+               public static final String[] PREFERENCES_ORDER = {
+                       OVERWRITE_VIDEO_FILE,
+                       DISABLE_RENDERING,
+                       DISABLE_SOUND,
+                       FFW_SPEED_FIRST_STAGE,
+                       FFW_SPEED_SECOND_STAGE,
+                       DO_NOT_DELETE_CUT_DEMOS,
+                       JOB_NAME_APPEND_DUPLICATE
+               };
+       }
+       
+       public static final String PREFERENCES_DIRNAME = "settings";
+       public static final String LOGS_DIRNAME = "logs";
+       public static final String PLUGINS_DIRNAME = "plugins";
+       public static final String APP_PREFERENCES_FILENAME = "app_preferences.xml";
+       public static final String JOBQUEUE_FILENAME = "jobs.dat";
+       
+       public static final int STATE_WORKING = 0;
+       public static final int STATE_IDLE = 1;
+       
+       private RecorderJobPoolExecutor poolExecutor;
+       private List<RecordJob> jobs;
+       private NDRPreferences preferences = null;
+       private List<DemoRecorderUI> registeredUserInterfaces;
+       private List<EncoderPlugin> encoderPlugins;
+       private int state = STATE_IDLE;
+       
+       public DemoRecorderApplication() {
+               poolExecutor = new RecorderJobPoolExecutor();
+               jobs = new CopyOnWriteArrayList<RecordJob>();
+               this.registeredUserInterfaces = new ArrayList<DemoRecorderUI>();
+               this.encoderPlugins = new ArrayList<EncoderPlugin>();
+               this.getPreferences();
+               this.loadPlugins();
+               this.configurePlugins();
+               this.loadJobQueue();
+       }
+       
+       public void setPreference(String category, String preference, boolean value) {
+               this.preferences.setProperty(category, preference, String.valueOf(value));
+       }
+       
+       public void setPreference(String category, String preference, int value) {
+               this.preferences.setProperty(category, preference, String.valueOf(value));
+       }
+       
+       public void setPreference(String category, String preference, String value) {
+               this.preferences.setProperty(category, preference, value);
+       }
+       
+       public NDRPreferences getPreferences() {
+               if (this.preferences == null) {
+                       this.preferences = new NDRPreferences();
+                       this.createPreferenceDefaultValues();
+                       File preferencesFile = DemoRecorderUtils.computeLocalFile(PREFERENCES_DIRNAME, APP_PREFERENCES_FILENAME);
+                       if (preferencesFile.exists()) {
+                               FileInputStream fis = null;
+                               try {
+                                       fis = new FileInputStream(preferencesFile);
+                                       this.preferences.loadFromXML(fis);
+                               } catch (Exception e) {
+                                       DemoRecorderUtils.showNonCriticalErrorDialog("Could not load the application preferences file!", e, true);
+                               }
+                       }
+               }
+               
+               return this.preferences;
+       }
+       
+       private void createPreferenceDefaultValues() {
+               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.OVERWRITE_VIDEO_FILE, "false");
+               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DISABLE_RENDERING, "true");
+               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DISABLE_SOUND, "true");
+               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.FFW_SPEED_FIRST_STAGE, "100");
+               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.FFW_SPEED_SECOND_STAGE, "10");
+               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DO_NOT_DELETE_CUT_DEMOS, "false");
+               this.preferences.setProperty(NDRPreferences.MAIN_APPLICATION, Preferences.JOB_NAME_APPEND_DUPLICATE, " duplicate");
+       }
+       
+       public void savePreferences() {
+               File preferencesFile = DemoRecorderUtils.computeLocalFile(PREFERENCES_DIRNAME, APP_PREFERENCES_FILENAME);
+               if (!preferencesFile.exists()) {
+                       try {
+                               preferencesFile.createNewFile();
+                       } catch (IOException e) {
+                               File parentDir = preferencesFile.getParentFile();
+                               if (!parentDir.exists()) {
+                                       try {
+                                               if (parentDir.mkdirs() == true) {
+                                                       try {
+                                                               preferencesFile.createNewFile();
+                                                       } catch (Exception ex) {}
+                                               }
+                                       } catch (Exception ex) {}
+                               }
+                       }
+               }
+               
+               if (!preferencesFile.exists()) {
+                       DemoRecorderException ex = new DemoRecorderException("Could not create the preferences file " + preferencesFile.getAbsolutePath());
+                       DemoRecorderUtils.showNonCriticalErrorDialog(ex);
+                       return;
+               }
+               
+               FileOutputStream fos;
+               try {
+                       fos = new FileOutputStream(preferencesFile);
+               } catch (FileNotFoundException e) {
+                       DemoRecorderUtils.showNonCriticalErrorDialog("Could not create the preferences file " + preferencesFile.getAbsolutePath() + ". Unsufficient rights?", e, true);
+                       return;
+               }
+               try {
+                       this.preferences.storeToXML(fos, null);
+               } catch (IOException e) {
+                       DemoRecorderUtils.showNonCriticalErrorDialog("Could not create the preferences file " + preferencesFile.getAbsolutePath(), e, true);
+               }
+       }
+       
+       public List<RecordJob> getRecordJobs() {
+               return new ArrayList<RecordJob>(this.jobs);
+       }
+       
+       public void startRecording() {
+               if (this.state != STATE_WORKING) {
+                       this.state = STATE_WORKING;
+                       
+                       for (RecordJob currentJob : this.jobs) {
+                               if (currentJob.getState() == RecordJob.State.WAITING) {
+                                       this.poolExecutor.runJob(currentJob);
+                               }
+                       }
+                       
+                       //notify ourself when job is done
+                       this.poolExecutor.runJob(new RecordsDoneJob(this));
+               }
+       }
+       
+       public void recordSelectedJobs(List<RecordJob> jobList) {
+               if (this.state == STATE_IDLE) {
+                       this.state = STATE_WORKING;
+                       for (RecordJob currentJob : jobList) {
+                               if (currentJob.getState() == RecordJob.State.WAITING) {
+                                       this.poolExecutor.runJob(currentJob);
+                               }
+                       }
+                       
+                       //notify ourself when job is done
+                       this.poolExecutor.runJob(new RecordsDoneJob(this));
+               }
+       }
+       
+       public void executePluginForSelectedJobs(EncoderPlugin plugin, List<RecordJob> jobList) {
+               if (this.state == STATE_IDLE) {
+                       this.state = STATE_WORKING;
+                       for (RecordJob currentJob : jobList) {
+                               if (currentJob.getState() == RecordJob.State.DONE) {
+                                       this.poolExecutor.runJob(new EncoderJob(currentJob, plugin));
+                               }
+                       }
+                       
+                       //notify ourself when job is done
+                       this.poolExecutor.runJob(new RecordsDoneJob(this));
+               }
+       }
+       
+       public void notifyAllJobsDone() {
+               this.state = STATE_IDLE;
+               
+               //notify all UIs
+               for (DemoRecorderUI currentUI : this.registeredUserInterfaces) {
+                       currentUI.recordingFinished();
+               }
+       }
+       
+       public synchronized void stopRecording() {
+               if (this.state == STATE_WORKING) {
+                       //clear the queue of the threadpoolexecutor and add the GUI/applayer notify job again
+                       this.poolExecutor.clearUnfinishedJobs();
+                       this.poolExecutor.runJob(new RecordsDoneJob(this));
+               }
+       }
+       
+       public RecordJob createRecordJob(
+               String name,
+               File enginePath,
+               String engineParameters,
+               File demoFile,
+               String relativeDemoPath,
+               File dpVideoPath,
+               File videoDestination,
+               String executeBeforeCap,
+               String executeAfterCap,
+               float startSecond,
+               float endSecond
+       ) {
+               int jobIndex = -1;
+               if (name == null || name.equals("")) {
+                       //we don't have a name, so use a generic one 
+                       jobIndex = this.getNewJobIndex();
+                       name = "Job " + jobIndex;
+               } else {
+                       //just use the name and keep jobIndex at -1. Jobs with real names don't need an index
+               }
+               
+               
+               
+               RecordJob newJob = new RecordJob(
+                       this,
+                       name,
+                       jobIndex,
+                       enginePath,
+                       engineParameters,
+                       demoFile,
+                       relativeDemoPath,
+                       dpVideoPath,
+                       videoDestination,
+                       executeBeforeCap,
+                       executeAfterCap,
+                       startSecond,
+                       endSecond
+               );
+               this.jobs.add(newJob);
+               this.fireUserInterfaceUpdate(newJob);
+               
+               return newJob;
+       }
+       
+       public synchronized boolean deleteRecordJob(RecordJob job) {
+               if (!this.jobs.contains(job)) {
+                       return false;
+               }
+               
+               //don't delete jobs that are scheduled for execution
+               if (this.poolExecutor.getJobList().contains(job)) {
+                       return false;
+               }
+               
+               this.jobs.remove(job);
+               return true;
+       }
+       
+       public void addUserInterfaceListener(DemoRecorderUI ui) {
+               this.registeredUserInterfaces.add(ui);
+       }
+       
+       /**
+        * Makes sure that all registered user interfaces can update their view/display.
+        * @param job either a job that's new to the UI, or one the UI already knows but of which details changed
+        */
+       public void fireUserInterfaceUpdate(RecordJob job) {
+               for (DemoRecorderUI ui : this.registeredUserInterfaces) {
+                       ui.RecordJobPropertiesChange(job);
+               }
+       }
+       
+       public int getNewJobIndex() {
+               int jobIndex;
+               if (this.jobs.size() == 0) {
+                       jobIndex = 1;
+               } else {
+                       int greatestIndex = -1;
+                       for (RecordJob j : this.jobs) {
+                               if (j.getJobIndex() > greatestIndex) {
+                                       greatestIndex = j.getJobIndex();
+                               }
+                       }
+                       if (greatestIndex == -1) {
+                               jobIndex = 1;
+                       } else {
+                               jobIndex = greatestIndex + 1;
+                       }
+               }
+               
+               return jobIndex;
+       }
+       
+       private void loadJobQueue() {
+               File defaultFile = DemoRecorderUtils.computeLocalFile(PREFERENCES_DIRNAME, JOBQUEUE_FILENAME);
+               this.loadJobQueue(defaultFile, true);
+       }
+       
+       /**
+        * Loads the jobs from the given file path. If override is enabled, the previous
+        * job list will be overwritten with the newly loaded list. Otherwise the loaded jobs
+        * are added to the already existing list.
+        * @param path
+        * @param override
+        * @return the number of jobs loaded from the file
+        */
+       @SuppressWarnings("unchecked")
+       public int loadJobQueue(File path, boolean override) {
+               if (!path.exists()) {
+                       return 0;
+               }
+               
+               try {
+                       FileInputStream fin = new FileInputStream(path);
+                       ObjectInputStream ois = new ObjectInputStream(fin);
+                       List<RecordJob> newList = (List<RecordJob>) ois.readObject();
+                       for (RecordJob currentJob : newList) {
+                               currentJob.setAppLayer(this);
+                       }
+                       if (override) {
+                               this.jobs = newList;
+                       } else {
+                               this.jobs.addAll(newList);
+                       }
+                       return newList.size();
+               } catch (Exception e) {
+                       DemoRecorderUtils.showNonCriticalErrorDialog("Could not load the job queue file " + path.getAbsolutePath(), e, true);
+                       return 0;
+               }
+       }
+       
+       public void saveJobQueue() {
+               File defaultFile = DemoRecorderUtils.computeLocalFile(PREFERENCES_DIRNAME, JOBQUEUE_FILENAME);
+               this.saveJobQueue(defaultFile);
+       }
+       
+       public void saveJobQueue(File path) {
+               if (!path.exists()) {
+                       try {
+                               path.createNewFile();
+                       } catch (IOException e) {
+                               File parentDir = path.getParentFile();
+                               if (!parentDir.exists()) {
+                                       try {
+                                               if (parentDir.mkdirs() == true) {
+                                                       try {
+                                                               path.createNewFile();
+                                                       } catch (Exception ex) {}
+                                               }
+                                       } catch (Exception ex) {}
+                               }
+                       }
+               }
+               
+               String exceptionMessage = "Could not save the job queue file " + path.getAbsolutePath();
+               
+               if (!path.exists()) {
+                       DemoRecorderException ex = new DemoRecorderException(exceptionMessage);
+                       DemoRecorderUtils.showNonCriticalErrorDialog(ex);
+                       return;
+               }
+               
+               //make sure that for the next start of the program the state is set to waiting again
+               for (RecordJob job : this.jobs) {
+                       if (job.getState() == RecordJob.State.PROCESSING) {
+                               job.setState(RecordJob.State.WAITING);
+                       }
+                       job.setAppLayer(null); //we don't want to serialize the app layer!
+               }
+               
+               try {
+                       FileOutputStream fout = new FileOutputStream(path);
+                       ObjectOutputStream oos = new ObjectOutputStream(fout);
+                       oos.writeObject(this.jobs);
+                       oos.close();
+               } catch (Exception e) {
+                       DemoRecorderUtils.showNonCriticalErrorDialog(exceptionMessage, e, true);
+               }
+               
+               //we sometimes also save the jobqueue and don't exit the program, so restore the applayer again
+               for (RecordJob job : this.jobs) {
+                       job.setAppLayer(this);
+               }
+       }
+       
+       public void shutDown() {
+               this.poolExecutor.shutDown();
+               this.savePreferences();
+               this.saveJobQueue();
+       }
+       
+       public int getState() {
+               return this.state;
+       }
+       
+       private void loadPlugins() {
+               File pluginDir = DemoRecorderUtils.computeLocalFile(PLUGINS_DIRNAME, "");
+
+               if (!pluginDir.exists()) {
+                       pluginDir.mkdir();
+               }
+
+               File[] jarFiles = pluginDir.listFiles();
+
+               List<URL> urlList = new ArrayList<URL>();
+               for (File f : jarFiles) {
+                       try {
+                               urlList.add(f.toURI().toURL());
+                       } catch (MalformedURLException ex) {}
+               }
+               ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
+               URL[] urls = new URL[urlList.size()];
+               urls = urlList.toArray(urls);
+               URLClassLoader classLoader = new URLClassLoader(urls, parentLoader);
+               
+               ServiceLoader<EncoderPlugin> loader = ServiceLoader.load(EncoderPlugin.class, classLoader);
+               for (EncoderPlugin implementation : loader) {
+                       this.encoderPlugins.add(implementation);
+               }
+       }
+       
+       private void configurePlugins() {
+               for (EncoderPlugin plugin : this.encoderPlugins) {
+                       plugin.setApplicationLayer(this);
+                       Properties pluginPreferences = plugin.getGlobalPreferences();
+                       for (Object preference : pluginPreferences.keySet()) {
+                               String preferenceString = (String) preference;
+                               
+                               if (this.preferences.getProperty(plugin.getName(), preferenceString) == null) {
+                                       String defaultValue = pluginPreferences.getProperty(preferenceString);
+                                       this.preferences.setProperty(plugin.getName(), preferenceString, defaultValue);
+                               }
+                       }
+               }
+       }
+
+       public List<EncoderPlugin> getEncoderPlugins() {
+               return encoderPlugins;
+       }
+}