Mercurial > hg > ccmieditor
changeset 6:1c5af356bb99
added 64 bit native narrator
allow hapitic native dll to load only on 32 bit JVM
refactored DiagramEditorApp for better inheritance
fixed Java 7 bug: NullPointerException when typing
minor bug fixes
added splashscreen
line wrap: on
line diff
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.java Mon Dec 17 18:39:40 2012 +0000 @@ -114,7 +114,7 @@ * tree representations of diagrams. */ @SuppressWarnings("serial") -public class EditorFrame extends JFrame { +public class EditorFrame extends JFrame { /** * Creates a new {@code EditorFrame} * @@ -122,9 +122,11 @@ * @param templateFiles an array of template files. New diagrams can be created from template files by clonation * @param backupDirPath the path of a folder where all the currently open diagrams will be saved if * the haptic device crashes - * @param templateEditors the template editors for this instance of the program + * @param templateEditors the template editors for this instance of the program + * @param additionalTemplate additional diagram templates. An entry will be created in {@code File->New Diagram} + * for each template */ - public EditorFrame(Haptics haptics, File[] templateFiles, String backupDirPath, TemplateEditor[] templateEditors){ + public EditorFrame(Haptics haptics, File[] templateFiles, String backupDirPath, TemplateEditor[] templateEditors, Diagram[] additionalTemplate){ this.backupDirPath = backupDirPath; /* load resources */ resources = ResourceBundle.getBundle(this.getClass().getName()); @@ -209,16 +211,20 @@ /* setup listeners */ initListeners(); /* set up menus */ - this.templateEditors = templateEditors; existingTemplateNames = new ArrayList<String>(10); existingTemplates = new ArrayList<Diagram>(10); int extensionLength = resources.getString("template.extension").length(); for(File file : templateFiles){ existingTemplateNames.add(file.getName().substring(0, file.getName().length()-extensionLength)); } - initMenu(); + initMenu(templateEditors); /* read template files. this call must be placed after menu creation as it adds menu items to file->new-> */ boolean someTemplateFilesNotRead = readTemplateFiles(templateFiles); + + for(Diagram additionalDiagram : additionalTemplate){ + addDiagramType(additionalDiagram); + } + /* become visible */ pack(); setVisible(true); @@ -303,10 +309,10 @@ }; } - private void initMenu(){ + private void initMenu(TemplateEditor[] templateEditors){ ResourceFactory factory = new ResourceFactory(resources); - JMenuBar menuBar = factory.createMenuBar(); + JMenuBar menuBar = SpeechMenuFactory.getMenuBar(); setJMenuBar(menuBar); /* --- FILE MENU --- */ @@ -330,6 +336,9 @@ fileSaveAsItem = factory.createMenuItem("file.save_as", this, "saveFileAs"); fileMenu.add(fileSaveAsItem); + + fileSaveCopyItem = factory.createMenuItem("file.save_copy", this, "saveCopy"); + fileMenu.add(fileSaveCopyItem); fileCloseItem = factory.createMenuItem("file.close",this,"closeFile"); fileMenu.add(fileCloseItem); @@ -503,78 +512,79 @@ } /* --- TEMPLATE --- */ - JMenu templateMenu = factory.createMenu("template"); - menuBar.add(templateMenu); + if(templateEditors.length > 0){ + JMenu templateMenu = factory.createMenu("template"); + menuBar.add(templateMenu); - for(final TemplateEditor templateEditor : templateEditors){ - templateMenu.add(factory.createMenuItemFromLabel( - templateEditor.getLabelForNew(), - new ActionListener(){ - @Override - public void actionPerformed(ActionEvent evt) { - Diagram diagram = templateEditor.createNew(EditorFrame.this, existingTemplateNames); - if(diagram == null) - return; - try{ - saveDiagramTemplate(diagram); - }catch(IOException ioe){ - SpeechOptionPane.showMessageDialog( - EditorFrame.this, - resources.getString("dialog.error.save_template")); - return; - } - addDiagramType(diagram); + for(final TemplateEditor templateEditor : templateEditors){ + JMenuItem newDiagramItem = SpeechMenuFactory.getMenuItem(templateEditor.getLabelForNew()); + newDiagramItem.addActionListener(new ActionListener(){ + @Override + public void actionPerformed(ActionEvent evt) { + Diagram diagram = templateEditor.createNew(EditorFrame.this, existingTemplateNames); + if(diagram == null) + return; + try{ + saveDiagramTemplate(diagram); + }catch(IOException ioe){ SpeechOptionPane.showMessageDialog( - EditorFrame.this, - MessageFormat.format(resources.getString("dialog.template_created"), diagram.getName()), - SpeechOptionPane.INFORMATION_MESSAGE); - SoundFactory.getInstance().play(SoundEvent.OK,null); + EditorFrame.this, + resources.getString("dialog.error.save_template")); + return; } - }) - ); + addDiagramType(diagram); + SpeechOptionPane.showMessageDialog( + EditorFrame.this, + MessageFormat.format(resources.getString("dialog.template_created"), diagram.getName()), + SpeechOptionPane.INFORMATION_MESSAGE); + SoundFactory.getInstance().play(SoundEvent.OK,null); + } + }); + templateMenu.add(newDiagramItem); - templateMenu.add(factory.createMenuItemFromLabel( - templateEditor.getLabelForEdit(), - new ActionListener(){ - @Override - public void actionPerformed(ActionEvent evt){ - if(existingTemplates.isEmpty()){ - NarratorFactory.getInstance().speak(resources.getString("dialog.error.no_template_to_edit")); - return; - } - Diagram[] diagrams = new Diagram[existingTemplates.size()]; - diagrams = existingTemplates.toArray(diagrams); - Diagram selectedDiagram = (Diagram)SpeechOptionPane.showSelectionDialog( + JMenuItem editDiagramItem = SpeechMenuFactory.getMenuItem(templateEditor.getLabelForEdit()); + editDiagramItem.addActionListener(new ActionListener(){ + @Override + public void actionPerformed(ActionEvent evt){ + if(existingTemplates.isEmpty()){ + NarratorFactory.getInstance().speak(resources.getString("dialog.error.no_template_to_edit")); + return; + } + Diagram[] diagrams = new Diagram[existingTemplates.size()]; + diagrams = existingTemplates.toArray(diagrams); + Diagram selectedDiagram = (Diagram)SpeechOptionPane.showSelectionDialog( + EditorFrame.this, + resources.getString("dialog.input.edit_diagram_template"), + diagrams, + diagrams[0]); + + if(selectedDiagram == null){ + SoundFactory.getInstance().play(SoundEvent.CANCEL); + return; + } + + Diagram diagram = templateEditor.edit(EditorFrame.this, existingTemplateNames,selectedDiagram); + if(diagram == null) + return; + try{ + saveDiagramTemplate(diagram); + }catch(IOException ioe){ + SpeechOptionPane.showMessageDialog( EditorFrame.this, - resources.getString("dialog.input.edit_diagram_template"), - diagrams, - diagrams[0]); - - if(selectedDiagram == null){ - SoundFactory.getInstance().play(SoundEvent.CANCEL); - return; - } - - Diagram diagram = templateEditor.edit(EditorFrame.this, existingTemplateNames,selectedDiagram); - if(diagram == null) - return; - try{ - saveDiagramTemplate(diagram); - }catch(IOException ioe){ - SpeechOptionPane.showMessageDialog( - EditorFrame.this, - resources.getString("dialog.error.save_template")); - return; - } - addDiagramType(diagram); - SpeechOptionPane.showMessageDialog( - EditorFrame.this, - MessageFormat.format(resources.getString("dialog.template_created"), diagram.getName()), - SpeechOptionPane.INFORMATION_MESSAGE); - SoundFactory.getInstance().play(SoundEvent.OK,null); + resources.getString("dialog.error.save_template")); + return; } + addDiagramType(diagram); + SpeechOptionPane.showMessageDialog( + EditorFrame.this, + MessageFormat.format(resources.getString("dialog.template_created"), diagram.getName()), + SpeechOptionPane.INFORMATION_MESSAGE); + SoundFactory.getInstance().play(SoundEvent.OK,null); } - )); + } + ); + templateMenu.add(editDiagramItem); + } } /* --- COLLABORATION ---- */ @@ -934,14 +944,14 @@ DiagramPanel diagramPanel = getActiveTab(); if (diagramPanel == null) // no tabs open return false; - String fileName = diagramPanel.getFilePath(); - if (fileName == null) { + String filePath = diagramPanel.getFilePath(); + if (filePath == null) { return saveFileAs(); } OutputStream out = null; try{ - File file = new File(fileName); + File file = new File(filePath); out = new BufferedOutputStream(new FileOutputStream(file)); Diagram d = diagramPanel.getDiagram(); PersistenceManager.encodeDiagramInstance(d, out); @@ -960,6 +970,41 @@ } /** + * Prompts the user with a dialog for saving a diagram on disk. The current diagram is not affected + * by this call and it keeps to be displayed in the editor. + * + * @return {@code true} if the file is successfully saved, or {@code false} otherwise. + */ + public boolean saveCopy(){ + DiagramPanel diagramPanel = getActiveTab(); + if (diagramPanel == null) // no tabs open + return false; + OutputStream out = null; + FileService.Save save; + try { + save = fileService.save( + PreferencesService.getInstance().get("dir.diagrams", "."), + diagramPanel.getFilePath(), //default file to save to + extensionFilter, + null, + defaultExtension, + null); + out = save.getOutputStream(); + if (out == null) /* user didn't select any file for saving */ + return false; + PersistenceManager.encodeDiagramInstance(diagramPanel.getDiagram(),save.getName(), out); + speakFocusedComponent(resources.getString("dialog.file_saved")); + return true; + } catch (IOException ioe) { + SpeechOptionPane.showMessageDialog(editorTabbedPane,ioe.getLocalizedMessage()); + return false; + } finally { + if(out != null) + try{out.close();}catch(IOException ioe){ioe.printStackTrace();} + } + } + + /** * Saves the current diagram as a new file. The user is prompter with a {@code FileChooser} * to chose a file to save the diagram to. * @@ -969,6 +1014,7 @@ DiagramPanel diagramPanel = getActiveTab(); if (diagramPanel == null) // no tabs open return false; + String oldName = diagramPanel.getDiagram().getName(); OutputStream out = null; try { String[] currentTabs = new String[editorTabbedPane.getTabCount()]; @@ -987,10 +1033,22 @@ return false; PersistenceManager.encodeDiagramInstance(diagramPanel.getDiagram(),save.getName(), out); - /* update the diagram panel, after the saving */ + /* update the diagram and the diageam panel, after the saving */ + diagramPanel.getDiagram().setName(save.getName()); diagramPanel.setFilePath(save.getPath()); diagramPanel.setModified(false); speakFocusedComponent(resources.getString("dialog.file_saved")); + /* update the haptics, remove first the diagram that is going o be renamed * + * and then re-add the diagram under the new name (avoids haptics * + * IllegalArgumentException when switching tab) */ + haptics.removeDiagram(oldName, null); + haptics.addNewDiagram(diagramPanel.getDiagram().getName()); + for(Node n : diagramPanel.getDiagram().getCollectionModel().getNodes()) + haptics.addNode(n.getBounds().getCenterX(), n.getBounds().getCenterY(), System.identityHashCode(n),null); + for(Edge e : diagramPanel.getDiagram().getCollectionModel().getEdges()){ + Edge.PointRepresentation pr = e.getPointRepresentation(); + haptics.addEdge(System.identityHashCode(e),pr.xs,pr.ys,pr.adjMatrix,pr.nodeStart,e.getStipplePattern(),e.getNameLine(),null); + } return true; }catch(IOException ioe){ SpeechOptionPane.showMessageDialog(editorTabbedPane,ioe.getLocalizedMessage()); @@ -1658,8 +1716,13 @@ server.share(diagram); ProtocolFactory.newInstance().send(localSocket, new Command(Command.Name.LOCAL,diagram.getName(),DiagramEventSource.NONE)); dPanel.setDiagram(NetDiagram.wrapLocalHost(diagram,localSocket,server.getLocalhostQueue(diagram.getName()),netLocalDiagramExceptionHandler)); + /* share a diagram once is enought :) */ shareDiagramMenuItem.setEnabled(false); + /* no close, there might be clients connected. unshare first. */ fileCloseItem.setEnabled(false); + /* only enabled for local diagram, otherwise changing the name * + * of the diagram would messes up the network protocol */ + fileSaveAsItem.setEnabled(false); dPanel.setAwarenessPanelEnabled(true); } catch (IOException e) { iLog("error sharing diagram",diagram.getName()+" "+e.getLocalizedMessage()); @@ -2118,8 +2181,8 @@ /* this is to prevent the user from creating other diagram prototypes with the same name */ existingTemplateNames.add(diagram.getName()); existingTemplates.add(diagram); - newMenu.add(new ResourceFactory(resources).configure(SpeechMenuFactory.getMenuItem(diagram.getName()),"", - new ActionListener(){ + JMenuItem newTypeItem = SpeechMenuFactory.getMenuItem(diagram.getName()); + newTypeItem.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent event){ Diagram clone = (Diagram)diagram.clone(); @@ -2141,7 +2204,8 @@ addTab(null, clone); iLog("new diagram created of type: "+diagram.getName()); } - })); + }); + newMenu.add(newTypeItem); } /** @@ -2190,7 +2254,7 @@ haptics.addNode(n.getBounds().getCenterX(), n.getBounds().getCenterY(), System.identityHashCode(n),null); for(Edge e : diagram.getCollectionModel().getEdges()){ Edge.PointRepresentation pr = e.getPointRepresentation(); - HapticsFactory.getInstance().addEdge(System.identityHashCode(e),pr.xs,pr.ys,pr.adjMatrix,pr.nodeStart,e.getStipplePattern(),e.getNameLine(),null); + haptics.addEdge(System.identityHashCode(e),pr.xs,pr.ys,pr.adjMatrix,pr.nodeStart,e.getStipplePattern(),e.getNameLine(),null); } /* install the listener that handling the haptics device and the one handling the audio feedback */ diagram.getCollectionModel().addCollectionListener(hapticTrigger); @@ -2210,6 +2274,7 @@ private void diagramPanelEnabledMenuUpdate(DiagramPanel dPanel){ fileSaveItem.setEnabled(false); fileSaveAsItem.setEnabled(false); + fileSaveCopyItem.setEnabled(false); fileCloseItem.setEnabled(false); shareDiagramMenuItem.setEnabled(false); graphExportItem.setEnabled(false); @@ -2219,13 +2284,17 @@ return; fileSaveItem.setEnabled(true); - fileSaveAsItem.setEnabled(true); + fileSaveCopyItem.setEnabled(true); graphExportItem.setEnabled(true); if(dPanel.getDiagram() instanceof NetDiagram){ if(dPanel.isAwarenessPanelVisible()) hideAwarenessPanelMenuItem.setEnabled(true); else showAwarenessPanelMenuItem.setEnabled(true); + }else{ + /* only enabled for local diagram, otherwise changing the name * + * of the diagram would messes up the network protocol */ + fileSaveAsItem.setEnabled(true); } boolean isSharedDiagram = dPanel.getDiagram() instanceof NetDiagram; @@ -2326,7 +2395,6 @@ private PreferencesService preferences; private HapticTrigger hapticTrigger; private DiagramElement hapticHighlightDiagramElement; - private TemplateEditor[] templateEditors; private ArrayList<String> existingTemplateNames; private ArrayList<Diagram> existingTemplates; private AwarenessPanelEnablingListener awarenessPanelListener; @@ -2337,6 +2405,7 @@ private JMenuItem fileSaveItem; private JMenuItem graphExportItem; private JMenuItem fileSaveAsItem; + private JMenuItem fileSaveCopyItem; private JMenuItem fileCloseItem; private JMenuItem insertMenuItem; private JMenuItem deleteMenuItem;
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.properties Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.properties Mon Dec 17 18:39:40 2012 +0000 @@ -31,7 +31,7 @@ dialog.about.license=This program comes with ABSOLUTELY NO WARRANTY.\u000AThis is free software, and you are welcome to redistribute it\u000Aunder certain conditions.\ Select License in the Help menu for details. dialog.about.title=About -dialog.license.title=License +dialog.license.title=License dialog.ok_button=Ok dialog.cancel_button=Cancel @@ -71,11 +71,11 @@ dialog.input.bookmark.delete=Select bookmark to remove dialog.input.bookmark.title=Bookmark dialog.input.notes.title=Notes -dialog.input.notes.text=Edit notes for +dialog.input.notes.text=Edit Notes for dialog.input.property.text=New {0}, enter name dialog.input.rename=Renaming {0}, Enter new name. dialog.input.jump.select=Where would you like to jump to ? -dialog.input.edge_operation.label=Set label +dialog.input.edge_operation.label=Set Label dialog.input.edge_operation.arrow_head=Set Arrow Head dialog.input.edge_operation.title= Edge End Operation dialog.input.edge_operation.select=What would you like to do ? @@ -107,6 +107,7 @@ dialog.lock_failure.notes=Tree node is being edited by another user dialog.lock_failure.bookmark=Tree node is being edited by another user dialog.lock_failure.must_exist=Element is candidate for deletion by another user +dialog.lock_failure.no_edge_creation="One or more nodes are candidates for creation by another user" dialog.property_editor.title=Property Editor dialog.property_editor.error.property_null=Properties cannot be null @@ -200,6 +201,7 @@ file.save.text=Save file.save.mnemonic=S file.save.accelerator=ctrl S +file.save_copy.text=Save a copy... file.save_as.text=Save as... #file.save_as.mnemonic=A file.close.text=Close
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/FileService.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/FileService.java Mon Dec 17 18:39:40 2012 +0000 @@ -153,6 +153,7 @@ * @param removeExtension the extension to be removed from the chosen file name. Use {@code null} for removing no extension * @param addExtension the extension to be added to the chosen file name. * @param currentTabs an array of already open files names. If the selected file matches any of these, then an + * Exception is thrown. If {@code null} is passed, then this parameter will be ignored and no check will be done. * @return an {@code FileService.Save} to handle the file selected by the user * @throws IOException if the file chosen by the uses doesn't exist or has a file name that's already * in {@code currentTabs}.
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/GraphPanel.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/GraphPanel.java Mon Dec 17 18:39:40 2012 +0000 @@ -674,14 +674,30 @@ @Override public void edgeCreated(Edge e) { ArrayList<DiagramNode> nodesToConnect = new ArrayList<DiagramNode>(selectedElements.size()); + for(DiagramElement element : selectedElements){ - if(element instanceof Node) + if(element instanceof Node){ + if(!modelUpdater.getLock(element, + Lock.MUST_EXIST, + new DiagramEventActionSource(DiagramEventSource.GRPH, + Command.Name.SELECT_NODE_FOR_EDGE_CREATION, + element.getId(), + element.getName()))){ + /* unlock the nodes locked so far */ + yieldLocks(nodesToConnect); + /* notify user */ + JOptionPane.showMessageDialog(GraphPanel.this, + ResourceBundle.getBundle(EditorFrame.class.getName()).getString("dialog.lock_failure.no_edge_creation")); + return; + } nodesToConnect.add((Node)element); + } } try { e.connect(nodesToConnect); - /* perform the command, no lock is needed for inserting */ modelUpdater.insertInCollection(e,DiagramEventSource.GRPH); + /* release the must-exist lock on nodes now that the edge is created */ + yieldLocks(nodesToConnect); } catch (ConnectNodesException cnEx) { JOptionPane.showMessageDialog(GraphPanel.this, cnEx.getLocalizedMessage(), @@ -690,6 +706,20 @@ iLog("insert edge error",cnEx.getMessage()); } } + + /* release all locks */ + private void yieldLocks(ArrayList<DiagramNode> nodesToConnect){ + for(DiagramNode node : nodesToConnect){ + modelUpdater.yieldLock(node, + Lock.MUST_EXIST, + new DiagramEventActionSource( + DiagramEventSource.GRPH, + Command.Name.UNSELECT_NODE_FOR_EDGE_CREATION, + node.getId(), + node.getName()) + ); + } + } } private List<Edge> edges;
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/Node.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/Node.java Mon Dec 17 18:39:40 2012 +0000 @@ -249,6 +249,7 @@ * * @param doc An XMl document * @param nodeTag the XML {@code PersistenceManager.NODE } tag with data for this node + * @throws IOException if something goes wrong when reading the document. E.g. when the file is corrupted * * @see uk.ac.qmul.eecs.ccmi.gui.persistence */
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/ResourceFactory.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/ResourceFactory.java Mon Dec 17 18:39:40 2012 +0000 @@ -26,22 +26,39 @@ import java.util.ResourceBundle; import javax.swing.JMenu; -import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.KeyStroke; /** - * A factory class for swing components creation support. Components that are created via this - * class are sonyfied via the Narrator + * A factory class for swing components creation support. + * + * Components that are created via this class are, in turn created by {@code SpeechMenuFactory} methods. + * This class handles the labelling and other menu properties (such as accelerators) + * using the {@code ResourceBundle} passed as argument to the constructor. */ class ResourceFactory{ public ResourceFactory(ResourceBundle bundle){ this.bundle = bundle; } - - public JMenuBar createMenuBar(){ - return SpeechMenuFactory.getMenuBar(); + public JMenu createMenu(String prefix){ + String text = bundle.getString(prefix + ".text"); + JMenu menu = SpeechMenuFactory.getMenu(text); + try{ + String mnemonic = bundle.getString(prefix + ".mnemonic"); + menu.setMnemonic(mnemonic.charAt(0)); + }catch (MissingResourceException exception){ + // ok not to set mnemonic + } + + try{ + String tooltip = bundle.getString(prefix + ".tooltip"); + menu.setToolTipText(tooltip); + } + catch (MissingResourceException exception){ + // ok not to set tooltip + } + return menu; } public JMenuItem createMenuItem(String prefix, @@ -58,12 +75,6 @@ return configure(menuItem, prefix, listener); } - public JMenuItem createMenuItemFromLabel(String label, ActionListener listener){ - JMenuItem menuItem = SpeechMenuFactory.getMenuItem(label); - menuItem.addActionListener(listener); - return menuItem; - } - public JMenuItem createCheckBoxMenuItem(String prefix, ActionListener listener){ String text = bundle.getString(prefix + ".text"); @@ -71,7 +82,7 @@ return configure(menuItem, prefix, listener); } - public JMenuItem configure(JMenuItem menuItem, + private JMenuItem configure(JMenuItem menuItem, String prefix, ActionListener listener){ menuItem.addActionListener(listener); try{ @@ -97,26 +108,5 @@ return menuItem; } - public JMenu createMenu(String prefix){ - String text = bundle.getString(prefix + ".text"); - JMenu menu = SpeechMenuFactory.getMenu(text); - try{ - String mnemonic = bundle.getString(prefix + ".mnemonic"); - menu.setMnemonic(mnemonic.charAt(0)); - }catch (MissingResourceException exception){ - // ok not to set mnemonic - } - - try{ - String tooltip = bundle.getString(prefix + ".tooltip"); - menu.setToolTipText(tooltip); - } - catch (MissingResourceException exception) - { - // ok not to set tooltip - } - return menu; - } - private ResourceBundle bundle; }
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechMenuFactory.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechMenuFactory.java Mon Dec 17 18:39:40 2012 +0000 @@ -29,11 +29,13 @@ import javax.swing.AbstractAction; import javax.swing.Action; +import javax.swing.ComponentInputMap; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; +import javax.swing.KeyStroke; import javax.swing.MenuElement; import javax.swing.MenuSelectionManager; @@ -51,15 +53,7 @@ /* implements the singleton pattern and keeps a static reference to the menuBar used by JMenuItems and JMenus */ public static JMenuBar getMenuBar(){ if(menuBar == null){ - menuBar = new JMenuBar(){ - @Override - public void processKeyEvent(KeyEvent e, MenuElement[] path, MenuSelectionManager manager) { - super.processKeyEvent(e,path,manager); - if(e.getKeyCode() == KeyEvent.VK_ESCAPE){ - NarratorFactory.getInstance().speak(resources.getString("menufactory.leaving")); - } - } - }; + menuBar = new SpeechMenuBar(); } return menuBar; } @@ -105,7 +99,26 @@ } }; - private static final String ACCELERATOR = "accelerator"; + private static class SpeechMenuBar extends JMenuBar{ + SpeechMenuBar() { + setInputMap(SpeechMenuBar.WHEN_IN_FOCUSED_WINDOW, new ComponentInputMap(this){ + @Override + public Object get(KeyStroke keyStroke){ + if(keyStroke == null) + return null; + return super.get(keyStroke); + } + }); + } + + @Override + public void processKeyEvent(KeyEvent e, MenuElement[] path, MenuSelectionManager manager) { + super.processKeyEvent(e,path,manager); + if(e.getKeyCode() == KeyEvent.VK_ESCAPE){ + NarratorFactory.getInstance().speak(resources.getString("menufactory.leaving")); + } + } + } /* * this class implements a menu item which speaks out its label when @@ -150,6 +163,8 @@ @Override public void setEnabled(boolean b){ super.setEnabled(b); + if(getAccelerator() == null) + return; if(b == false){ /* if the menu item gets disabled, then set up an action in the menuBar to respond to * the accelerator key stroke, so that the user gets a feedback (error sound) @@ -210,4 +225,5 @@ private static JMenuBar menuBar; private static ResourceBundle resources = ResourceBundle.getBundle(EditorFrame.class.getName()); + private static final String ACCELERATOR = "accelerator"; }
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/persistence/PersistenceManager.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/persistence/PersistenceManager.java Mon Dec 17 18:39:40 2012 +0000 @@ -170,7 +170,10 @@ * @see http://download.oracle.com/javase/6/docs/api/javax/xml/transform/stream/StreamResult.html * * @param diagram the diagram to encode - * @param newName the new name of the diagram to encode. This will also be the name of the file + * @param newName the new name of the diagram to encode. This will also be the name of the file but {@code diagram} + * will still keep the old name, that is the value returned by {@code getName()} won't be changed to {@code newName}. + * If a {@code null} value is passed than the value returned by {@code diagram.getName()} will be used. + * * @param out where the diagram will be encoded * @throws IOException if there are any I/O problems with the file */ @@ -277,8 +280,6 @@ } catch (TransformerException te) { throw new IOException(resources.getString("dialog.error.problem.save"),te); } - if(newName != null) - diagram.setName(newName); } /**
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/FalconHaptics.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/FalconHaptics.java Mon Dec 17 18:39:40 2012 +0000 @@ -37,6 +37,10 @@ class FalconHaptics extends Thread implements Haptics { static Haptics createInstance(HapticListenerThread listener) { + if(OsDetector.has64BitJVM()){// no 64 native library supported yet + return null; + } + if(OsDetector.isWindows()){ /* create a directory for the dll distributed with HAPI library */ String libDir = PreferencesService.getInstance().get("dir.libs", System.getProperty("java.io.tmpdir"));
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/Haptics.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/Haptics.java Mon Dec 17 18:39:40 2012 +0000 @@ -33,6 +33,13 @@ public void switchDiagram(String diagramName); + /** + * Removes a diagram from the collection of haptic diagrams. + * + * @param diagramNameToRemove the unique name of the diagram to remove + * @param diagramNameOfNext the name of the next diagram to render hapticly. If + * {@code null}, no diagram will be rendered. + */ public void removeDiagram(String diagramNameToRemove, String diagramNameOfNext); public void addNode(double x, double y, int nodeHashCode, String diagramName);
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticsFactory.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticsFactory.java Mon Dec 17 18:39:40 2012 +0000 @@ -45,19 +45,27 @@ * a conflict between function names. */ String defaultDevice = PreferencesService.getInstance().get("haptic_device", TABLET_ID); + /* temporary set the preference to default: if the device crashes during init * + * the user will still be able to restart the diagram editor */ + PreferencesService.getInstance().put("haptic_device", TABLET_ID); + if(PHANTOM_ID.equals(defaultDevice)){ /* OmniHaptics first */ hapticsInstance = OmniHaptics.createInstance(listener); - if(hapticsInstance != null)//OmniHaptics instance successfully created: return + if(hapticsInstance != null){//OmniHaptics instance successfully created: return + PreferencesService.getInstance().put("haptic_device", defaultDevice); return; + } }else if(FALCON_ID.equals(defaultDevice)){ /* Falcon first */ hapticsInstance = FalconHaptics.createInstance(listener); if(hapticsInstance != null){ //FalconHaptics instance successfully created: return + PreferencesService.getInstance().put("haptic_device", defaultDevice); return; } } + PreferencesService.getInstance().put("haptic_device", defaultDevice); /* no devices available, stop the listener and go for the default */ if(listener.isAlive()){ listener.interrupt();
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/OmniHaptics.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/OmniHaptics.java Mon Dec 17 18:39:40 2012 +0000 @@ -45,6 +45,10 @@ if(listener == null) throw new IllegalArgumentException("listener cannot be null"); + if(OsDetector.has64BitJVM()){ + return null;// no 64 native library supported yet + } + if(OsDetector.isWindows()){ /* try to load .dll. First copy it in the home/ccmi_editor_data/lib directory */ URL url = OmniHaptics.class.getResource("OmniHaptics.dll");
--- a/java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.java Mon Dec 17 18:39:40 2012 +0000 @@ -30,6 +30,7 @@ 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; @@ -184,21 +185,64 @@ */ @Override public void run() { - editorFrame = new EditorFrame(haptics,templateFiles,backupDirPath,getTemplateEditors()); + editorFrame = new EditorFrame(haptics,getTemplateFiles(),backupDirPath,getTemplateEditors(),getDiagrams()); } - public TemplateEditor[] getTemplateEditors(){ + /** + * 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(); + 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) { @@ -244,7 +288,7 @@ return editorFrame; } - static EditorFrame editorFrame; + private static EditorFrame editorFrame; Haptics haptics; File[] templateFiles; TemplateEditor[] templateCreators;
--- a/java/src/uk/ac/qmul/eecs/ccmi/network/ServerConnectionManager.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/network/ServerConnectionManager.java Mon Dec 17 18:39:40 2012 +0000 @@ -95,7 +95,9 @@ ""+AwarenessMessage.USERNAMES_SEPARATOR+unsc.userName ); for(UserNameSocketChannel userNameSocketChannel : entry.getValue()){ - protocol.send(userNameSocketChannel.channel, awMsg); + /* don't send aw msg to the local channel */ + if(!userNameSocketChannel.channel.equals(localChannel)) + protocol.send(userNameSocketChannel.channel, awMsg); } break; } @@ -188,7 +190,7 @@ /* it's NULL for NOTES lock and SELECT_NODE_FOR_EDGE_CREATION must not clean the text panel, because its record is temporary */ if(released && source != DiagramEventActionSource.NULL && source.getCmd() != Command.Name.SELECT_NODE_FOR_EDGE_CREATION){ - if(source.getCmd() == Command.Name.UNSELECT_NODE_FOR_EDGE_CREATION){ + if(source.getCmd() == Command.Name.UNSELECT_NODE_FOR_EDGE_CREATION && broadcastFilter.accept(source)){ /* unselect node for edge creation is treated differently because it doesn't * * clear the text panel but adds another record, which is temporary */ handleAwarenessMessage(new AwarenessMessage(
--- a/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SimpleShapeNode.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SimpleShapeNode.java Mon Dec 17 18:39:40 2012 +0000 @@ -451,6 +451,13 @@ private static final Rectangle2D.Double minBounds = new Rectangle2D.Double(0,0,DEFAULT_WIDTH,DEFAULT_HEIGHT); private static final int PROP_NODE_DIST = 50; + /** + * When properties are configure to appear outside, the values are represented as small nodes + * connected (with a straight line) to the node they belong to. This class represents such + * small nodes. The possible shapes are: triangle, rectangle, square, circle, ellipse and no shape in + * which case only the string with the property value and the line connecting it to the node is shown. + * + */ protected static class PropertyNode{ public PropertyNode(ShapeType aShape){ /* add a little padding in the shape holding the label */
--- a/java/src/uk/ac/qmul/eecs/ccmi/sound/BeadsSound.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/sound/BeadsSound.java Mon Dec 17 18:39:40 2012 +0000 @@ -20,7 +20,7 @@ package uk.ac.qmul.eecs.ccmi.sound; import java.io.InputStream; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; import net.beadsproject.beads.core.AudioContext; @@ -34,14 +34,14 @@ /** * The Sound interface implementation using the Beads library. - * For more info abou the library see http://www.beadsproject.net/. + * For more info about the library see http://www.beadsproject.net/. */ class BeadsSound implements Sound { public BeadsSound(){ ac = new AudioContext(); - playerListeners = new HashMap<SoundEvent,PlayerListener>(); - loopPlayers = new HashMap<SoundEvent,UGen>(); + playerListeners = new EnumMap<SoundEvent,PlayerListener>(SoundEvent.class); + loopPlayers = new EnumMap<SoundEvent,UGen>(SoundEvent.class); /* pre load all the sample to avoid future overhead */ for(SoundEvent key : AudioResourcesService.eventTypes()){
--- a/java/src/uk/ac/qmul/eecs/ccmi/sound/PlayerListener.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/sound/PlayerListener.java Mon Dec 17 18:39:40 2012 +0000 @@ -22,9 +22,12 @@ /** * {@code PlayerListeners} can be registered to an object implementing the * {@code Sound} interface. Each time a sound is played registered listeners - * will be triggered just after the sound is over. + * will be triggered just after the sound is played. * */ public interface PlayerListener { + /** + * Called when the sound is played + */ void playEnded(); }
--- a/java/src/uk/ac/qmul/eecs/ccmi/speech/NativeNarrator.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/speech/NativeNarrator.java Mon Dec 17 18:39:40 2012 +0000 @@ -37,12 +37,13 @@ static { nativeLibraryNotFound = true; if(OsDetector.isWindows()){ - URL url = NativeNarrator.class.getResource("WinNarrator.dll"); + String res = OsDetector.has64BitJVM() ? "WinNarrator64.dll" : "WinNarrator.dll" ; + URL url = NativeNarrator.class.getResource(res); if(url != null){ ResourceFileWriter fileWriter = new ResourceFileWriter(url); fileWriter.writeOnDisk( PreferencesService.getInstance().get("dir.libs", System.getProperty("java.io.tmpdir")), - "CCmIWinNarrator.dll"); + OsDetector.has64BitJVM() ? "CCmIWinNarrator64.dll" : "CCmIWinNarrator.dll"); String path = fileWriter.getFilePath(); if(path != null) try{
--- a/java/src/uk/ac/qmul/eecs/ccmi/utils/OsDetector.java Tue Jul 10 22:39:37 2012 +0100 +++ b/java/src/uk/ac/qmul/eecs/ccmi/utils/OsDetector.java Mon Dec 17 18:39:40 2012 +0000 @@ -38,5 +38,10 @@ return(OS.indexOf( "nix") >=0 || OS.indexOf( "nux") >=0); } + public static boolean has64BitJVM(){ + String dataModel = System.getProperty("sun.arch.data.model"); + return dataModel.equals("64"); + } + static final String OS = System.getProperty("os.name").toLowerCase(); }