view java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.java @ 1:e3935c01cde2 tip

moved license of PdPersistenceManager to the beginning of the file
author Fiore Martin <f.martin@qmul.ac.uk>
date Tue, 08 Jul 2014 19:52:03 +0100
parents 78b7fc5391a2
children
line wrap: on
line source
/*  
 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;
}