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