diff java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.java @ 0:78b7fc5391a2

first import, outcome of NIME 2014 hackaton
author Fiore Martin <f.martin@qmul.ac.uk>
date Tue, 08 Jul 2014 16:28:59 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.java	Tue Jul 08 16:28:59 2014 +0100
@@ -0,0 +1,297 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ Copyright (C) 2011  Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/  
+
+package uk.ac.qmul.eecs.ccmi.main;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+import uk.ac.qmul.eecs.ccmi.gui.Diagram;
+import uk.ac.qmul.eecs.ccmi.gui.EditorFrame;
+import uk.ac.qmul.eecs.ccmi.gui.HapticKindle;
+import uk.ac.qmul.eecs.ccmi.gui.SpeechOptionPane;
+import uk.ac.qmul.eecs.ccmi.gui.TemplateEditor;
+import uk.ac.qmul.eecs.ccmi.haptics.Haptics;
+import uk.ac.qmul.eecs.ccmi.haptics.HapticsFactory;
+import uk.ac.qmul.eecs.ccmi.simpletemplate.SimpleTemplateEditor;
+import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
+import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
+import uk.ac.qmul.eecs.ccmi.utils.CCmIUncaughtExceptionHandler;
+import uk.ac.qmul.eecs.ccmi.utils.InteractionLog;
+import uk.ac.qmul.eecs.ccmi.utils.PreferencesService;
+import uk.ac.qmul.eecs.ccmi.utils.ResourceFileWriter;
+
+/**
+ * 
+ * The application class with the main method. The main performs 
+ * the start up initialization  and then displays the graphical user interface.
+ *
+ */
+public class DiagramEditorApp implements Runnable {
+	
+	/* initialize all the non gui resources */
+	private void init() throws IOException {
+		Thread.setDefaultUncaughtExceptionHandler(new CCmIUncaughtExceptionHandler());
+		final ResourceBundle resources = ResourceBundle.getBundle(DiagramEditorApp.class.getName());
+		/* create the home directory if it does not exist and store the path into the preferences */
+		PreferencesService preferences = PreferencesService.getInstance(); 
+		String homeDirPath = preferences.get("home", null);
+		if(homeDirPath == null){
+			homeDirPath = new StringBuilder(System.getProperty("user.home")).append(System.getProperty("file.separator")).append(resources.getString("dir.home")).toString();
+			preferences.put("home", homeDirPath);
+		}
+		File homeDir = new File(homeDirPath);
+		mkDir(homeDir, resources);
+		
+		File backupDir = new File(homeDir,resources.getString("dir.backups"));
+		mkDir(backupDir, resources);
+		backupDirPath = backupDir.getAbsolutePath();
+		
+		/* create the templates directory into the home directory */ 
+		File templateDir = new File(homeDir,resources.getString("dir.templates"));
+		mkDir(templateDir,resources);
+		
+		/* create the images directory into the home directory */
+		File imagesDir = new File(homeDir,resources.getString("dir.images"));
+		mkDir(imagesDir,resources);
+		preferences.put("dir.images", imagesDir.getAbsolutePath());
+		
+		/* create the diagrams dir into the home directory */
+		File diagramDir = new File(homeDir,resources.getString("dir.diagrams"));
+		mkDir(diagramDir,resources);
+		preferences.put("dir.diagrams", diagramDir.getAbsolutePath());
+		
+		/* create the libs directory into he home directory */
+		File libsDir = new File(homeDir,resources.getString("dir.libs"));
+		mkDir(libsDir,resources);
+		preferences.put("dir.libs", libsDir.getAbsolutePath());
+		
+		/* write the template files included in the software in the template dir, if they don't exist yet */
+		ResourceFileWriter resourceWriter = new ResourceFileWriter(getClass().getResource("UML Diagram.xml"));
+		resourceWriter.writeOnDisk(templateDir.getAbsolutePath(),"UML Diagram.xml");
+		resourceWriter.setResource(getClass().getResource("Tube.xml"));
+		resourceWriter.writeOnDisk(templateDir.getAbsolutePath(),"Tube.xml");
+		resourceWriter.setResource(getClass().getResource("Organization Chart.xml"));
+		resourceWriter.writeOnDisk(templateDir.getAbsolutePath(),"Organization Chart.xml");
+		
+		/* read the template files into an array to pass to the EditorFrame instance */
+		FilenameFilter filter = new FilenameFilter() {
+			@Override
+		    public boolean accept(File f, String name) {
+		    	return (name.endsWith(resources.getString("template.extension")));
+		    }
+		};
+		templateFiles = templateDir.listFiles(filter);
+		File logDir = new File(homeDir,resources.getString("dir.log"));
+		mkDir(logDir,resources);
+		preferences.put("dir.log", logDir.getAbsolutePath());
+		
+		String enableLog = preferences.get("enable_log", "false");
+		if(Boolean.parseBoolean(enableLog)){
+			try{
+				InteractionLog.enable(logDir.getAbsolutePath());
+				InteractionLog.log("PROGRAM STARTED");
+			}catch(IOException ioe){
+				/* if logging was enabled, the possibility to log is considered inescapable */
+				/* at launch time: do not allow the execution to continue any further       */
+				throw new RuntimeException(ioe);
+			}
+		}
+		
+		/* create sound, speech engines */
+		NarratorFactory.createInstance();
+		SoundFactory.createInstance();
+	}
+	
+	/* loads haptic device. If the user is running the software for the first time *
+	 * they're prompted with a dialog to select the device they want to use        */
+	private void initHaptics(){
+		final PreferencesService preferences = PreferencesService.getInstance();
+		if(!Boolean.parseBoolean(preferences.get("haptic_device.initialized", "false"))){
+			try {
+				SwingUtilities.invokeAndWait(new Runnable(){
+					@Override
+					public void run() {
+						try {
+							UIManager.setLookAndFeel(
+									UIManager.getSystemLookAndFeelClassName());
+						}catch(Exception e){/* nevermind */}
+						
+						String [] hapticDevices = {
+								HapticsFactory.PHANTOM_ID,
+								HapticsFactory.FALCON_ID,
+								HapticsFactory.TABLET_ID};
+						String selection = (String)SpeechOptionPane.showSelectionDialog(null, 
+								ResourceBundle.getBundle(DiagramEditorApp.class.getName()).getString("haptics_init.welcome"), 
+								hapticDevices, 
+								hapticDevices[2]);
+						if(selection == null)
+							System.exit(0);
+						preferences.put("haptic_device", selection);
+						preferences.put("haptic_device.initialized", "true");
+					}
+				});
+			}catch(InvocationTargetException ite){
+				throw new RuntimeException(ite);
+			}catch(InterruptedException ie){
+				throw new RuntimeException(ie);
+			}
+		}
+	
+		/* creates the Haptics instance */
+		HapticsFactory.createInstance(new HapticKindle());
+		haptics = HapticsFactory.getInstance();
+		if(haptics.isAlive())
+			NarratorFactory.getInstance().speakWholeText("Haptic device successfully initialized");
+	}
+
+	/* return true if the directory was created, false if it existed before */
+	private boolean mkDir(File dir,ResourceBundle resources) throws IOException{
+		boolean created = dir.mkdir();
+		if(!dir.exists())
+			throw new IOException(MessageFormat.format(
+					resources.getString("dir.error_msg"),
+					dir.getAbsolutePath())
+					);
+		return created;
+	}
+	
+	/**
+	 * build up the GUI and display it
+	 */
+	@Override
+	public void run() {
+		editorFrame = new EditorFrame(haptics,getTemplateFiles(),backupDirPath,getTemplateEditors(),getDiagrams());
+	}
+	
+	/**
+	 * Provides template editors to create own templates using the diagram editor.
+	 * 
+	 * Subclasses who don't want any template editor to appear in the diagram 
+	 * can just return an empty array. Returning {@code null} will throw an exception.
+	 * 
+	 * @return an array of template editors 
+	 */
+	protected TemplateEditor[] getTemplateEditors(){
+		TemplateEditor[] templateEditors = new TemplateEditor[1];
+		templateEditors[0] = new SimpleTemplateEditor();
+		return templateEditors;
+	}
+	
+	/**
+	 * Returns the template files detected in the ccmi_editor_data/templates directory.
+	 * 
+	 * Returning {@code null} will throw an exception.
+	 * 
+	 * @return an array of (xml) Files containing a template 
+	 */
+	protected File[] getTemplateFiles(){
+		return templateFiles;
+	}
+	
+	/**
+	 * Returns an empty list. This method can be overwritten by subclasses to 
+	 * provide their own custom diagrams. Such diagrams will appear in the menu.
+	 * 
+	 * Returning {@code null} will throw an exception.
+	 * 
+	 * @return an array of diagram templates. The array is empty in this implementation. 
+	 */
+	protected Diagram[] getDiagrams(){
+		return new Diagram[0];
+	}
+
+	/**
+	 * The main function 
+	 * @param args this software accepts no args from the command line
+	 */
+	public static void main(String[] args){
+		DiagramEditorApp application = new DiagramEditorApp();	
+		mainImplementation(application);
+	}
+	
+	
+	/**
+	 * Implementation of the main body. It can be used to run the program 
+	 * using a subclass of {@code DiagramEditorApp}, providing it's own
+	 * diagram templates 
+	 *  
+	 * @param application the diagram editor application to execute 
+	 */
+	public final static void mainImplementation(DiagramEditorApp application) {	
+		try{
+			application.init();
+		} catch (IOException e) {
+			final String msg = e.getLocalizedMessage();
+			try {
+				SwingUtilities.invokeAndWait(new Runnable(){
+					@Override
+					public void run(){
+						try {
+							UIManager.setLookAndFeel(
+									UIManager.getSystemLookAndFeelClassName());
+						}catch(Exception e){/* nevermind */}
+						JOptionPane.showMessageDialog(null, msg);
+					}
+				});
+			} catch (InterruptedException ex) {
+				throw new RuntimeException(ex);
+			} catch (InvocationTargetException ex) {
+				throw new RuntimeException(ex);
+			}
+			System.exit(-1);
+		}
+		
+		application.initHaptics();
+		
+		/* start the application */
+		try {
+			SwingUtilities.invokeAndWait(application);
+        } catch (InvocationTargetException ex) {
+            throw new RuntimeException(ex);
+        } catch (InterruptedException ex) {
+        	throw new RuntimeException(ex);
+        }    
+	}
+	
+	/**
+	 * Returns the reference to the unique {@code EditorFrame} instance of the program. 
+	 * The main GUI class. 
+	 *    
+	 * @return an reference to {@code EditorFrame} 
+	 */
+	public static EditorFrame getFrame(){
+		return editorFrame;
+	}
+	
+	private static EditorFrame editorFrame;
+	Haptics haptics;
+	File[] templateFiles;
+	TemplateEditor[] templateCreators;
+	String backupDirPath;
+}
+