annotate java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.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
rev   line source
f@0 1 /*
f@0 2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
f@0 3
f@0 4 Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com)
f@0 5 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
f@0 6
f@0 7 This program is free software: you can redistribute it and/or modify
f@0 8 it under the terms of the GNU General Public License as published by
f@0 9 the Free Software Foundation, either version 3 of the License, or
f@0 10 (at your option) any later version.
f@0 11
f@0 12 This program is distributed in the hope that it will be useful,
f@0 13 but WITHOUT ANY WARRANTY; without even the implied warranty of
f@0 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f@0 15 GNU General Public License for more details.
f@0 16
f@0 17 You should have received a copy of the GNU General Public License
f@0 18 along with this program. If not, see <http://www.gnu.org/licenses/>.
f@0 19 */
f@0 20
f@0 21 package uk.ac.qmul.eecs.ccmi.gui;
f@0 22
f@0 23 import java.awt.Color;
f@0 24 import java.awt.Dimension;
f@0 25 import java.awt.Graphics2D;
f@0 26 import java.awt.KeyboardFocusManager;
f@0 27 import java.awt.Toolkit;
f@0 28 import java.awt.event.ActionEvent;
f@0 29 import java.awt.event.ActionListener;
f@0 30 import java.awt.event.WindowAdapter;
f@0 31 import java.awt.event.WindowEvent;
f@0 32 import java.awt.event.WindowFocusListener;
f@0 33 import java.awt.geom.Rectangle2D;
f@0 34 import java.awt.image.BufferedImage;
f@0 35 import java.io.BufferedInputStream;
f@0 36 import java.io.BufferedOutputStream;
f@0 37 import java.io.BufferedReader;
f@0 38 import java.io.ByteArrayInputStream;
f@0 39 import java.io.File;
f@0 40 import java.io.FileOutputStream;
f@0 41 import java.io.IOException;
f@0 42 import java.io.InputStream;
f@0 43 import java.io.InputStreamReader;
f@0 44 import java.io.OutputStream;
f@0 45 import java.net.InetSocketAddress;
f@0 46 import java.net.URL;
f@0 47 import java.nio.channels.SocketChannel;
f@0 48 import java.text.MessageFormat;
f@0 49 import java.text.SimpleDateFormat;
f@0 50 import java.util.ArrayList;
f@0 51 import java.util.Arrays;
f@0 52 import java.util.Date;
f@0 53 import java.util.ResourceBundle;
f@0 54 import java.util.Set;
f@0 55 import java.util.concurrent.ExecutionException;
f@0 56 import java.util.regex.Matcher;
f@0 57 import java.util.regex.Pattern;
f@0 58
f@0 59 import javax.imageio.ImageIO;
f@0 60 import javax.swing.ImageIcon;
f@0 61 import javax.swing.JCheckBoxMenuItem;
f@0 62 import javax.swing.JFrame;
f@0 63 import javax.swing.JMenu;
f@0 64 import javax.swing.JMenuBar;
f@0 65 import javax.swing.JMenuItem;
f@0 66 import javax.swing.JTree;
f@0 67 import javax.swing.SwingUtilities;
f@0 68 import javax.swing.UIManager;
f@0 69 import javax.swing.UnsupportedLookAndFeelException;
f@0 70 import javax.swing.event.ChangeEvent;
f@0 71 import javax.swing.event.ChangeListener;
f@0 72 import javax.swing.event.MenuEvent;
f@0 73 import javax.swing.event.MenuListener;
f@0 74 import javax.swing.event.TreeSelectionEvent;
f@0 75 import javax.swing.event.TreeSelectionListener;
f@0 76 import javax.swing.tree.TreePath;
f@0 77
f@0 78 import uk.ac.qmul.eecs.ccmi.diagrammodel.ConnectNodesException;
f@0 79 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement;
f@0 80 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramTreeNode;
f@0 81 import uk.ac.qmul.eecs.ccmi.diagrammodel.EdgeReferenceMutableTreeNode;
f@0 82 import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties.Modifiers;
f@0 83 import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeReferenceMutableTreeNode;
f@0 84 import uk.ac.qmul.eecs.ccmi.diagrammodel.PropertyMutableTreeNode;
f@0 85 import uk.ac.qmul.eecs.ccmi.diagrammodel.PropertyTypeMutableTreeNode;
f@0 86 import uk.ac.qmul.eecs.ccmi.diagrammodel.TypeMutableTreeNode;
f@0 87 import uk.ac.qmul.eecs.ccmi.gui.awareness.BroadcastFilter;
f@0 88 import uk.ac.qmul.eecs.ccmi.gui.awareness.DisplayFilter;
f@0 89 import uk.ac.qmul.eecs.ccmi.gui.persistence.PersistenceManager;
f@0 90 import uk.ac.qmul.eecs.ccmi.haptics.Haptics;
f@0 91 import uk.ac.qmul.eecs.ccmi.haptics.HapticsFactory;
f@0 92 import uk.ac.qmul.eecs.ccmi.network.AwarenessMessage;
f@0 93 import uk.ac.qmul.eecs.ccmi.network.ClientConnectionManager;
f@0 94 import uk.ac.qmul.eecs.ccmi.network.ClientConnectionManager.RmDiagramRequest;
f@0 95 import uk.ac.qmul.eecs.ccmi.network.ClientConnectionManager.SendAwarenessRequest;
f@0 96 import uk.ac.qmul.eecs.ccmi.network.Command;
f@0 97 import uk.ac.qmul.eecs.ccmi.network.DiagramDownloader;
f@0 98 import uk.ac.qmul.eecs.ccmi.network.DiagramEventActionSource;
f@0 99 import uk.ac.qmul.eecs.ccmi.network.DiagramShareException;
f@0 100 import uk.ac.qmul.eecs.ccmi.network.NetDiagram;
f@0 101 import uk.ac.qmul.eecs.ccmi.network.ProtocolFactory;
f@0 102 import uk.ac.qmul.eecs.ccmi.network.Server;
f@0 103 import uk.ac.qmul.eecs.ccmi.pdsupport.PdDiagram;
f@0 104 import uk.ac.qmul.eecs.ccmi.pdsupport.PdPersistenceManager;
f@0 105 import uk.ac.qmul.eecs.ccmi.sound.PlayerListener;
f@0 106 import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
f@0 107 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
f@0 108 import uk.ac.qmul.eecs.ccmi.speech.Narrator;
f@0 109 import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
f@0 110 import uk.ac.qmul.eecs.ccmi.utils.ExceptionHandler;
f@0 111 import uk.ac.qmul.eecs.ccmi.utils.InteractionLog;
f@0 112 import uk.ac.qmul.eecs.ccmi.utils.PreferencesService;
f@0 113
f@0 114 /**
f@0 115 * The main frame of the editor which contains diagram panes that show graphs and
f@0 116 * tree representations of diagrams.
f@0 117 */
f@0 118 @SuppressWarnings("serial")
f@0 119 public class EditorFrame extends JFrame {
f@0 120 /**
f@0 121 * Creates a new {@code EditorFrame}
f@0 122 *
f@0 123 * @param haptics an instance of {@code Haptics} handling the haptic device during this
f@0 124 * @param templateFiles an array of template files. New diagrams can be created from template files by clonation
f@0 125 * @param backupDirPath the path of a folder where all the currently open diagrams will be saved if
f@0 126 * the haptic device crashes
f@0 127 * @param templateEditors the template editors for this instance of the program
f@0 128 * @param additionalTemplate additional diagram templates. An entry will be created in {@code File->New Diagram}
f@0 129 * for each template
f@0 130 */
f@0 131 public EditorFrame(Haptics haptics, File[] templateFiles, String backupDirPath, TemplateEditor[] templateEditors, Diagram[] additionalTemplate){
f@0 132 this.backupDirPath = backupDirPath;
f@0 133 /* load resources */
f@0 134 resources = ResourceBundle.getBundle(this.getClass().getName());
f@0 135
f@0 136 /* haptics */
f@0 137 this.haptics = haptics;
f@0 138 hapticTrigger = new HapticTrigger();
f@0 139
f@0 140 /* read editor related preferences */
f@0 141 preferences = PreferencesService.getInstance();
f@0 142
f@0 143 URL url = getClass().getResource("ccmi_favicon.gif");
f@0 144 setIconImage(new ImageIcon(url).getImage());
f@0 145 changeLookAndFeel(preferences.get("laf", null));
f@0 146
f@0 147 recentFiles = new ArrayList<String>();
f@0 148 File lastDir = new File(".");
f@0 149 String recent = preferences.get("recent", "").trim();
f@0 150 if (recent.length() > 0){
f@0 151 recentFiles.addAll(Arrays.asList(recent.split("[|]")));
f@0 152 lastDir = new File(recentFiles.get(0)).getParentFile();
f@0 153 }
f@0 154 fileService = new FileService.ChooserService(lastDir);
f@0 155
f@0 156 /* set up extensions */
f@0 157 defaultExtension = resources.getString("files.extension");
f@0 158 extensionFilter = new ExtensionFilter(
f@0 159 resources.getString("files.name"),
f@0 160 new String[] { defaultExtension });
f@0 161 exportFilter = new ExtensionFilter(
f@0 162 resources.getString("files.image.name"),
f@0 163 resources.getString("files.image.extension"));
f@0 164
f@0 165 /* start building the GUI */
f@0 166 editorTabbedPane = new EditorTabbedPane(this);
f@0 167 setContentPane(editorTabbedPane);
f@0 168
f@0 169
f@0 170 setTitle(resources.getString("app.name"));
f@0 171 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
f@0 172
f@0 173 int screenWidth = (int)screenSize.getWidth();
f@0 174 int screenHeight = (int)screenSize.getHeight();
f@0 175
f@0 176 setLocation(screenWidth / 16, screenHeight / 16);
f@0 177 editorTabbedPane.setPreferredSize(new Dimension(
f@0 178 screenWidth * 5 / 8, screenHeight * 5 / 8));
f@0 179
f@0 180 /* install the player listener (a narrator speech) for CANCEL, EMPTY and MESSAGE ok event. They are *
f@0 181 * not depending on what the user is doing and the speech is therefore always the same. We only need *
f@0 182 * to set the listener once and it will do the job each time the sound is triggered */
f@0 183 SoundFactory.getInstance().setDefaultPlayerListener(new PlayerListener(){
f@0 184 @Override
f@0 185 public void playEnded() {
f@0 186 DiagramPanel dPanel = getActiveTab();
f@0 187 if(dPanel != null){// we can cancel a dialog even when no diagram is open (e.g. for tcp connections)
f@0 188 speakFocusedComponent("");
f@0 189 }else{
f@0 190 NarratorFactory.getInstance().speak(MessageFormat.format(resources.getString("speech.cancelled"),""));
f@0 191 }
f@0 192 }
f@0 193 }, SoundEvent.CANCEL);
f@0 194 SoundFactory.getInstance().setDefaultPlayerListener(new PlayerListener(){
f@0 195 @Override
f@0 196 public void playEnded() {
f@0 197 DiagramPanel dPanel = getActiveTab();
f@0 198 DiagramTree tree = dPanel.getTree();
f@0 199 NarratorFactory.getInstance().speak(MessageFormat.format(resources.getString("speech.empty_property"),tree.currentPathSpeech()));
f@0 200 }
f@0 201 }, SoundEvent.EMPTY);
f@0 202 SoundFactory.getInstance().setDefaultPlayerListener(new PlayerListener(){
f@0 203 @Override
f@0 204 public void playEnded() {
f@0 205 DiagramPanel dPanel = getActiveTab();
f@0 206 if(dPanel != null){
f@0 207 DiagramTree tree = dPanel.getTree();
f@0 208 NarratorFactory.getInstance().speak(tree.currentPathSpeech());
f@0 209 }
f@0 210 }
f@0 211 }, SoundEvent.MESSAGE_OK);
f@0 212
f@0 213 /* setup listeners */
f@0 214 initListeners();
f@0 215 /* set up menus */
f@0 216 existingTemplateNames = new ArrayList<String>(10);
f@0 217 existingTemplates = new ArrayList<Diagram>(10);
f@0 218 int extensionLength = resources.getString("template.extension").length();
f@0 219 for(File file : templateFiles){
f@0 220 existingTemplateNames.add(file.getName().substring(0, file.getName().length()-extensionLength));
f@0 221 }
f@0 222 initMenu(templateEditors);
f@0 223 /* read template files. this call must be placed after menu creation as it adds menu items to file->new-> */
f@0 224 boolean someTemplateFilesNotRead = readTemplateFiles(templateFiles);
f@0 225
f@0 226 for(Diagram additionalDiagram : additionalTemplate){
f@0 227 addDiagramType(additionalDiagram);
f@0 228 }
f@0 229
f@0 230 /* become visible */
f@0 231 pack();
f@0 232 setVisible(true);
f@0 233 /* if some templates were not read successfully, warn the user with a message */
f@0 234 if(someTemplateFilesNotRead){
f@0 235 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.error.filesnotread"), SpeechOptionPane.WARNING_MESSAGE);
f@0 236 }
f@0 237 }
f@0 238
f@0 239 private void initListeners(){
f@0 240 /* window closing */
f@0 241 setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
f@0 242 addWindowListener(new WindowAdapter(){
f@0 243 @Override
f@0 244 public void windowClosing(WindowEvent event){
f@0 245 exit();
f@0 246 }
f@0 247 @Override
f@0 248 public void windowOpened(WindowEvent e) {
f@0 249 // bring the window to front, else the openGL window would have higher priority
f@0 250 e.getWindow().toFront();
f@0 251 }
f@0 252 });
f@0 253
f@0 254 addWindowFocusListener(new WindowFocusListener(){
f@0 255 @Override
f@0 256 public void windowGainedFocus(WindowEvent evt) {
f@0 257 if(evt.getOppositeWindow() == null)
f@0 258 NarratorFactory.getInstance().speak(resources.getString("window.focus"));
f@0 259 }
f@0 260
f@0 261 @Override
f@0 262 public void windowLostFocus(WindowEvent evt) {
f@0 263 if(evt.getOppositeWindow() == null)
f@0 264 NarratorFactory.getInstance().speak(resources.getString("window.unfocus"));
f@0 265 }
f@0 266 });
f@0 267
f@0 268 /* set up listeners reacting to change of selection in the tree and tab */
f@0 269 tabChangeListener = new ChangeListener(){
f@0 270 @Override
f@0 271 public void stateChanged(ChangeEvent evt) {
f@0 272 DiagramPanel diagramPanel = getActiveTab();
f@0 273 if (diagramPanel != null){
f@0 274 /* give the focus to the Content Pane, otherwise it's grabbed by the rootPane */
f@0 275 getContentPane().requestFocusInWindow();
f@0 276 TreePath path = diagramPanel.getTree().getSelectionPath();
f@0 277 treeEnabledMenuUpdate(path);
f@0 278 NarratorFactory.getInstance().speak(MessageFormat.format(resources.getString("window.tab"),editorTabbedPane.getTitleAt(editorTabbedPane.getSelectedIndex())));
f@0 279
f@0 280 // updated the haptics
f@0 281 HapticsFactory.getInstance().switchDiagram(diagramPanel.getDiagram().getName());
f@0 282 iLog("diagram tab changed to "+editorTabbedPane.getTitleAt(editorTabbedPane.getSelectedIndex()));
f@0 283 }else{
f@0 284 treeEnabledMenuUpdate(null);
f@0 285 }
f@0 286 /* if we change tab, the haptic highlight must be set again */
f@0 287 selectHapticHighligh(null);
f@0 288 /* so do the menu depending on the panel */
f@0 289 diagramPanelEnabledMenuUpdate(diagramPanel);
f@0 290 }
f@0 291 };
f@0 292 editorTabbedPane.addChangeListener(tabChangeListener);
f@0 293
f@0 294 treeSelectionListener = new TreeSelectionListener(){
f@0 295 @Override
f@0 296 public void valueChanged(TreeSelectionEvent evt) {
f@0 297 treeEnabledMenuUpdate(evt.getPath());
f@0 298 }
f@0 299 };
f@0 300
f@0 301 netLocalDiagramExceptionHandler = new ExceptionHandler(){
f@0 302 @Override
f@0 303 public void handleException(Exception e) {
f@0 304 SwingUtilities.invokeLater(new Runnable(){
f@0 305 @Override
f@0 306 public void run() {
f@0 307 SpeechOptionPane.showMessageDialog(EditorFrame.this, resources.getString("dialog.error.local_server"));
f@0 308 }
f@0 309 });
f@0 310 }
f@0 311 };
f@0 312 }
f@0 313
f@0 314 private void initMenu(TemplateEditor[] templateEditors){
f@0 315 ResourceFactory factory = new ResourceFactory(resources);
f@0 316
f@0 317 JMenuBar menuBar = SpeechMenuFactory.getMenuBar();
f@0 318 setJMenuBar(menuBar);
f@0 319
f@0 320 /* --- FILE MENU --- */
f@0 321 JMenu fileMenu = factory.createMenu("file");
f@0 322 menuBar.add(fileMenu);
f@0 323
f@0 324 /* menu items and listener added by addDiagramType function */
f@0 325 newMenu = factory.createMenu("file.new");
f@0 326 fileMenu.add(newMenu);
f@0 327
f@0 328 JMenuItem fileOpenItem = factory.createMenuItem(
f@0 329 "file.open", this, "openFile");
f@0 330 fileMenu.add(fileOpenItem);
f@0 331
f@0 332 recentFilesMenu = factory.createMenu("file.recent");
f@0 333 buildRecentFilesMenu();
f@0 334 fileMenu.add(recentFilesMenu);
f@0 335
f@0 336 fileSaveItem = factory.createMenuItem("file.save", this, "saveFile");
f@0 337 fileMenu.add(fileSaveItem);
f@0 338
f@0 339 fileSaveAsItem = factory.createMenuItem("file.save_as", this, "saveFileAs");
f@0 340 fileMenu.add(fileSaveAsItem);
f@0 341
f@0 342 fileSaveCopyItem = factory.createMenuItem("file.save_copy", this, "saveCopy");
f@0 343 fileMenu.add(fileSaveCopyItem);
f@0 344
f@0 345 fileCloseItem = factory.createMenuItem("file.close",this,"closeFile");
f@0 346 fileMenu.add(fileCloseItem);
f@0 347
f@0 348 graphExportItem = factory.createMenuItem("file.export_image", this, "exportImage");
f@0 349 fileMenu.add(graphExportItem);
f@0 350
f@0 351 JMenuItem fileExitItem = factory.createMenuItem(
f@0 352 "file.exit", this, "exit");
f@0 353 fileMenu.add(fileExitItem);
f@0 354
f@0 355 /* --- EDIT MENU --- */
f@0 356 JMenu editMenu = factory.createMenu("edit");
f@0 357 menuBar.add(editMenu);
f@0 358
f@0 359 locateMenuItem = factory.createMenuItem("edit.locate", this,"locate");
f@0 360 editMenu.add(locateMenuItem);
f@0 361
f@0 362 highlightMenuItem = factory.createMenuItem("edit.highlight", this, "hHighlight");
f@0 363 highlightMenuItem.setEnabled(false);
f@0 364 editMenu.add(highlightMenuItem);
f@0 365
f@0 366 jumpMenuItem = factory.createMenuItem("edit.jump",this,"jump");
f@0 367 editMenu.add(jumpMenuItem);
f@0 368
f@0 369 insertMenuItem = factory.createMenuItem("edit.insert", this, "insert");
f@0 370 editMenu.add(insertMenuItem);
f@0 371
f@0 372 deleteMenuItem = factory.createMenuItem("edit.delete",this,"delete");
f@0 373 editMenu.add(deleteMenuItem);
f@0 374
f@0 375 renameMenuItem = factory.createMenuItem("edit.rename",this,"rename");
f@0 376 editMenu.add(renameMenuItem);
f@0 377
f@0 378 selectMenuItem = factory.createMenuItem("edit.select", this, "selectNode");
f@0 379 editMenu.add(selectMenuItem);
f@0 380
f@0 381 bookmarkMenuItem = factory.createMenuItem("edit.bookmark",this,"editBookmarks");
f@0 382 editMenu.add(bookmarkMenuItem);
f@0 383
f@0 384 editNotesMenuItem = factory.createMenuItem("edit.edit_note",this,"editNotes");
f@0 385 editMenu.add(editNotesMenuItem);
f@0 386
f@0 387 /* --- VIEW MENU --- */
f@0 388 JMenu viewMenu = factory.createMenu("view");
f@0 389 menuBar.add(viewMenu);
f@0 390
f@0 391 viewMenu.add(factory.createMenuItem(
f@0 392 "view.zoom_out", new
f@0 393 ActionListener(){
f@0 394 public void actionPerformed(ActionEvent event){
f@0 395 DiagramPanel dPanel = getActiveTab();
f@0 396 if (dPanel == null)
f@0 397 return;
f@0 398 dPanel.getGraphPanel().changeZoom(-1);
f@0 399 }
f@0 400 }));
f@0 401
f@0 402 viewMenu.add(factory.createMenuItem(
f@0 403 "view.zoom_in", new
f@0 404 ActionListener(){
f@0 405 public void actionPerformed(ActionEvent event){
f@0 406 DiagramPanel dPanel = getActiveTab();
f@0 407 if (dPanel == null)
f@0 408 return;
f@0 409 dPanel.getGraphPanel().changeZoom(1);
f@0 410 }
f@0 411 }));
f@0 412
f@0 413 viewMenu.add(factory.createMenuItem(
f@0 414 "view.grow_drawing_area", new
f@0 415 ActionListener(){
f@0 416 public void actionPerformed(ActionEvent event){
f@0 417 DiagramPanel dPanel = getActiveTab();
f@0 418 if (dPanel == null)
f@0 419 return;
f@0 420 GraphPanel gPanel = dPanel.getGraphPanel();
f@0 421 Rectangle2D bounds = gPanel.getGraphBounds();
f@0 422 bounds.add(gPanel.getBounds());
f@0 423 gPanel.setMinBounds(new Rectangle2D.Double(0, 0,
f@0 424 GROW_SCALE_FACTOR * bounds.getWidth(),
f@0 425 GROW_SCALE_FACTOR * bounds.getHeight()));
f@0 426 gPanel.revalidate();
f@0 427 gPanel.repaint();
f@0 428 }
f@0 429 }));
f@0 430
f@0 431 viewMenu.add(factory.createMenuItem(
f@0 432 "view.clip_drawing_area", new
f@0 433 ActionListener(){
f@0 434 public void actionPerformed(ActionEvent event){
f@0 435 DiagramPanel dPanel = getActiveTab();
f@0 436 if (dPanel == null)
f@0 437 return;
f@0 438 GraphPanel gPanel = dPanel.getGraphPanel();
f@0 439 gPanel.setMinBounds(null);
f@0 440 gPanel.revalidate();
f@0 441 gPanel.repaint();
f@0 442 }
f@0 443 }));
f@0 444
f@0 445 viewMenu.add(factory.createMenuItem(
f@0 446 "view.smaller_grid", new
f@0 447 ActionListener()
f@0 448 {
f@0 449 public void actionPerformed(ActionEvent event)
f@0 450 {
f@0 451 DiagramPanel dPanel = getActiveTab();
f@0 452 if (dPanel == null)
f@0 453 return;
f@0 454 dPanel.getGraphPanel().changeGridSize(-1);
f@0 455 }
f@0 456 }));
f@0 457
f@0 458 viewMenu.add(factory.createMenuItem(
f@0 459 "view.larger_grid", new
f@0 460 ActionListener(){
f@0 461 public void actionPerformed(ActionEvent event){
f@0 462 DiagramPanel dPanel = getActiveTab();
f@0 463 if (dPanel == null)
f@0 464 return;
f@0 465 dPanel.getGraphPanel().changeGridSize(1);
f@0 466 }
f@0 467 }));
f@0 468
f@0 469 final JCheckBoxMenuItem hideGridItem;
f@0 470 viewMenu.add(hideGridItem = (JCheckBoxMenuItem) factory.createCheckBoxMenuItem(
f@0 471 "view.hide_grid", new
f@0 472 ActionListener()
f@0 473 {
f@0 474 public void actionPerformed(ActionEvent event)
f@0 475 {
f@0 476 DiagramPanel dPanel = getActiveTab();
f@0 477 if (dPanel == null)
f@0 478 return;
f@0 479 JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) event.getSource();
f@0 480 dPanel.getGraphPanel().setHideGrid(menuItem.isSelected());
f@0 481 }
f@0 482 }));
f@0 483
f@0 484 viewMenu.addMenuListener(new
f@0 485 MenuListener(){
f@0 486 /* changes the checkbox according to the diagram selected */
f@0 487 public void menuSelected(MenuEvent event){
f@0 488 DiagramPanel dPanel = getActiveTab();
f@0 489 if (dPanel == null)
f@0 490 return;
f@0 491 hideGridItem.setSelected(dPanel.getGraphPanel().getHideGrid());
f@0 492 }
f@0 493 public void menuDeselected(MenuEvent event){}
f@0 494 public void menuCanceled(MenuEvent event){}
f@0 495 });
f@0 496
f@0 497 JMenu lafMenu = factory.createMenu("view.change_laf");
f@0 498 viewMenu.add(lafMenu);
f@0 499
f@0 500 UIManager.LookAndFeelInfo[] infos =
f@0 501 UIManager.getInstalledLookAndFeels();
f@0 502 for (int i = 0; i < infos.length; i++){
f@0 503 final UIManager.LookAndFeelInfo info = infos[i];
f@0 504 JMenuItem item = SpeechMenuFactory.getMenuItem(info.getName());
f@0 505 lafMenu.add(item);
f@0 506 item.addActionListener(new
f@0 507 ActionListener(){
f@0 508 public void actionPerformed(ActionEvent event){
f@0 509 String laf = info.getClassName();
f@0 510 changeLookAndFeel(laf);
f@0 511 preferences.put("laf", laf);
f@0 512 }
f@0 513 });
f@0 514 }
f@0 515
f@0 516 /* --- TEMPLATE --- */
f@0 517 if(templateEditors.length > 0){
f@0 518 JMenu templateMenu = factory.createMenu("template");
f@0 519 menuBar.add(templateMenu);
f@0 520
f@0 521 for(final TemplateEditor templateEditor : templateEditors){
f@0 522 JMenuItem newDiagramItem = SpeechMenuFactory.getMenuItem(templateEditor.getLabelForNew());
f@0 523 newDiagramItem.addActionListener(new ActionListener(){
f@0 524 @Override
f@0 525 public void actionPerformed(ActionEvent evt) {
f@0 526 Diagram diagram = templateEditor.createNew(EditorFrame.this, existingTemplateNames);
f@0 527 if(diagram == null)
f@0 528 return;
f@0 529 try{
f@0 530 saveDiagramTemplate(diagram);
f@0 531 }catch(IOException ioe){
f@0 532 SpeechOptionPane.showMessageDialog(
f@0 533 EditorFrame.this,
f@0 534 resources.getString("dialog.error.save_template"));
f@0 535 return;
f@0 536 }
f@0 537 addDiagramType(diagram);
f@0 538 SpeechOptionPane.showMessageDialog(
f@0 539 EditorFrame.this,
f@0 540 MessageFormat.format(resources.getString("dialog.template_created"), diagram.getName()),
f@0 541 SpeechOptionPane.INFORMATION_MESSAGE);
f@0 542 SoundFactory.getInstance().play(SoundEvent.OK,null);
f@0 543 }
f@0 544 });
f@0 545 templateMenu.add(newDiagramItem);
f@0 546
f@0 547 JMenuItem editDiagramItem = SpeechMenuFactory.getMenuItem(templateEditor.getLabelForEdit());
f@0 548 editDiagramItem.addActionListener(new ActionListener(){
f@0 549 @Override
f@0 550 public void actionPerformed(ActionEvent evt){
f@0 551 if(existingTemplates.isEmpty()){
f@0 552 NarratorFactory.getInstance().speak(resources.getString("dialog.error.no_template_to_edit"));
f@0 553 return;
f@0 554 }
f@0 555 Diagram[] diagrams = new Diagram[existingTemplates.size()];
f@0 556 diagrams = existingTemplates.toArray(diagrams);
f@0 557 Diagram selectedDiagram = (Diagram)SpeechOptionPane.showSelectionDialog(
f@0 558 EditorFrame.this,
f@0 559 resources.getString("dialog.input.edit_diagram_template"),
f@0 560 diagrams,
f@0 561 diagrams[0]);
f@0 562
f@0 563 if(selectedDiagram == null){
f@0 564 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 565 return;
f@0 566 }
f@0 567
f@0 568 Diagram diagram = templateEditor.edit(EditorFrame.this, existingTemplateNames,selectedDiagram);
f@0 569 if(diagram == null)
f@0 570 return;
f@0 571 try{
f@0 572 saveDiagramTemplate(diagram);
f@0 573 }catch(IOException ioe){
f@0 574 SpeechOptionPane.showMessageDialog(
f@0 575 EditorFrame.this,
f@0 576 resources.getString("dialog.error.save_template"));
f@0 577 return;
f@0 578 }
f@0 579 addDiagramType(diagram);
f@0 580 SpeechOptionPane.showMessageDialog(
f@0 581 EditorFrame.this,
f@0 582 MessageFormat.format(resources.getString("dialog.template_created"), diagram.getName()),
f@0 583 SpeechOptionPane.INFORMATION_MESSAGE);
f@0 584 SoundFactory.getInstance().play(SoundEvent.OK,null);
f@0 585 }
f@0 586 }
f@0 587 );
f@0 588 templateMenu.add(editDiagramItem);
f@0 589 }
f@0 590 }
f@0 591
f@0 592 /* --- COLLABORATION ---- */
f@0 593 JMenu collabMenu = factory.createMenu("collab");
f@0 594 menuBar.add(collabMenu);
f@0 595
f@0 596 startServerMenuItem = factory.createMenuItem("collab.start_server", this, "startServer");
f@0 597 collabMenu.add(startServerMenuItem);
f@0 598
f@0 599 stopServerMenuItem = factory.createMenuItem("collab.stop_server", this, "stopServer");
f@0 600 collabMenu.add(stopServerMenuItem);
f@0 601 stopServerMenuItem.setEnabled(false);
f@0 602
f@0 603 shareDiagramMenuItem = factory.createMenuItem("collab.share_diagram", this, "shareDiagram");
f@0 604 collabMenu.add(shareDiagramMenuItem);
f@0 605
f@0 606 collabMenu.add(factory.createMenuItem("collab.open_shared_diagram", this, "openSharedDiagram"));
f@0 607
f@0 608 showAwarenessPanelMenuItem = factory.createMenuItem("collab.show_awareness_panel", this, "showAwarenessPanel");
f@0 609 collabMenu.add(showAwarenessPanelMenuItem);
f@0 610
f@0 611 hideAwarenessPanelMenuItem = factory.createMenuItem("collab.hide_awareness_panel", this, "hideAwarenessPanel");
f@0 612 collabMenu.add(hideAwarenessPanelMenuItem);
f@0 613
f@0 614 awarenessPanelListener = new AwarenessPanelEnablingListener(){
f@0 615 @Override
f@0 616 public void awarenessPanelEnabled(boolean enabled) {
f@0 617 if(enabled){
f@0 618 showAwarenessPanelMenuItem.setEnabled(true);
f@0 619 hideAwarenessPanelMenuItem.setEnabled(false);
f@0 620 }else{
f@0 621 showAwarenessPanelMenuItem.setEnabled(false);
f@0 622 hideAwarenessPanelMenuItem.setEnabled(false);
f@0 623 }
f@0 624 }
f@0 625
f@0 626 @Override
f@0 627 public void awarenessPanelVisible(boolean visible) {
f@0 628 if(visible){
f@0 629 showAwarenessPanelMenuItem.setEnabled(false);
f@0 630 hideAwarenessPanelMenuItem.setEnabled(true);
f@0 631 }else{
f@0 632 showAwarenessPanelMenuItem.setEnabled(true);
f@0 633 hideAwarenessPanelMenuItem.setEnabled(false);
f@0 634 }
f@0 635 }
f@0 636 };
f@0 637
f@0 638 /* --- PREFERENCES --- */
f@0 639 JMenu preferencesMenu = factory.createMenu("preferences");
f@0 640 menuBar.add(preferencesMenu);
f@0 641
f@0 642 /* show haptic window menu item only unless it's a actual haptic device thread *
f@0 643 * that has its own window run by a native dll */
f@0 644 if(!HapticsFactory.getInstance().isAlive()){
f@0 645 preferencesMenu.add(factory.createCheckBoxMenuItem("preferences.show_haptics", new ActionListener(){
f@0 646 @Override
f@0 647 public void actionPerformed(ActionEvent evt){
f@0 648 JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) evt.getSource();
f@0 649 haptics.setVisible(menuItem.isSelected());
f@0 650 NarratorFactory.getInstance().speakWholeText(resources.getString(
f@0 651 menuItem.isSelected() ? "speech.haptic_window_open" : "speech.haptic_window_close"));
f@0 652 }
f@0 653 }));
f@0 654 }
f@0 655
f@0 656 preferencesMenu.add(factory.createMenuItem("preferences.change_haptics", new ActionListener(){
f@0 657 @Override
f@0 658 public void actionPerformed(ActionEvent evt){
f@0 659 String [] hapticDevices = {
f@0 660 HapticsFactory.PHANTOM_ID,
f@0 661 HapticsFactory.FALCON_ID,
f@0 662 HapticsFactory.TABLET_ID};
f@0 663 String selection = (String)SpeechOptionPane.showSelectionDialog(
f@0 664 EditorFrame.this,
f@0 665 resources.getString("dialog.input.haptics.select"),
f@0 666 hapticDevices,
f@0 667 hapticDevices[0]);
f@0 668 if(selection == null)
f@0 669 return;
f@0 670 preferences.put("haptic_device", selection);
f@0 671 SpeechOptionPane.showMessageDialog(EditorFrame.this,
f@0 672 MessageFormat.format(resources.getString("dialog.feedback.haptic_init"),selection),
f@0 673 SpeechOptionPane.WARNING_MESSAGE);
f@0 674 }
f@0 675 }));
f@0 676
f@0 677 // awareness menu
f@0 678 JMenu awarenessMenu = factory.createMenu("preferences.awareness");
f@0 679 preferencesMenu.add(awarenessMenu);
f@0 680
f@0 681 awarenessMenu.add(factory.createMenuItem("preferences.awareness.username", this, "showAwarnessUsernameDialog"));
f@0 682 awarenessMenu.add(factory.createMenuItem("preferences.awareness.broadcast", this, "showAwarenessBroadcastDialog"));
f@0 683 awarenessMenu.add(factory.createMenuItem("preferences.awareness.display", this, "showAwarenessDisplayDialog"));
f@0 684
f@0 685 JMenuItem enableAwarenessVoiceMenuItem = factory.createCheckBoxMenuItem("preferences.awareness.enable_voice",
f@0 686 new ActionListener(){
f@0 687 @Override
f@0 688 public void actionPerformed(ActionEvent evt) {
f@0 689 JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) evt.getSource();
f@0 690 NarratorFactory.getInstance().setMuted(!menuItem.isSelected(),Narrator.SECOND_VOICE);
f@0 691 PreferencesService.getInstance().put("second_voice_enabled", Boolean.toString(menuItem.isSelected()));
f@0 692 }
f@0 693 });
f@0 694 NarratorFactory.getInstance().setMuted(true,Narrator.FIRST_VOICE);
f@0 695 enableAwarenessVoiceMenuItem.setSelected(Boolean.parseBoolean(
f@0 696 PreferencesService.getInstance().get("second_voice_enabled", Boolean.toString(true))));
f@0 697 awarenessMenu.add(enableAwarenessVoiceMenuItem);
f@0 698 NarratorFactory.getInstance().setMuted(false,Narrator.FIRST_VOICE);
f@0 699
f@0 700 // sound
f@0 701 JMenu soundMenu = factory.createMenu("preferences.sound");
f@0 702 preferencesMenu.add(soundMenu);
f@0 703
f@0 704 JMenuItem muteMenuItem = factory.createCheckBoxMenuItem("preferences.sound.mute", new ActionListener(){
f@0 705 @Override
f@0 706 public void actionPerformed(ActionEvent evt) {
f@0 707 JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) evt.getSource();
f@0 708 NarratorFactory.getInstance().setMuted(menuItem.isSelected(),Narrator.FIRST_VOICE);
f@0 709 SoundFactory.getInstance().setMuted(menuItem.isSelected());
f@0 710 }
f@0 711 });
f@0 712 soundMenu.add(muteMenuItem);
f@0 713
f@0 714 JMenuItem rateMenuItem = factory.createMenuItem("preferences.sound.rate", new ActionListener(){
f@0 715 @Override
f@0 716 public void actionPerformed(ActionEvent evt) {
f@0 717 Integer newRate = SpeechOptionPane.showNarratorRateDialog(
f@0 718 EditorFrame.this,
f@0 719 resources.getString("dialog.input.sound_rate"),
f@0 720 NarratorFactory.getInstance().getRate(),
f@0 721 Narrator.MIN_RATE,
f@0 722 Narrator.MAX_RATE);
f@0 723 if(newRate != null){
f@0 724 NarratorFactory.getInstance().setRate(newRate);
f@0 725 NarratorFactory.getInstance().speak(
f@0 726 MessageFormat.format(
f@0 727 resources.getString("dialog.feedback.speech_rate"),
f@0 728 newRate));
f@0 729 }else{
f@0 730 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 731 }
f@0 732 }
f@0 733 });
f@0 734 soundMenu.add(rateMenuItem);
f@0 735
f@0 736 //server ports
f@0 737 JMenu networkingMenu = factory.createMenu("preferences.server");
f@0 738 preferencesMenu.add(networkingMenu);
f@0 739
f@0 740 JMenuItem localPortMenuItem = factory.createMenuItem("preferences.local_server.port", this, "showLocalServerPortDialog");
f@0 741 networkingMenu.add(localPortMenuItem);
f@0 742
f@0 743 JMenuItem remotePortMenuItem = factory.createMenuItem("preferences.remote_server.port", this, "showRemoteServerPortDialog");
f@0 744 networkingMenu.add(remotePortMenuItem);
f@0 745
f@0 746 // accessible file chooser
f@0 747 JMenuItem fileChooserMenuItem = factory.createCheckBoxMenuItem("preferences.file_chooser", new ActionListener(){
f@0 748 @Override
f@0 749 public void actionPerformed(ActionEvent evt) {
f@0 750 JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) evt.getSource();
f@0 751 PreferencesService.getInstance().put("use_accessible_filechooser", Boolean.toString(menuItem.isSelected()));
f@0 752 }
f@0 753 });
f@0 754
f@0 755 JMenuItem enableLogMenuItem = factory.createCheckBoxMenuItem("preferences.enable_log", new ActionListener(){
f@0 756 @Override
f@0 757 public void actionPerformed(ActionEvent evt) {
f@0 758 JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) evt.getSource();
f@0 759 PreferencesService preferences = PreferencesService.getInstance();
f@0 760 preferences.put("enable_log", Boolean.toString(menuItem.isSelected()));
f@0 761 if(menuItem.isSelected()){
f@0 762 try{
f@0 763 InteractionLog.enable(preferences.get("dir.log", ""));
f@0 764 }catch(IOException ioe){
f@0 765 SpeechOptionPane.showMessageDialog(EditorFrame.this, resources.getString("dialog.error.log_enable"));
f@0 766 }
f@0 767 }else{
f@0 768 InteractionLog.disable();
f@0 769 }
f@0 770 }
f@0 771 });
f@0 772
f@0 773 /* temporarily mute the narrator to select the menus without triggering a speech */
f@0 774 NarratorFactory.getInstance().setMuted(true,Narrator.FIRST_VOICE);
f@0 775 fileChooserMenuItem.setSelected(Boolean.parseBoolean(PreferencesService.getInstance().get("use_accessible_filechooser", "true")));
f@0 776 enableLogMenuItem.setSelected(Boolean.parseBoolean(PreferencesService.getInstance().get("enable_log", "false")));
f@0 777 NarratorFactory.getInstance().setMuted(false,Narrator.FIRST_VOICE);
f@0 778 preferencesMenu.add(fileChooserMenuItem);
f@0 779 preferencesMenu.add(enableLogMenuItem);
f@0 780
f@0 781
f@0 782 /* --- HELP --- */
f@0 783 JMenu helpMenu = factory.createMenu("help");
f@0 784 menuBar.add(helpMenu);
f@0 785
f@0 786 helpMenu.add(factory.createMenuItem(
f@0 787 "help.about", this, "showAboutDialog"));
f@0 788
f@0 789 helpMenu.add(factory.createMenuItem(
f@0 790 "help.license", this, "showLicense"));
f@0 791
f@0 792 treeEnabledMenuUpdate(null);
f@0 793 diagramPanelEnabledMenuUpdate(null);
f@0 794 }
f@0 795
f@0 796 private void changeLookAndFeel(String lafName){
f@0 797 if(lafName == null)
f@0 798 lafName = UIManager.getSystemLookAndFeelClassName();
f@0 799 try{
f@0 800 UIManager.setLookAndFeel(lafName);
f@0 801 SwingUtilities.updateComponentTreeUI(EditorFrame.this);
f@0 802 }
f@0 803 catch (ClassNotFoundException ex) {}
f@0 804 catch (InstantiationException ex) {}
f@0 805 catch (IllegalAccessException ex) {}
f@0 806 catch (UnsupportedLookAndFeelException ex) {}
f@0 807 }
f@0 808
f@0 809 /**
f@0 810 * Adds a file name to the "recent files" list and rebuilds the "recent files" menu.
f@0 811 * @param newFile the file name to add
f@0 812 */
f@0 813 private void addRecentFile(final String newFile){
f@0 814 recentFiles.remove(newFile);
f@0 815 if (newFile == null || newFile.equals("")) return;
f@0 816 recentFiles.add(0, newFile);
f@0 817 buildRecentFilesMenu();
f@0 818 }
f@0 819
f@0 820 /* speaks out the selected tree node if the tree is focused or the tab label if the tab is focused */
f@0 821 private void speakFocusedComponent(String message){
f@0 822 message = (message == null) ? "" : message+"; ";//add a dot to pause the TTS
f@0 823 DiagramPanel dPanel = getActiveTab();
f@0 824 if(dPanel != null){
f@0 825 String focusedComponent = null;
f@0 826 if(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() instanceof JTree)
f@0 827 focusedComponent = dPanel.getTree().currentPathSpeech();
f@0 828 else
f@0 829 focusedComponent = MessageFormat.format(resources.getString("window.tab"),editorTabbedPane.getComponentTabTitle(dPanel));
f@0 830 NarratorFactory.getInstance().speak(message+focusedComponent);
f@0 831 }
f@0 832 }
f@0 833 /**
f@0 834 * Rebuilds the "recent files" menu.
f@0 835 */
f@0 836 private void buildRecentFilesMenu(){
f@0 837 recentFilesMenu.removeAll();
f@0 838 for (int i = 0; i < recentFiles.size(); i++){
f@0 839 final String file = recentFiles.get(i);
f@0 840 String name = new File(file).getName();
f@0 841 JMenuItem item = SpeechMenuFactory.getMenuItem(name);
f@0 842 item.setToolTipText(file);
f@0 843 recentFilesMenu.add(item);
f@0 844 item.addActionListener(new
f@0 845 ActionListener(){
f@0 846 public void actionPerformed(ActionEvent event){
f@0 847 try {
f@0 848 FileService.Open open = new FileService.DirectService().open(new File(((JMenuItem)event.getSource()).getToolTipText()));
f@0 849 InputStream in = open.getInputStream();
f@0 850 String path = open.getPath();
f@0 851 for(int i=0; i<editorTabbedPane.getTabCount();i++){
f@0 852 if(path.equals(editorTabbedPane.getToolTipTextAt(i))){
f@0 853 editorTabbedPane.setSelectedIndex(i);
f@0 854 return;
f@0 855 }
f@0 856 }
f@0 857 Diagram diagram = path.endsWith(PdPersistenceManager.PD_EXTENSION) ?
f@0 858 PdPersistenceManager.getInstance().decodeDiagramInstance(in) :
f@0 859 PersistenceManager.decodeDiagramInstance(in);
f@0 860 addTab(open.getPath(), diagram);
f@0 861 } catch (IOException exception) {
f@0 862 SpeechOptionPane.showMessageDialog(
f@0 863 editorTabbedPane,
f@0 864 exception.getLocalizedMessage());
f@0 865 }
f@0 866 }
f@0 867 });
f@0 868 }
f@0 869 }
f@0 870
f@0 871 /**
f@0 872 Asks the user to open a graph file.
f@0 873 */
f@0 874 public void openFile(){
f@0 875 InputStream in = null;
f@0 876 try{
f@0 877 FileService.Open open = fileService.open(null,null, extensionFilter,this);
f@0 878 in = open.getInputStream();
f@0 879 if(in != null){ // open.getInputStream() == null -> user clicked on cancel
f@0 880 String path = open.getPath();
f@0 881 int index = editorTabbedPane.getPathTabIndex(path);
f@0 882 if(index != -1){ //diagram is already open
f@0 883 editorTabbedPane.setSelectedIndex(index);
f@0 884 speakFocusedComponent("");
f@0 885 return;
f@0 886 }
f@0 887 /* every opened diagram must have a unique name */
f@0 888 if(editorTabbedPane.getDiagramNameTabIndex(open.getName()) != -1)
f@0 889 throw new IOException(resources.getString("dialog.error.same_file_name"));
f@0 890 iLog("START READ LOCAL DIAGRAM "+open.getName());
f@0 891 Diagram diagram = path.endsWith(PdPersistenceManager.PD_EXTENSION) ?
f@0 892 PdPersistenceManager.getInstance().decodeDiagramInstance(in) :
f@0 893 PersistenceManager.decodeDiagramInstance(in);
f@0 894 iLog("END READ LOCAL DIAGRAM "+open.getName());
f@0 895 /* force the name of the diagram to be the same as the file name *
f@0 896 * it should never be useful, unless the .ccmi file is edited manually */
f@0 897 diagram.setName(open.getName());
f@0 898 addTab(open.getPath(), diagram);
f@0 899 addRecentFile(open.getPath());
f@0 900 }
f@0 901 }
f@0 902 catch (IOException exception) {
f@0 903 SpeechOptionPane.showMessageDialog(
f@0 904 editorTabbedPane,
f@0 905 exception.getLocalizedMessage());
f@0 906 }finally{
f@0 907 if(in != null)
f@0 908 try{in.close();}catch(IOException ioe){ioe.printStackTrace();}
f@0 909 }
f@0 910 }
f@0 911
f@0 912 /**
f@0 913 * Close a diagram tab. If the diagram has not been saved the user will be
f@0 914 * asked if they want to save the diagram.
f@0 915 */
f@0 916 public void closeFile(){
f@0 917 DiagramPanel dPanel = getActiveTab();
f@0 918 if(dPanel.isModified()||dPanel.getFilePath() == null){
f@0 919 int answer = SpeechOptionPane.showConfirmDialog(
f@0 920 EditorFrame.this,
f@0 921 resources.getString("dialog.confirm.close"),
f@0 922 SpeechOptionPane.YES_NO_OPTION);
f@0 923
f@0 924 if(answer == SpeechOptionPane.YES_OPTION) // save file only if the user decides to
f@0 925 if(!saveFile())
f@0 926 return; /* if the user closes the save dialog do nothing */
f@0 927 }
f@0 928 iLog("diagram closed :"+dPanel.getDiagram().getName());
f@0 929 NarratorFactory.getInstance().speak(MessageFormat.format(resources.getString("speech.diagram_closed"),dPanel.getDiagram().getName()));
f@0 930 if(dPanel.getDiagram() instanceof NetDiagram){
f@0 931 if(clientConnectionManager != null && clientConnectionManager.isAlive())
f@0 932 clientConnectionManager.addRequest(new RmDiagramRequest(dPanel.getDiagram().getName()));
f@0 933 }
f@0 934 editorTabbedPane.remove(dPanel);
f@0 935 //getActiveTab, after removing, returns the new selected panel after the remotion, if any
f@0 936 String newFocusedTabName = null;
f@0 937 if(getActiveTab() != null){
f@0 938 newFocusedTabName = getActiveTab().getDiagram().getName();
f@0 939 }
f@0 940 haptics.removeDiagram(dPanel.getDiagram().getName(), newFocusedTabName);
f@0 941 }
f@0 942
f@0 943 /**
f@0 944 * Saves the currently open tab diagram into a file. If the diagram has no file path associated
f@0 945 * (it has never been saved before), {@link #saveFileAs()} is called.
f@0 946 *
f@0 947 * @return {@code true} if the file is successfully saved, or {@code false} otherwise.
f@0 948 */
f@0 949 public boolean saveFile(){
f@0 950 DiagramPanel diagramPanel = getActiveTab();
f@0 951 if (diagramPanel == null) // no tabs open
f@0 952 return false;
f@0 953 String filePath = diagramPanel.getFilePath();
f@0 954 if (filePath == null) {
f@0 955 return saveFileAs();
f@0 956 }
f@0 957
f@0 958 OutputStream out = null;
f@0 959 try{
f@0 960 File file = new File(filePath);
f@0 961 out = new BufferedOutputStream(new FileOutputStream(file));
f@0 962 Diagram d = diagramPanel.getDiagram();
f@0 963 if(d instanceof PdDiagram){
f@0 964 PdPersistenceManager.getInstance().encodeDiagramInstance(d, out);
f@0 965 }else {
f@0 966 PersistenceManager.encodeDiagramInstance(d, out);
f@0 967 }
f@0 968 /* we saved the diagram, therefore there are no more pending changes */
f@0 969 diagramPanel.setModified(false);
f@0 970 speakFocusedComponent(resources.getString("dialog.file_saved"));
f@0 971 return true;
f@0 972 }catch(IOException ioe){
f@0 973 SpeechOptionPane.showMessageDialog(editorTabbedPane,ioe.getLocalizedMessage());
f@0 974 return false;
f@0 975 }finally{
f@0 976 try {
f@0 977 out.close();
f@0 978 }catch(IOException ioe){ /*can't do anything */ }
f@0 979 }
f@0 980 }
f@0 981
f@0 982 /**
f@0 983 * Prompts the user with a dialog for saving a diagram on disk. The current diagram is not affected
f@0 984 * by this call and it keeps to be displayed in the editor.
f@0 985 *
f@0 986 * @return {@code true} if the file is successfully saved, or {@code false} otherwise.
f@0 987 */
f@0 988 public boolean saveCopy(){
f@0 989 DiagramPanel diagramPanel = getActiveTab();
f@0 990 if (diagramPanel == null) // no tabs open
f@0 991 return false;
f@0 992 OutputStream out = null;
f@0 993 FileService.Save save;
f@0 994 try {
f@0 995 save = fileService.save(
f@0 996 PreferencesService.getInstance().get("dir.diagrams", "."),
f@0 997 diagramPanel.getFilePath(), //default file to save to
f@0 998 extensionFilter,
f@0 999 null,
f@0 1000 defaultExtension,
f@0 1001 null);
f@0 1002 out = save.getOutputStream();
f@0 1003 if (out == null) /* user didn't select any file for saving */
f@0 1004 return false;
f@0 1005 if(diagramPanel.getDiagram() instanceof PdDiagram){
f@0 1006 PdPersistenceManager.getInstance().encodeDiagramInstance(diagramPanel.getDiagram(),save.getName(), out);
f@0 1007 }else{
f@0 1008 PersistenceManager.encodeDiagramInstance(diagramPanel.getDiagram(),save.getName(), out);
f@0 1009 }
f@0 1010 speakFocusedComponent(resources.getString("dialog.file_saved"));
f@0 1011 return true;
f@0 1012 } catch (IOException ioe) {
f@0 1013 SpeechOptionPane.showMessageDialog(editorTabbedPane,ioe.getLocalizedMessage());
f@0 1014 return false;
f@0 1015 } finally {
f@0 1016 if(out != null)
f@0 1017 try{out.close();}catch(IOException ioe){ioe.printStackTrace();}
f@0 1018 }
f@0 1019 }
f@0 1020
f@0 1021 /**
f@0 1022 * Saves the current diagram as a new file. The user is prompter with a {@code FileChooser}
f@0 1023 * to chose a file to save the diagram to.
f@0 1024 *
f@0 1025 * @return {@code true} if the file is successfully saved, or {@code false} otherwise.
f@0 1026 */
f@0 1027 public boolean saveFileAs() {
f@0 1028 DiagramPanel diagramPanel = getActiveTab();
f@0 1029 if (diagramPanel == null) // no tabs open
f@0 1030 return false;
f@0 1031 String oldName = diagramPanel.getDiagram().getName();
f@0 1032 OutputStream out = null;
f@0 1033 try {
f@0 1034 String[] currentTabs = new String[editorTabbedPane.getTabCount()];
f@0 1035 for(int i=0; i<editorTabbedPane.getTabCount();i++){
f@0 1036 currentTabs[i] = editorTabbedPane.getTitleAt(i);
f@0 1037 }
f@0 1038 FileService.Save save = fileService.save(
f@0 1039 PreferencesService.getInstance().get("dir.diagrams", "."),
f@0 1040 diagramPanel.getFilePath(),
f@0 1041 extensionFilter,
f@0 1042 null,
f@0 1043 defaultExtension,
f@0 1044 currentTabs);
f@0 1045 out = save.getOutputStream();
f@0 1046 if (out == null) /* user didn't select any file for saving */
f@0 1047 return false;
f@0 1048
f@0 1049 if(diagramPanel.getDiagram() instanceof PdDiagram){
f@0 1050 PdPersistenceManager.getInstance().encodeDiagramInstance(diagramPanel.getDiagram(),save.getName(), out);
f@0 1051 }else{
f@0 1052 PersistenceManager.encodeDiagramInstance(diagramPanel.getDiagram(),save.getName(), out);
f@0 1053 }
f@0 1054
f@0 1055 /* update the diagram and the diageam panel, after the saving */
f@0 1056 diagramPanel.getDiagram().setName(save.getName());
f@0 1057 diagramPanel.setFilePath(save.getPath());
f@0 1058 diagramPanel.setModified(false);
f@0 1059 speakFocusedComponent(resources.getString("dialog.file_saved"));
f@0 1060 /* update the haptics, remove first the diagram that is going o be renamed *
f@0 1061 * and then re-add the diagram under the new name (avoids haptics *
f@0 1062 * IllegalArgumentException when switching tab) */
f@0 1063 haptics.removeDiagram(oldName, null);
f@0 1064 haptics.addNewDiagram(diagramPanel.getDiagram().getName());
f@0 1065 for(Node n : diagramPanel.getDiagram().getCollectionModel().getNodes())
f@0 1066 haptics.addNode(n.getBounds().getCenterX(), n.getBounds().getCenterY(), System.identityHashCode(n),null);
f@0 1067 for(Edge e : diagramPanel.getDiagram().getCollectionModel().getEdges()){
f@0 1068 Edge.PointRepresentation pr = e.getPointRepresentation();
f@0 1069 haptics.addEdge(System.identityHashCode(e),pr.xs,pr.ys,pr.adjMatrix,pr.nodeStart,e.getStipplePattern(),e.getNameLine(),null);
f@0 1070 }
f@0 1071 return true;
f@0 1072 }catch(IOException ioe){
f@0 1073 SpeechOptionPane.showMessageDialog(editorTabbedPane,ioe.getLocalizedMessage());
f@0 1074 return false;
f@0 1075 }finally{
f@0 1076 if(out != null)
f@0 1077 try{out.close();}catch(IOException ioe){ioe.printStackTrace();}
f@0 1078 }
f@0 1079 }
f@0 1080
f@0 1081 /**
f@0 1082 Exits the program if no graphs have been modified
f@0 1083 or if the user agrees to abandon modified graphs.
f@0 1084 */
f@0 1085 public void exit(){
f@0 1086 /* check first whether there are modified diagrams */
f@0 1087 int diagramsToSave = 0;
f@0 1088 for(int i=0; i<editorTabbedPane.getTabCount();i++){
f@0 1089 DiagramPanel dPanel = editorTabbedPane.getComponentAt(i);
f@0 1090 if(dPanel.isModified()||dPanel.getFilePath() == null){
f@0 1091 diagramsToSave++;
f@0 1092 }
f@0 1093 }
f@0 1094
f@0 1095 if(diagramsToSave > 0){
f@0 1096 int answer = SpeechOptionPane.showConfirmDialog(
f@0 1097 EditorFrame.this,
f@0 1098 MessageFormat.format(resources.getString("dialog.confirm.exit"), diagramsToSave),
f@0 1099 SpeechOptionPane.YES_NO_OPTION);
f@0 1100 // if the doesn't want to save changes, veto the close
f@0 1101 if(answer != SpeechOptionPane.NO_OPTION){
f@0 1102 if(answer == SpeechOptionPane.YES_OPTION){ // user clicked on yes button we just get them back to the editor
f@0 1103 speakFocusedComponent("");
f@0 1104 }else{// user pressed the ESC button
f@0 1105 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1106 }
f@0 1107 return;
f@0 1108 }
f@0 1109 }
f@0 1110
f@0 1111 NarratorFactory.getInstance().dispose();
f@0 1112 SoundFactory.getInstance().dispose();
f@0 1113 haptics.dispose();
f@0 1114 if(server != null)
f@0 1115 server.shutdown();
f@0 1116 if(clientConnectionManager != null)
f@0 1117 clientConnectionManager.shutdown();
f@0 1118 BroadcastFilter broadcastFilter = BroadcastFilter.getInstance();
f@0 1119 if(broadcastFilter != null)
f@0 1120 broadcastFilter.saveProperties(this);
f@0 1121 DisplayFilter displayFilter = DisplayFilter.getInstance();
f@0 1122 if(displayFilter != null)
f@0 1123 displayFilter.saveProperties(this);
f@0 1124
f@0 1125 while(haptics.isAlive()){/* wait */}
f@0 1126 /* closes the logger's handlers */
f@0 1127 iLog("PROGRAM EXIT");
f@0 1128 InteractionLog.dispose();
f@0 1129
f@0 1130 savePreferences();
f@0 1131 System.exit(0);
f@0 1132 }
f@0 1133
f@0 1134 /**
f@0 1135 * Changes the selection path of the diagram tree to a specific destination.
f@0 1136 * The user with a selection dialog to choose the destination from the following
f@0 1137 * choices : the root of the diagram, one of the element types, a bookmarked
f@0 1138 * node or a node/edge reference.
f@0 1139 */
f@0 1140 public void jump(){
f@0 1141 String[] options = new String[canJumpRef ? 4 : 3];
f@0 1142 options[0] = resources.getString("options.jump.type");
f@0 1143 options[1] = resources.getString("options.jump.diagram");
f@0 1144 options[2] = resources.getString("options.jump.bookmark");
f@0 1145 if(canJumpRef){
f@0 1146 options[3] = resources.getString("options.jump.reference");
f@0 1147 }
f@0 1148 iLog("open jump to dialog","");
f@0 1149 String result = (String)SpeechOptionPane.showSelectionDialog(
f@0 1150 EditorFrame.this,
f@0 1151 resources.getString("dialog.input.jump.select"),
f@0 1152 options,
f@0 1153 options[0]);
f@0 1154 DiagramPanel dPanel = getActiveTab();
f@0 1155 DiagramTree tree = dPanel.getTree();
f@0 1156 if(result != null){
f@0 1157 if(result.equals(options[0])){ // jump type
f@0 1158 tree.jump(DiagramTree.JumpTo.SELECTED_TYPE);
f@0 1159 }else if(result.equals(options[1])){// diagram
f@0 1160 tree.jump(DiagramTree.JumpTo.ROOT);
f@0 1161 }else if(result.equals(options[2])){// bookmark
f@0 1162 tree.jump(DiagramTree.JumpTo.BOOKMARK);
f@0 1163 }else if(result.equals(options[3])){
f@0 1164 tree.jump(DiagramTree.JumpTo.REFERENCE);
f@0 1165 }
f@0 1166 }else{
f@0 1167 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1168 iLog("cancel jump to dialog","");
f@0 1169 }
f@0 1170 }
f@0 1171
f@0 1172 /**
f@0 1173 * Locates on the haptic device the node or edge currently selected on the diagram tree. A command
f@0 1174 * is sent to the haptic device which in turns drag the user to the node/edge location.
f@0 1175 */
f@0 1176 public void locate(){
f@0 1177 DiagramPanel dPanel = getActiveTab();
f@0 1178 DiagramTree tree = dPanel.getTree();
f@0 1179 DiagramElement de = (DiagramElement)tree.getSelectionPath().getLastPathComponent();
f@0 1180 HapticsFactory.getInstance().attractTo(System.identityHashCode(de));
f@0 1181 iLog("locate " +((de instanceof Node)? "node" : "edge"),DiagramElement.toLogString(de));
f@0 1182 }
f@0 1183
f@0 1184 /**
f@0 1185 * Selects on the diagram tree the node or edge that's currently being touched by the haptic
f@0 1186 * device.
f@0 1187 */
f@0 1188 public void hHighlight() {
f@0 1189 getActiveTab().getTree().jumpTo(hapticHighlightDiagramElement);
f@0 1190 iLog("highlight " +((hapticHighlightDiagramElement instanceof Node)? "node" : "edge"),DiagramElement.toLogString(hapticHighlightDiagramElement));
f@0 1191 }
f@0 1192
f@0 1193 /**
f@0 1194 * Sends a command to the haptic device to pick up an element (node or edge) for
f@0 1195 * moving it.
f@0 1196 *
f@0 1197 * @param de the diagram element to be picked up
f@0 1198 */
f@0 1199 public void hPickUp(DiagramElement de) {
f@0 1200 HapticsFactory.getInstance().pickUp(System.identityHashCode(de));
f@0 1201 }
f@0 1202
f@0 1203 /**
f@0 1204 * Prompts the user for an object insertion. The object can be a node, an edge, a property,
f@0 1205 * a modifier, an edge label, an edge arrow head. Which object is to be inserted depends on
f@0 1206 * the currently selected tree node on the tree.
f@0 1207 */
f@0 1208 public void insert(){
f@0 1209 DiagramPanel dPanel = getActiveTab();
f@0 1210 final DiagramTree tree = dPanel.getTree();
f@0 1211 DiagramTreeNode treeNode = (DiagramTreeNode)tree.getSelectionPath().getLastPathComponent();
f@0 1212 DiagramModelUpdater modelUpdater = dPanel.getDiagram().getModelUpdater();
f@0 1213 if(treeNode instanceof TypeMutableTreeNode){ //adding a diagram Element
f@0 1214 TypeMutableTreeNode typeNode = (TypeMutableTreeNode)treeNode;
f@0 1215 final DiagramElement diagramElement = (DiagramElement)typeNode.getPrototype().clone();
f@0 1216 try {
f@0 1217 if(diagramElement instanceof Edge){
f@0 1218 Edge edge = (Edge)diagramElement;
f@0 1219 edge.connect(Arrays.asList(tree.getSelectedNodes()));
f@0 1220 iLog("insert edge",DiagramElement.toLogString(edge));
f@0 1221 modelUpdater.insertInTree(diagramElement);
f@0 1222
f@0 1223 /* remove the selections on the edge's nodes and release their lock */
f@0 1224 tree.clearNodeSelections();
f@0 1225 for(int i=0; i<edge.getNodesNum();i++){
f@0 1226 modelUpdater.yieldLock(edge, Lock.MUST_EXIST,new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.INSERT_EDGE,edge.getId(),edge.getName()));
f@0 1227 }
f@0 1228 }else{ // adding a Node
f@0 1229 iLog("insert node ",DiagramElement.toLogString(diagramElement));
f@0 1230 modelUpdater.insertInTree(diagramElement);
f@0 1231 }
f@0 1232 } catch (ConnectNodesException cne) {
f@0 1233 final String message = cne.getLocalizedMessage();
f@0 1234 SoundFactory.getInstance().play(SoundEvent.ERROR, new PlayerListener(){
f@0 1235 @Override
f@0 1236 public void playEnded() {
f@0 1237 NarratorFactory.getInstance().speak(message);
f@0 1238 }
f@0 1239 });
f@0 1240 SoundFactory.getInstance().play(SoundEvent.ERROR);
f@0 1241 iLog("insert edge error",message);
f@0 1242 }
f@0 1243 }else if(treeNode instanceof PropertyTypeMutableTreeNode){ //adding a property
f@0 1244 PropertyTypeMutableTreeNode propTypeNode = (PropertyTypeMutableTreeNode)treeNode;
f@0 1245 Node n = (Node)propTypeNode.getNode();
f@0 1246 if(!modelUpdater.getLock(n, Lock.PROPERTIES, new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.ADD_PROPERTY,n.getId(),n.getName()))){
f@0 1247 iLog("Could not get lock on node for add properties",DiagramElement.toLogString(n));
f@0 1248 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.lock_failure.properties"), SpeechOptionPane.INFORMATION_MESSAGE);
f@0 1249 SoundFactory.getInstance().play(SoundEvent.MESSAGE_OK);
f@0 1250 return;
f@0 1251 }
f@0 1252
f@0 1253 iLog("open insert property dialog","");
f@0 1254 final String propertyValue = SpeechOptionPane.showInputDialog(EditorFrame.this,
f@0 1255 MessageFormat.format(resources.getString("dialog.input.property.text"), propTypeNode.getName()),
f@0 1256 ""
f@0 1257 );
f@0 1258 if(propertyValue != null){
f@0 1259 if(!propertyValue.isEmpty()){ //check that the user didn't enter an empty string
f@0 1260 iLog("insert property ", propTypeNode.getType()+" "+propertyValue);
f@0 1261 modelUpdater.addProperty(n, propTypeNode.getType(), propertyValue, DiagramEventSource.TREE);
f@0 1262 }else{
f@0 1263 SoundFactory.getInstance().play(SoundEvent.EMPTY);
f@0 1264 iLog("insert property", "");
f@0 1265 }
f@0 1266 }else{
f@0 1267 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1268 iLog("cancel insert property dialog","");
f@0 1269 }
f@0 1270 modelUpdater.yieldLock(n, Lock.PROPERTIES,new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.ADD_PROPERTY,n.getId(),n.getName()));
f@0 1271 }else if(treeNode instanceof PropertyMutableTreeNode){ // edit modifiers
f@0 1272 iLog("open modifiers dialog","");
f@0 1273 PropertyTypeMutableTreeNode typeNode = (PropertyTypeMutableTreeNode)treeNode.getParent();
f@0 1274 Node n = (Node)typeNode.getNode();
f@0 1275 Modifiers modifiers = n.getProperties().getModifiers(typeNode.getType());
f@0 1276 if(modifiers.isNull()){
f@0 1277 iLog("error:no modifiers for this property","");
f@0 1278 NarratorFactory.getInstance().speak(MessageFormat.format(resources.getString("dialog.warning.null_modifiers"),typeNode.getType()));
f@0 1279 }else{
f@0 1280 if(!modelUpdater.getLock(n, Lock.PROPERTIES,new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_MODIFIERS,n.getId(),n.getName()))){
f@0 1281 iLog("Could not get lock on node for set modifiers",DiagramElement.toLogString(n));
f@0 1282 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.lock_failure.properties"), SpeechOptionPane.INFORMATION_MESSAGE);
f@0 1283 SoundFactory.getInstance().play(SoundEvent.MESSAGE_OK);
f@0 1284 return;
f@0 1285 }
f@0 1286
f@0 1287 int index = typeNode.getIndex(treeNode);
f@0 1288 Set<Integer> result = SpeechOptionPane.showModifiersDialog(EditorFrame.this,
f@0 1289 MessageFormat.format(resources.getString("dialog.input.check_modifiers"),
f@0 1290 n.getProperties().getValues(typeNode.getType()).get(index)) ,
f@0 1291 modifiers.getTypes(),
f@0 1292 modifiers.getIndexes(index)
f@0 1293 );
f@0 1294 if(result == null){
f@0 1295 iLog("cancel modifiers dialog","");
f@0 1296 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1297 }else{
f@0 1298 iLog("edit modifiers",Arrays.toString(result.toArray()));
f@0 1299 modelUpdater.setModifiers(n, typeNode.getType(), index, result,DiagramEventSource.TREE);
f@0 1300 }
f@0 1301 modelUpdater.yieldLock(n, Lock.PROPERTIES,new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_MODIFIERS,n.getId(),n.getName()));
f@0 1302 }
f@0 1303 }else{ //NodeReferenceMutableTreeNode = edit label and arrow head
f@0 1304 NodeReferenceMutableTreeNode nodeRef = (NodeReferenceMutableTreeNode)treeNode;
f@0 1305 Node n = (Node)nodeRef.getNode();
f@0 1306 Edge e = (Edge)nodeRef.getEdge();
f@0 1307 if(!modelUpdater.getLock(e, Lock.EDGE_END,new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_ENDLABEL,e.getId(),e.getName()))){
f@0 1308 iLog("Could not get lock on edge for end label",DiagramElement.toLogString(e));
f@0 1309 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.lock_failure.end_label"), SpeechOptionPane.INFORMATION_MESSAGE);
f@0 1310 SoundFactory.getInstance().play(SoundEvent.MESSAGE_OK);
f@0 1311 return;
f@0 1312 }
f@0 1313 iLog("open edge operation selection dialog","");
f@0 1314
f@0 1315 boolean hasAvailArrowHeads = (e.getAvailableEndDescriptions().length > 0);
f@0 1316 String[] operations = new String[hasAvailArrowHeads ? 2 : 1];
f@0 1317 operations[0] = resources.getString("dialog.input.edge_operation.label");
f@0 1318 if(hasAvailArrowHeads)
f@0 1319 operations[1] = resources.getString("dialog.input.edge_operation.arrow_head");
f@0 1320 String choice = (String)SpeechOptionPane.showSelectionDialog(
f@0 1321 EditorFrame.this,
f@0 1322 resources.getString("dialog.input.edge_operation.select"),
f@0 1323 operations,
f@0 1324 operations[0]);
f@0 1325
f@0 1326 if(choice == null){
f@0 1327 iLog("cancel edge operation selection dialog","");
f@0 1328 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1329 modelUpdater.yieldLock(e, Lock.EDGE_END,
f@0 1330 new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_ENDLABEL,e.getId(),e.getName()));
f@0 1331 return;
f@0 1332 }
f@0 1333 if(choice.equals(operations[0])){ //operations[0] = edit edge end-label
f@0 1334 iLog("open edge label dialog","");
f@0 1335 String label = SpeechOptionPane.showInputDialog(
f@0 1336 EditorFrame.this,
f@0 1337 MessageFormat.format(resources.getString("dialog.input.edge_label"),n.getType(), n.getName()),
f@0 1338 e.getEndLabel(n) );
f@0 1339 if(label != null){
f@0 1340 modelUpdater.setEndLabel(e, n, label,DiagramEventSource.TREE);
f@0 1341 SoundFactory.getInstance().play(SoundEvent.OK, new PlayerListener(){
f@0 1342 @Override
f@0 1343 public void playEnded() {
f@0 1344 NarratorFactory.getInstance().speak(tree.currentPathSpeech());
f@0 1345 }
f@0 1346 });
f@0 1347 }else{
f@0 1348 iLog("cancel edge label dialog","");
f@0 1349 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1350 }
f@0 1351 }else{//operations[1] = edit edge arrow head
f@0 1352 String[] endDescriptions = new String[e.getAvailableEndDescriptions().length+1];
f@0 1353 for(int i=0;i<e.getAvailableEndDescriptions().length;i++)
f@0 1354 endDescriptions[i] = e.getAvailableEndDescriptions()[i];
f@0 1355 endDescriptions[endDescriptions.length-1] = Edge.NO_ENDDESCRIPTION_STRING;
f@0 1356
f@0 1357 iLog("open edge arrow head dialog","");
f@0 1358 final String endDescription = (String)SpeechOptionPane.showSelectionDialog(
f@0 1359 EditorFrame.this,
f@0 1360 MessageFormat.format(resources.getString("dialog.input.edge_arrowhead"),n.getType(), n.getName()),
f@0 1361 endDescriptions,
f@0 1362 endDescriptions[0]
f@0 1363 );
f@0 1364 if(endDescription != null){
f@0 1365 int index = Edge.NO_END_DESCRIPTION_INDEX;
f@0 1366 for(int i=0;i<e.getAvailableEndDescriptions().length;i++)
f@0 1367 if(endDescription.equals(e.getAvailableEndDescriptions()[i])){
f@0 1368 index = i;
f@0 1369 break;
f@0 1370 }
f@0 1371 modelUpdater.setEndDescription(e, n, index,DiagramEventSource.TREE);
f@0 1372 }else{
f@0 1373 iLog("cancel edge arrow head dialog","");
f@0 1374 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1375 }
f@0 1376 }
f@0 1377 modelUpdater.yieldLock(e, Lock.EDGE_END,
f@0 1378 new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_ENDLABEL,e.getId(),e.getName()));
f@0 1379 }
f@0 1380 }
f@0 1381
f@0 1382 /**
f@0 1383 * Prompts the user for an object deletion. The object can be a node, an edge, a property,
f@0 1384 * a modifier, an edge label, an edge arrow head. Which object is to be deleted depends on
f@0 1385 * the currently selected tree node on the tree.
f@0 1386 */
f@0 1387 public void delete(){
f@0 1388 DiagramPanel dPanel = getActiveTab();
f@0 1389 final DiagramTree tree = dPanel.getTree();
f@0 1390 final DiagramTreeNode treeNode = (DiagramTreeNode)tree.getSelectionPath().getLastPathComponent();
f@0 1391 DiagramModelUpdater modelUpdater = dPanel.getDiagram().getModelUpdater();
f@0 1392 if(treeNode instanceof DiagramElement){ //delete a diagram element
f@0 1393 final DiagramElement element = (DiagramElement)treeNode;
f@0 1394 boolean isNode = element instanceof Node;
f@0 1395 if(!modelUpdater.getLock(element,
f@0 1396 Lock.DELETE,
f@0 1397 new DiagramEventActionSource(DiagramEventSource.TREE,
f@0 1398 isNode ? Command.Name.REMOVE_NODE : Command.Name.REMOVE_EDGE,
f@0 1399 element.getId(),element.getName()))){
f@0 1400 iLog("Could not get lock on element for deletion",DiagramElement.toLogString(element));
f@0 1401 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.lock_failure.delete"), SpeechOptionPane.INFORMATION_MESSAGE);
f@0 1402 SoundFactory.getInstance().play(SoundEvent.MESSAGE_OK);
f@0 1403 return;
f@0 1404 }
f@0 1405 iLog("open delete "+ (isNode ? "node" : "edge") +" dialog","");
f@0 1406 int choice = SpeechOptionPane.showConfirmDialog(
f@0 1407 EditorFrame.this,
f@0 1408 MessageFormat.format(resources.getString("dialog.confirm.deletion"),element.getType(), element.getName()),
f@0 1409 SpeechOptionPane.OK_CANCEL_OPTION);
f@0 1410 if(choice != SpeechOptionPane.OK_OPTION){
f@0 1411 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1412 iLog("cancel delete " + (isNode ? "node" : "edge") +" dialog","");
f@0 1413 modelUpdater.yieldLock(element,
f@0 1414 Lock.DELETE,
f@0 1415 new DiagramEventActionSource(DiagramEventSource.TREE,
f@0 1416 isNode ? Command.Name.REMOVE_NODE : Command.Name.REMOVE_EDGE,
f@0 1417 element.getId(),
f@0 1418 element.getName()));
f@0 1419 return;
f@0 1420 }
f@0 1421 modelUpdater.takeOutFromTree(element);
f@0 1422 /* don't need to unlock because the object doesn't exist any more, but *
f@0 1423 * still need to make other users aware that the deletion process is finished */
f@0 1424 modelUpdater.sendAwarenessMessage(
f@0 1425 AwarenessMessage.Name.STOP_A,
f@0 1426 new DiagramEventActionSource(DiagramEventSource.TREE,
f@0 1427 isNode ? Command.Name.REMOVE_NODE : Command.Name.REMOVE_EDGE,
f@0 1428 element.getId(),element.getName())
f@0 1429 );
f@0 1430 }else if(treeNode.getParent() instanceof PropertyTypeMutableTreeNode){ //deleting a property
f@0 1431 PropertyTypeMutableTreeNode typeNode = (PropertyTypeMutableTreeNode)treeNode.getParent();
f@0 1432 Node n = (Node)typeNode.getNode();
f@0 1433 if(!modelUpdater.getLock(n,
f@0 1434 Lock.PROPERTIES,
f@0 1435 new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.REMOVE_PROPERTY,n.getId(),n.getName()))){
f@0 1436 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.lock_failure.properties"), SpeechOptionPane.INFORMATION_MESSAGE);
f@0 1437 iLog("Could not get lock for properties for deletion",DiagramElement.toLogString(n));
f@0 1438 SoundFactory.getInstance().play(SoundEvent.MESSAGE_OK);
f@0 1439 return;
f@0 1440 }
f@0 1441 iLog("open delete property dialog","");
f@0 1442 int choice = SpeechOptionPane.showConfirmDialog(
f@0 1443 EditorFrame.this,
f@0 1444 MessageFormat.format(resources.getString("dialog.confirm.deletion"),typeNode.getType(),treeNode.getName()),
f@0 1445 SpeechOptionPane.OK_CANCEL_OPTION);
f@0 1446 if(choice != SpeechOptionPane.OK_OPTION){
f@0 1447 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1448 iLog("cancel delete property dialog","");
f@0 1449 modelUpdater.yieldLock(n,
f@0 1450 Lock.PROPERTIES,
f@0 1451 new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.REMOVE_PROPERTY,n.getId(),n.getName()));
f@0 1452 return;
f@0 1453 }
f@0 1454 modelUpdater.removeProperty(n, typeNode.getType(), typeNode.getIndex(treeNode),DiagramEventSource.TREE);
f@0 1455 }else
f@0 1456 throw new IllegalStateException("Cannot delete a tree node of type: "+treeNode.getClass().getName());
f@0 1457 }
f@0 1458
f@0 1459 /**
f@0 1460 * Prompts the user for an object deletion. The object can be a node, an edge or a property.
f@0 1461 * Which object is to be renamed depends on the currently selected tree node on the tree.
f@0 1462 */
f@0 1463 public void rename(){
f@0 1464 DiagramPanel dPanel = getActiveTab();
f@0 1465 DiagramModelUpdater modelUpdater = dPanel.getDiagram().getModelUpdater();
f@0 1466 final DiagramTree tree = dPanel.getTree();
f@0 1467 DiagramTreeNode treeNode = (DiagramTreeNode)tree.getSelectionPath().getLastPathComponent();
f@0 1468 MessageFormat formatter = new MessageFormat(resources.getString("dialog.input.rename"));
f@0 1469 if(treeNode instanceof DiagramElement){
f@0 1470 DiagramElement element = (DiagramElement)dPanel.getTree().getSelectionPath().getLastPathComponent();
f@0 1471 Object arg[] = {element.getName()};
f@0 1472 boolean isNode = element instanceof Node;
f@0 1473 if(!modelUpdater.getLock(element,
f@0 1474 Lock.NAME,
f@0 1475 new DiagramEventActionSource(DiagramEventSource.TREE,
f@0 1476 isNode ? Command.Name.SET_NODE_NAME : Command.Name.SET_EDGE_NAME,element.getId(),element.getName()))){
f@0 1477 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.lock_failure.name"),SpeechOptionPane.INFORMATION_MESSAGE);
f@0 1478 iLog("Could not get lock on element for renaming",DiagramElement.toLogString(element));
f@0 1479 SoundFactory.getInstance().play(SoundEvent.MESSAGE_OK);
f@0 1480 return;
f@0 1481 }
f@0 1482 iLog("open rename "+(isNode ? "node" : "edge")+" dialog",DiagramElement.toLogString(element));
f@0 1483 String name = SpeechOptionPane.showInputDialog(EditorFrame.this,
f@0 1484 formatter.format(arg),
f@0 1485 element.getName());
f@0 1486 if(name != null){
f@0 1487 modelUpdater.setName(element,name,DiagramEventSource.TREE);
f@0 1488 }else{
f@0 1489 iLog("cancel rename "+(isNode ? "node" : "edge")+" dialog",DiagramElement.toLogString(element));
f@0 1490 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1491 }
f@0 1492 modelUpdater.yieldLock(element,
f@0 1493 Lock.NAME,
f@0 1494 new DiagramEventActionSource(
f@0 1495 DiagramEventSource.TREE,
f@0 1496 isNode ? Command.Name.SET_NODE_NAME : Command.Name.SET_EDGE_NAME,
f@0 1497 element.getId(),
f@0 1498 element.getName()));
f@0 1499 }else if(treeNode instanceof PropertyMutableTreeNode){
f@0 1500 PropertyTypeMutableTreeNode typeNode = (PropertyTypeMutableTreeNode)treeNode.getParent();
f@0 1501 Node n = (Node)typeNode.getNode();
f@0 1502 if(!modelUpdater.getLock(n,
f@0 1503 Lock.PROPERTIES,
f@0 1504 new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_PROPERTY,n.getId(),n.getName()))){
f@0 1505 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.lock_failure.properties"), SpeechOptionPane.INFORMATION_MESSAGE);
f@0 1506 iLog("Could not get lock on properties for renaming",DiagramElement.toLogString(n));
f@0 1507 SoundFactory.getInstance().play(SoundEvent.MESSAGE_OK);
f@0 1508 return;
f@0 1509 }
f@0 1510 Object arg[] = {treeNode.getName()};
f@0 1511 iLog("open rename property dialog",treeNode.getName());
f@0 1512 String name = SpeechOptionPane.showInputDialog(EditorFrame.this,
f@0 1513 formatter.format(arg),
f@0 1514 treeNode.getName());
f@0 1515 if(name == null){
f@0 1516 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1517 iLog("cancel rename property dialog",treeNode.getName());
f@0 1518 return;
f@0 1519 }
f@0 1520 modelUpdater.setProperty(n, typeNode.getType(), typeNode.getIndex(treeNode), name,DiagramEventSource.TREE);
f@0 1521 modelUpdater.yieldLock(n,
f@0 1522 Lock.PROPERTIES,
f@0 1523 new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_PROPERTY,n.getId(),n.getName()));
f@0 1524 }else
f@0 1525 throw new IllegalStateException("Cannot delete a tree node of type: "+treeNode.getClass().getName());
f@0 1526 }
f@0 1527
f@0 1528 /**
f@0 1529 * Prompts the user with a dialog to add or remove bookmarks on a tree node. Which node is to be
f@0 1530 * (un)bookmarked depends on the currently selected tree node on the tree.
f@0 1531 */
f@0 1532 public void editBookmarks(){
f@0 1533 boolean addBookmark = true;
f@0 1534 DiagramPanel dPanel = getActiveTab();
f@0 1535 final DiagramTree tree = dPanel.getTree();
f@0 1536 DiagramTreeNode treeNode = (DiagramTreeNode)tree.getLastSelectedPathComponent();
f@0 1537 DiagramModelUpdater modelUpdater = dPanel.getDiagram().getModelUpdater();
f@0 1538
f@0 1539 if(!modelUpdater.getLock(treeNode,
f@0 1540 Lock.BOOKMARK,
f@0 1541 DiagramEventActionSource.NULL)){
f@0 1542 iLog("Cannot get lock on tree node for bookmark", treeNode.getName());
f@0 1543 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.lock_failure.bookmark"), SpeechOptionPane.INFORMATION_MESSAGE);
f@0 1544 SoundFactory.getInstance().play(SoundEvent.MESSAGE_OK);
f@0 1545 return;
f@0 1546 }
f@0 1547
f@0 1548 if(!treeNode.getBookmarkKeys().isEmpty()){
f@0 1549 /* the are already bookmarks, thus we let the user chose whether they want to */
f@0 1550 /* add a new one or remove an old one */
f@0 1551 String[] options = {
f@0 1552 resources.getString("dialog.input.bookmark.select.add"),
f@0 1553 resources.getString("dialog.input.bookmark.select.remove")
f@0 1554 };
f@0 1555
f@0 1556 iLog("open select add/remove bookmark dialog","");
f@0 1557 String result = (String)SpeechOptionPane.showSelectionDialog(EditorFrame.this,
f@0 1558 resources.getString("dialog.input.bookmark.select.add_remove"),
f@0 1559 options,
f@0 1560 options[0]);
f@0 1561
f@0 1562 if(result == null){
f@0 1563 iLog("cancel select add/remove bookmark dialog","");
f@0 1564 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1565 modelUpdater.yieldLock(treeNode, Lock.BOOKMARK, DiagramEventActionSource.NULL);
f@0 1566 return;
f@0 1567 }
f@0 1568 if(result.equals(options[1]))
f@0 1569 addBookmark = false;
f@0 1570 }
f@0 1571
f@0 1572 if(addBookmark){
f@0 1573 boolean uniqueBookmarkChosen = false;
f@0 1574 while(!uniqueBookmarkChosen){
f@0 1575 iLog("open add bookmark dialog","");
f@0 1576 String bookmark = SpeechOptionPane.showInputDialog(EditorFrame.this, resources.getString("dialog.input.bookmark.text"),"");
f@0 1577 if(bookmark != null){
f@0 1578 if("".equals(bookmark)){
f@0 1579 iLog("error: entered empty bookmark","");
f@0 1580 SoundFactory.getInstance().play(SoundEvent.ERROR);// without listeners in order not to overwrite the speechdialog popping up again
f@0 1581 NarratorFactory.getInstance().speakWholeText(resources.getString("dialog.input.bookmark.text.empty"));
f@0 1582 }else if(tree.getModel().getBookmarks().contains(bookmark)){
f@0 1583 iLog("error: entered bookmark already existing",bookmark);
f@0 1584 SoundFactory.getInstance().play(SoundEvent.ERROR);
f@0 1585 NarratorFactory.getInstance().speakWholeText(resources.getString("dialog.input.bookmark.text.already_existing"));
f@0 1586 }else{
f@0 1587 tree.getModel().putBookmark(bookmark, treeNode,DiagramEventSource.TREE);
f@0 1588 uniqueBookmarkChosen = true;
f@0 1589 }
f@0 1590 }else{
f@0 1591 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1592 iLog("cancel add bookmark dialog","");
f@0 1593 break; //user no longer wants to choose, exit the dialog thus
f@0 1594 }
f@0 1595 }
f@0 1596 }else{ // removing a bookmark
f@0 1597 String[] bookmarksArray = new String[treeNode.getBookmarkKeys().size()];
f@0 1598 bookmarksArray = treeNode.getBookmarkKeys().toArray(bookmarksArray);
f@0 1599
f@0 1600 iLog("open remove bookmark dialog","");
f@0 1601 final String bookmark = (String)SpeechOptionPane.showSelectionDialog(
f@0 1602 EditorFrame.this,
f@0 1603 resources.getString("dialog.input.bookmark.delete"),
f@0 1604 bookmarksArray,
f@0 1605 bookmarksArray[0]
f@0 1606 );
f@0 1607
f@0 1608 if(bookmark != null){
f@0 1609 tree.getModel().removeBookmark(bookmark,DiagramEventSource.TREE);
f@0 1610 }else{
f@0 1611 iLog("cancel remove bookmark dialog","");
f@0 1612 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1613 }
f@0 1614 }
f@0 1615 modelUpdater.yieldLock(treeNode, Lock.BOOKMARK, DiagramEventActionSource.NULL);
f@0 1616 }
f@0 1617
f@0 1618 /**
f@0 1619 * Prompts the user with a dialog edit notes on a tree node. Which node is to be
f@0 1620 * noted depends on the currently selected tree node on the tree.
f@0 1621 */
f@0 1622 public void editNotes(){
f@0 1623 DiagramPanel dPanel = getActiveTab();
f@0 1624 DiagramTreeNode treeNode = (DiagramTreeNode)dPanel.getTree().getLastSelectedPathComponent();
f@0 1625 DiagramModelUpdater modelUpdater = dPanel.getDiagram().getModelUpdater();
f@0 1626 if(!modelUpdater.getLock(treeNode, Lock.NOTES, DiagramEventActionSource.NULL)){
f@0 1627 iLog("Could not get lock on tree node for notes",treeNode.getName());
f@0 1628 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.lock_failure.notes"), SpeechOptionPane.INFORMATION_MESSAGE);
f@0 1629 SoundFactory.getInstance().play(SoundEvent.MESSAGE_OK);
f@0 1630 return;
f@0 1631 }
f@0 1632
f@0 1633 String typeString = "";
f@0 1634 /* if the note is for a diagram element the dialog message is changed so that the type precedes the name */
f@0 1635 if(treeNode instanceof DiagramElement){
f@0 1636 typeString = ((DiagramElement)treeNode).getType() + " ";
f@0 1637 }
f@0 1638 /* if the note is for a property tree node the dialog message is changed so that the type precedes the name */
f@0 1639 if(treeNode instanceof PropertyMutableTreeNode){
f@0 1640 PropertyTypeMutableTreeNode parent = (PropertyTypeMutableTreeNode)treeNode.getParent();
f@0 1641 typeString = parent.getType() + " ";
f@0 1642 }
f@0 1643 iLog("open edit note dialog","");
f@0 1644 String result = SpeechOptionPane.showTextAreaDialog(EditorFrame.this, resources.getString("dialog.input.notes.text")+typeString+treeNode.getName() ,treeNode.getNotes());
f@0 1645 if(result != null){
f@0 1646 modelUpdater.setNotes(treeNode, result,DiagramEventSource.TREE);
f@0 1647 }else{
f@0 1648 iLog("cancel edit note dialog","");
f@0 1649 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1650 }
f@0 1651 modelUpdater.yieldLock(treeNode, Lock.NOTES,DiagramEventActionSource.NULL);
f@0 1652 }
f@0 1653
f@0 1654 /**
f@0 1655 * Starts the server on a background thread.
f@0 1656 */
f@0 1657 public void startServer(){
f@0 1658 iLog("server started","");
f@0 1659 /* If the awareness filter has not been created yet (by opening the awareness filter dialog) then create it */
f@0 1660 BroadcastFilter filter = BroadcastFilter.getInstance();
f@0 1661 if(filter == null)
f@0 1662 try{
f@0 1663 filter = BroadcastFilter.createInstance();
f@0 1664 }catch(IOException ioe){
f@0 1665 SpeechOptionPane.showMessageDialog(this, ioe.getLocalizedMessage());
f@0 1666 }
f@0 1667 /* create the server */
f@0 1668 server = Server.createServer();
f@0 1669 try{
f@0 1670 server.init();
f@0 1671 }catch(IOException ioe){
f@0 1672 SpeechOptionPane.showMessageDialog(
f@0 1673 editorTabbedPane,
f@0 1674 ioe.getLocalizedMessage());
f@0 1675 iLog("error: starting server",ioe.getLocalizedMessage());
f@0 1676 return;
f@0 1677 }
f@0 1678 server.start();
f@0 1679 startServerMenuItem.setEnabled(false);
f@0 1680 stopServerMenuItem.setEnabled(true);
f@0 1681 if(getActiveTab() != null && (!(getActiveTab().getDiagram() instanceof NetDiagram)))
f@0 1682 shareDiagramMenuItem.setEnabled(true);
f@0 1683 }
f@0 1684
f@0 1685 /**
f@0 1686 * Stops the running server
f@0 1687 */
f@0 1688 public void stopServer(){
f@0 1689 /* those network diagrams which are connected to the local server are reverted, *
f@0 1690 * that is the diagram panel is set with the delegate diagram of the network diagram */
f@0 1691 for(int i=0; i < editorTabbedPane.getTabCount(); i++){
f@0 1692 DiagramPanel dPanel = editorTabbedPane.getComponentAt(i);
f@0 1693 if(dPanel.getDiagram() instanceof NetDiagram){
f@0 1694 NetDiagram netDiagram = (NetDiagram)dPanel.getDiagram();
f@0 1695 if(netDiagram.getSocketChannel().equals(localSocket)){
f@0 1696 dPanel.setAwarenessPanelEnabled(false);
f@0 1697 dPanel.setDiagram(netDiagram.getDelegate());
f@0 1698 }
f@0 1699 }
f@0 1700 }
f@0 1701 server.shutdown(resources.getString("server.shutdown_msg"));
f@0 1702 server = null;
f@0 1703 if(localSocket != null){
f@0 1704 try{localSocket.close();}catch(IOException ioe){ioe.printStackTrace();}
f@0 1705 localSocket = null;
f@0 1706 }
f@0 1707 startServerMenuItem.setEnabled(true);
f@0 1708 stopServerMenuItem.setEnabled(false);
f@0 1709 shareDiagramMenuItem.setEnabled(false);
f@0 1710 if(getActiveTab() != null)
f@0 1711 fileCloseItem.setEnabled(true);
f@0 1712 iLog("server stopped","");
f@0 1713 }
f@0 1714
f@0 1715 /**
f@0 1716 * Makes a diagram shared on the server. When a diagram is shared, remote users can connect to the
f@0 1717 * server and edit it collaboratively with the local and the other connected users.
f@0 1718 */
f@0 1719 public void shareDiagram(){
f@0 1720 try{
f@0 1721 if(server == null)
f@0 1722 throw new DiagramShareException(resources.getString("server.not_running_exc"));
f@0 1723
f@0 1724 DiagramPanel dPanel = getActiveTab();
f@0 1725 Diagram diagram = dPanel.getDiagram();
f@0 1726 try {
f@0 1727 iLog("share diagram",diagram.getName());
f@0 1728 /* check if it's already connected to the local server (a.k.a. another diagram has been shared previously */
f@0 1729 if(localSocket == null){
f@0 1730 int port = Integer.parseInt(PreferencesService.getInstance().get("server.local_port",Server.DEFAULT_REMOTE_PORT));
f@0 1731 InetSocketAddress address = new InetSocketAddress("127.0.0.1",port);
f@0 1732 localSocket = SocketChannel.open(address);
f@0 1733 }
f@0 1734
f@0 1735 server.share(diagram);
f@0 1736 ProtocolFactory.newInstance().send(localSocket, new Command(Command.Name.LOCAL,diagram.getName(),DiagramEventSource.NONE));
f@0 1737 dPanel.setDiagram(NetDiagram.wrapLocalHost(diagram,localSocket,server.getLocalhostQueue(diagram.getName()),netLocalDiagramExceptionHandler));
f@0 1738 /* share a diagram once is enought :) */
f@0 1739 shareDiagramMenuItem.setEnabled(false);
f@0 1740 /* no close, there might be clients connected. unshare first. */
f@0 1741 fileCloseItem.setEnabled(false);
f@0 1742 /* only enabled for local diagram, otherwise changing the name *
f@0 1743 * of the diagram would messes up the network protocol */
f@0 1744 fileSaveAsItem.setEnabled(false);
f@0 1745 dPanel.setAwarenessPanelEnabled(true);
f@0 1746 } catch (IOException e) {
f@0 1747 iLog("error sharing diagram",diagram.getName()+" "+e.getLocalizedMessage());
f@0 1748 SpeechOptionPane.showMessageDialog(EditorFrame.this, e.getLocalizedMessage());
f@0 1749 return;
f@0 1750 }
f@0 1751 }catch(DiagramShareException dse){
f@0 1752 SpeechOptionPane.showMessageDialog(EditorFrame.this, dse.getLocalizedMessage());
f@0 1753 }
f@0 1754 }
f@0 1755
f@0 1756 /**
f@0 1757 * Prompts the user for a server address and connect to the server. The server is queried for the list
f@0 1758 * of the shared diagrams and the user is prompted with a selection dialog to chose a diagram to download.
f@0 1759 * The diagram is then downloaded and loaded into the diagram editor, ready for shared editing.
f@0 1760 *
f@0 1761 */
f@0 1762 public void openSharedDiagram(){
f@0 1763 iLog("open open share diagram dialog","");
f@0 1764 /* open the window prompting for the server address and make checks on the user input */
f@0 1765 String addr = SpeechOptionPane.showInputDialog(
f@0 1766 EditorFrame.this,
f@0 1767 resources.getString("dialog.share_diagram.enter_address"),
f@0 1768 PreferencesService.getInstance().get("server.address", ""));
f@0 1769 if(addr == null){
f@0 1770 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1771 iLog("cancel open share diagram dialog","");
f@0 1772 return;
f@0 1773 }else if(!ProtocolFactory.validateIPAddr(addr)){
f@0 1774 iLog("error:invalid IP address",addr);
f@0 1775 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.share_diagram.wrong_ip"));
f@0 1776 return;
f@0 1777 }else{
f@0 1778 PreferencesService.getInstance().put("server.address", addr);
f@0 1779 /* open the channel for the new diagram */
f@0 1780 SocketChannel channel = null;
f@0 1781 try {
f@0 1782 channel = SocketChannel.open();
f@0 1783 } catch (IOException e) {
f@0 1784 iLog("error:could not connect to the server","");
f@0 1785 SpeechOptionPane.showMessageDialog(EditorFrame.this, resources.getString("dialog.error.no_connection_to_server"));
f@0 1786 return;
f@0 1787 }
f@0 1788 /* download the diagram list */
f@0 1789 DiagramDownloader downloader = new DiagramDownloader(
f@0 1790 channel,
f@0 1791 addr,
f@0 1792 DiagramDownloader.CONNECT_AND_DOWNLOAD_LIST_TASK
f@0 1793 );
f@0 1794 iLog("open download diagram list dialog","");
f@0 1795 int option = SpeechOptionPane.showProgressDialog(EditorFrame.this, resources.getString("dialog.downloading_diagram_list"), downloader,500);
f@0 1796 if(option == SpeechOptionPane.CANCEL_OPTION){
f@0 1797 iLog("cancel download diagram list dialog","");
f@0 1798 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1799 try{channel.close();}catch(IOException ioe){ioe.printStackTrace();}
f@0 1800 }else{
f@0 1801 try{
f@0 1802 /* show the available diagram list */
f@0 1803 String result = downloader.get();
f@0 1804 if(result == null)
f@0 1805 throw new Exception(resources.getString("dialog.error.no_diagrams_on_server")); // go to the catch block
f@0 1806 String[] diagramsList = result.split("\n");
f@0 1807
f@0 1808 iLog("open select diagram to download dialog","");
f@0 1809 String diagramName = (String)SpeechOptionPane.showSelectionDialog(EditorFrame.this, "Select diagram to download", diagramsList, diagramsList[0]);
f@0 1810 if(diagramName == null){
f@0 1811 iLog("cancel select diagram to download dialog","");
f@0 1812 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1813 try{channel.close();}catch(IOException ioe){ioe.printStackTrace();}
f@0 1814 return;
f@0 1815 }
f@0 1816 /* there cannot be two diagrams with the same name open at the same time */
f@0 1817 if(editorTabbedPane.getDiagramNameTabIndex(diagramName) != -1)
f@0 1818 throw new IOException(resources.getString("dialog.error.same_file_name"));
f@0 1819 /* download the chosen diagram */
f@0 1820 downloader = new DiagramDownloader(channel,diagramName,DiagramDownloader.DOWNLOAD_DIAGRAM_TASK);
f@0 1821 iLog("open downloading diagram dialog",diagramName);
f@0 1822 option = SpeechOptionPane.showProgressDialog(EditorFrame.this, MessageFormat.format(resources.getString("dialog.downloading_diagram"), diagramName), downloader,500);
f@0 1823 if(option == SpeechOptionPane.CANCEL_OPTION){
f@0 1824 iLog("cancel downloading diagram dialog",diagramName);
f@0 1825 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 1826 try{channel.close();}catch(IOException ioe){ioe.printStackTrace();};
f@0 1827 }else{
f@0 1828 result = downloader.get();
f@0 1829
f@0 1830 if(clientConnectionManager == null || !clientConnectionManager.isAlive()){
f@0 1831 clientConnectionManager = new ClientConnectionManager(editorTabbedPane);
f@0 1832 clientConnectionManager.start();
f@0 1833 }
f@0 1834
f@0 1835 iLog("START READ NETWORK DIAGRAM "+diagramName);
f@0 1836 // FIXME no pd patches supported
f@0 1837 Diagram diagram = PersistenceManager.decodeDiagramInstance(new BufferedInputStream(new ByteArrayInputStream(result.getBytes("UTF-8"))));
f@0 1838 iLog("END READ NETWORK DIAGRAM "+diagramName);
f@0 1839 /* remove all the bookmarks in the server diagram model instance */
f@0 1840 for(String bookmarkKey : diagram.getTreeModel().getBookmarks())
f@0 1841 diagram.getTreeModel().removeBookmark(bookmarkKey,DiagramEventSource.TREE);
f@0 1842 Diagram newDiagram = NetDiagram.wrapRemoteHost(diagram,clientConnectionManager,channel);
f@0 1843 DiagramPanel dPanel = addTab(null,newDiagram);
f@0 1844 /* enable awareness on the new diagram */
f@0 1845 dPanel.setAwarenessPanelEnabled(true);
f@0 1846 /* make the network thread aware of the new shared diagram, from here on the messages received from the server will take effect */
f@0 1847 clientConnectionManager.addRequest(new ClientConnectionManager.AddDiagramRequest(channel, diagram));
f@0 1848 clientConnectionManager.addRequest(new SendAwarenessRequest(channel, new AwarenessMessage(
f@0 1849 AwarenessMessage.Name.USERNAME_A,
f@0 1850 newDiagram.getName(),
f@0 1851 AwarenessMessage.getDefaultUserName()
f@0 1852 )));
f@0 1853 }
f@0 1854 }catch(RuntimeException rte){
f@0 1855 throw new RuntimeException(rte);
f@0 1856 }catch(ExecutionException ee){
f@0 1857 try{channel.close();}catch(IOException ioe){ioe.printStackTrace();};
f@0 1858 /* if the exception happened in the DiagramDownloader then it's wrapped into an *
f@0 1859 * ExecutionException and we have to unwrap it to get a neat message for the user */
f@0 1860 SpeechOptionPane.showMessageDialog(
f@0 1861 editorTabbedPane,
f@0 1862 ee.getCause().getLocalizedMessage());
f@0 1863 iLog("error: "+ee.getCause().getMessage(),"");
f@0 1864 }catch(Exception exception){
f@0 1865 try{channel.close();}catch(IOException ioe){ioe.printStackTrace();};
f@0 1866 SpeechOptionPane.showMessageDialog(
f@0 1867 editorTabbedPane,
f@0 1868 exception.getLocalizedMessage());
f@0 1869 iLog("error: "+exception.getMessage(),"");
f@0 1870 }
f@0 1871 }
f@0 1872 }
f@0 1873 }
f@0 1874
f@0 1875 /**
f@0 1876 * Shows the awareness panel, a text pane where all the awareness informations received by the server
f@0 1877 * are displayed.
f@0 1878 */
f@0 1879 public void showAwarenessPanel(){
f@0 1880 DiagramPanel dPanel = getActiveTab();
f@0 1881 if(dPanel != null){
f@0 1882 dPanel.setAwarenessPanelVisible(true);
f@0 1883 NarratorFactory.getInstance().speak(resources.getString("speech.awareness_panel.open"));
f@0 1884 }
f@0 1885 }
f@0 1886
f@0 1887 /**
f@0 1888 * Hides the awareness panel, a text pane where all the awareness informations received by the server
f@0 1889 * are displayed.
f@0 1890 */
f@0 1891 public void hideAwarenessPanel(){
f@0 1892 DiagramPanel dPanel = getActiveTab();
f@0 1893 if(dPanel != null){
f@0 1894 dPanel.setAwarenessPanelVisible(false);
f@0 1895 NarratorFactory.getInstance().speak(resources.getString("speech.awareness_panel.close"));
f@0 1896 dPanel.getTree().requestFocus();
f@0 1897 }
f@0 1898 }
f@0 1899
f@0 1900 /**
f@0 1901 * Saves all the open diagram which have been modified since the last time they were saved into the
f@0 1902 * <i>backup</i> folder in the ccmi_editor_data directory.
f@0 1903 */
f@0 1904 public void backupOpenDiagrams(){
f@0 1905 SimpleDateFormat dateFormat = new SimpleDateFormat("EEE_d_MMM_yyyy_HH_mm_ss");
f@0 1906 String date = dateFormat.format(new Date());
f@0 1907 File backupDir = new File(new StringBuilder(backupDirPath)
f@0 1908 .append(System.getProperty("file.separator"))
f@0 1909 .append(date)
f@0 1910 .toString());
f@0 1911 backupDir.mkdir();
f@0 1912 for(int i=0; i<editorTabbedPane.getTabCount();i++){
f@0 1913 DiagramPanel dPanel = editorTabbedPane.getComponentAt(i);
f@0 1914 if(dPanel.isModified()||dPanel.getFilePath() == null){
f@0 1915 Diagram diagram = dPanel.getDiagram();
f@0 1916 File file = new File(backupDir,diagram.getName()+".ccmi");
f@0 1917 try {
f@0 1918 FileService.Save save = new FileService.DirectService().save((file));
f@0 1919 if(diagram instanceof PdDiagram){
f@0 1920 PdPersistenceManager.getInstance().encodeDiagramInstance(diagram, save.getOutputStream());
f@0 1921 }else {
f@0 1922 PersistenceManager.encodeDiagramInstance(diagram, save.getOutputStream());
f@0 1923 }
f@0 1924 } catch (IOException e) {
f@0 1925 e.printStackTrace();
f@0 1926 }
f@0 1927 }
f@0 1928 }
f@0 1929 }
f@0 1930
f@0 1931 /**
f@0 1932 Exports the current graph to an image file.
f@0 1933 */
f@0 1934 public void exportImage(){
f@0 1935 DiagramPanel dPanel = getActiveTab();
f@0 1936 if (dPanel == null)
f@0 1937 return;
f@0 1938 OutputStream out = null;
f@0 1939 try{
f@0 1940 String imageExtensions = resources.getString("files.image.extension");
f@0 1941 /* default save dir is the same as the diagram's or home/images otherwise */
f@0 1942 String path = dPanel.getFilePath();
f@0 1943 if(path == null)
f@0 1944 path = PreferencesService.getInstance().get("dir.images", ".");
f@0 1945 FileService.Save save = fileService.save(path, dPanel.getDiagram().getName(), exportFilter,
f@0 1946 defaultExtension, imageExtensions,null);
f@0 1947 out = save.getOutputStream();
f@0 1948 if (out != null){
f@0 1949 /* if the diagram has a name (has already been saved) then prompt the user with the name of
f@0 1950 * the diagram with a jpg extension. */
f@0 1951 String fileName = FileService.getFileNameFromPath(save.getPath(),true);
f@0 1952 String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
f@0 1953 if (!ImageIO.getImageWritersByFormatName(extension).hasNext()){
f@0 1954 throw new IOException(MessageFormat.format(
f@0 1955 resources.getString("dialog.error.unsupported_image"),
f@0 1956 extension
f@0 1957 ));
f@0 1958 }
f@0 1959 GraphPanel gPanel = dPanel.getGraphPanel();
f@0 1960 try{
f@0 1961 saveImage(gPanel, out, extension);
f@0 1962 speakFocusedComponent(resources.getString("dialog.file_saved"));
f@0 1963 }catch(IOException ioe){
f@0 1964 throw new IOException(resources.getString("dialog.error.save_image"),ioe);
f@0 1965 }
f@0 1966 }
f@0 1967 }
f@0 1968 catch (IOException ioe){
f@0 1969 SpeechOptionPane.showMessageDialog(editorTabbedPane,ioe.getMessage());
f@0 1970 }finally{
f@0 1971 if(out != null)
f@0 1972 try{out.close();}catch(IOException ioe){ioe.printStackTrace();}
f@0 1973 }
f@0 1974 }
f@0 1975
f@0 1976 /**
f@0 1977 Exports a current graph to an image file.
f@0 1978 @param graph the graph
f@0 1979 @param out the output stream
f@0 1980 @param format the image file format
f@0 1981
f@0 1982 @throws IOException if something goes wrong during the I/O operations
f@0 1983 */
f@0 1984 public static void saveImage(GraphPanel graph, OutputStream out, String format)
f@0 1985 throws IOException {
f@0 1986 // need a dummy image to get a Graphics to measure the size
f@0 1987 Rectangle2D bounds = graph.getBounds();
f@0 1988 BufferedImage image
f@0 1989 = new BufferedImage((int)bounds.getWidth() + 1,
f@0 1990 (int)bounds.getHeight() + 1,
f@0 1991 BufferedImage.TYPE_INT_RGB);
f@0 1992 Graphics2D g2 = (Graphics2D)image.getGraphics();
f@0 1993 g2.translate(-bounds.getX(), -bounds.getY());
f@0 1994 g2.setColor(Color.WHITE);
f@0 1995 g2.fill(new Rectangle2D.Double(
f@0 1996 bounds.getX(),
f@0 1997 bounds.getY(),
f@0 1998 bounds.getWidth() + 1,
f@0 1999 bounds.getHeight() + 1));
f@0 2000 g2.setColor(Color.BLACK);
f@0 2001 g2.setBackground(Color.WHITE);
f@0 2002 boolean hideGrid = graph.getHideGrid();
f@0 2003 graph.setHideGrid(true);
f@0 2004 graph.paintComponent(g2);
f@0 2005 graph.setHideGrid(hideGrid);
f@0 2006 ImageIO.write(image, format, out);
f@0 2007 }
f@0 2008
f@0 2009 /**
f@0 2010 * Shows the configuration dialog of the broadcast filter. The broadcast filter affects
f@0 2011 * which awareness informations are broadcasted from the server to the other clients.
f@0 2012 * If the local editor is not running the server, changes to the broadcast filter
f@0 2013 * will have no effect.
f@0 2014 */
f@0 2015 public void showAwarenessBroadcastDialog(){
f@0 2016 BroadcastFilter filter = BroadcastFilter.getInstance();
f@0 2017 if(filter == null)
f@0 2018 try{
f@0 2019 filter = BroadcastFilter.createInstance();
f@0 2020 }catch(IOException ioe){
f@0 2021 SpeechOptionPane.showMessageDialog(this, ioe.getLocalizedMessage());
f@0 2022 }
f@0 2023 filter.showDialog(this);
f@0 2024 }
f@0 2025
f@0 2026 /**
f@0 2027 * Shows the configuration dialog of the display filter. The display filter affects
f@0 2028 * which awareness informations are received from the server are actually displayed
f@0 2029 * to the user.
f@0 2030 */
f@0 2031 public void showAwarenessDisplayDialog(){
f@0 2032 DisplayFilter filter = DisplayFilter.getInstance();
f@0 2033 if(filter == null)
f@0 2034 try{
f@0 2035 filter = DisplayFilter.createInstance();
f@0 2036 }catch(IOException ioe){
f@0 2037 SpeechOptionPane.showMessageDialog(this, ioe.getLocalizedMessage());
f@0 2038 }
f@0 2039 filter.showDialog(this);
f@0 2040 }
f@0 2041
f@0 2042 /**
f@0 2043 * Prompts the user with a dialog to choose he awareness username. The username is used in the
f@0 2044 * awareness information to identify which client is doing the actions that are being notified.
f@0 2045 */
f@0 2046 public void showAwarnessUsernameDialog(){
f@0 2047 String oldName = AwarenessMessage.getDefaultUserName();
f@0 2048 String newName = SpeechOptionPane.showInputDialog(
f@0 2049 this,
f@0 2050 resources.getString("dialog.input.awerness_username"),
f@0 2051 oldName
f@0 2052 );
f@0 2053 if(newName == null){
f@0 2054 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 2055 return;
f@0 2056 }
f@0 2057
f@0 2058 if(newName.trim().isEmpty()){
f@0 2059 SoundFactory.getInstance().play(SoundEvent.ERROR, new PlayerListener(){
f@0 2060 @Override
f@0 2061 public void playEnded() {
f@0 2062 NarratorFactory.getInstance().speak(resources.getString("speech.empty_userame"));
f@0 2063 }
f@0 2064 });
f@0 2065 return;
f@0 2066 }
f@0 2067
f@0 2068 if(!newName.equals(oldName)){//if the name hasn't changed don't issue any message
f@0 2069 PreferencesService.getInstance().put("user.name", newName);
f@0 2070 AwarenessMessage.setDefaultUserName(newName);
f@0 2071 NarratorFactory.getInstance().speak(MessageFormat.format(
f@0 2072 resources.getString("dialog.feedback.awareness_username"),
f@0 2073 newName
f@0 2074 ));
f@0 2075 for(int i=0; i<editorTabbedPane.getTabCount(); i++){
f@0 2076 Diagram diagram = editorTabbedPane.getComponentAt(i).getDiagram();
f@0 2077 diagram.getModelUpdater().sendAwarenessMessage(
f@0 2078 AwarenessMessage.Name.USERNAME_A,
f@0 2079 newName);// send the new name only, the old name will be added by the server
f@0 2080 }
f@0 2081 }
f@0 2082 }
f@0 2083
f@0 2084 /**
f@0 2085 * Prompts the user with a dialog to choose the port number the local server will listen on.
f@0 2086 */
f@0 2087 public void showLocalServerPortDialog(){
f@0 2088 showServerPortDialog("server.local_port","dialog.input.local_server_port","dialog.feedback.local_server_port");
f@0 2089 }
f@0 2090
f@0 2091 /**
f@0 2092 * Prompts the user with a dialog to choose the remote server port number to connect to.
f@0 2093 */
f@0 2094 public void showRemoteServerPortDialog(){
f@0 2095 showServerPortDialog("server.remote_port","dialog.input.remote_server_port","dialog.feedback.remote_server_port");
f@0 2096 }
f@0 2097
f@0 2098 private void showServerPortDialog(String preferenceKey,String dialogMessage,String feedbackMessage){
f@0 2099 String oldPort = PreferencesService.getInstance().get(preferenceKey,Server.DEFAULT_LOCAL_PORT);
f@0 2100 String newPort = SpeechOptionPane.showInputDialog(this, resources.getString(dialogMessage), oldPort);
f@0 2101 if(newPort == null){
f@0 2102 SoundFactory.getInstance().play(SoundEvent.CANCEL);
f@0 2103 return;
f@0 2104 }
f@0 2105
f@0 2106 boolean badFormat = false;
f@0 2107 try {
f@0 2108 int port = Integer.parseInt(newPort);
f@0 2109 if(port <= 0 || port > 65535)
f@0 2110 badFormat = true;
f@0 2111 }catch(NumberFormatException nfe){
f@0 2112 badFormat = true;
f@0 2113 }
f@0 2114
f@0 2115 if(badFormat){
f@0 2116 SoundFactory.getInstance().play(SoundEvent.ERROR, new PlayerListener(){
f@0 2117 @Override
f@0 2118 public void playEnded() {
f@0 2119 NarratorFactory.getInstance().speak(resources.getString("speech.bad_format_port"));
f@0 2120 }
f@0 2121 });
f@0 2122 return;
f@0 2123 }
f@0 2124 PreferencesService.getInstance().put(preferenceKey,newPort);
f@0 2125 NarratorFactory.getInstance().speak(MessageFormat.format(
f@0 2126 resources.getString(feedbackMessage),newPort));
f@0 2127 }
f@0 2128
f@0 2129 /**
f@0 2130 Displays the About dialog box.
f@0 2131 */
f@0 2132 public void showAboutDialog(){
f@0 2133 String options[] = {resources.getString("dialog.ok_button")};
f@0 2134 SpeechSummaryPane.showDialog(this,
f@0 2135 resources.getString("dialog.about.title"),
f@0 2136 MessageFormat.format(resources.getString("dialog.about"),
f@0 2137 resources.getString("app.name"),
f@0 2138 resources.getString("app.version"),
f@0 2139 resources.getString("dialog.about.description"),
f@0 2140 resources.getString("dialog.about.license")),
f@0 2141 SpeechSummaryPane.OK_OPTION,
f@0 2142 options
f@0 2143 );
f@0 2144 }
f@0 2145
f@0 2146 /**
f@0 2147 * Displays the Software license in a dialog box.
f@0 2148 */
f@0 2149 public void showLicense() {
f@0 2150 BufferedReader reader = null;
f@0 2151 try{
f@0 2152 reader = new BufferedReader(
f@0 2153 new InputStreamReader(
f@0 2154 getClass().getResourceAsStream(
f@0 2155 "license.txt")));
f@0 2156 StringBuilder builder = new StringBuilder();
f@0 2157 String line;
f@0 2158 while ((line = reader.readLine()) != null){
f@0 2159 builder.append(line).append('\n');
f@0 2160 }
f@0 2161 String options[] = {resources.getString("dialog.ok_button")};
f@0 2162 SpeechSummaryPane.showDialog(editorTabbedPane,
f@0 2163 resources.getString("dialog.license.title"),
f@0 2164 builder.toString(),
f@0 2165 SpeechSummaryPane.OK_OPTION,options);
f@0 2166 }catch (IOException exception){
f@0 2167 SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.error.license_not_found"));
f@0 2168 }finally{
f@0 2169 if(reader != null)
f@0 2170 try{reader.close();}catch(IOException ioe){ioe.printStackTrace();}
f@0 2171 }
f@0 2172 }
f@0 2173
f@0 2174 /**
f@0 2175 * Saves the diagram template in the <i>templates</i> folder.
f@0 2176 *
f@0 2177 * The template can then be reused to create new diagrams with the same type of nodes and
f@0 2178 * edges.
f@0 2179 *
f@0 2180 * @param diagram the diagram to get the template from
f@0 2181 * @throws IOException if something goes wrong with I/O when saving the file
f@0 2182 */
f@0 2183 public void saveDiagramTemplate(Diagram diagram) throws IOException {
f@0 2184 File file = new File(
f@0 2185 new StringBuilder(PreferencesService.getInstance().get("home", "."))
f@0 2186 .append(System.getProperty("file.separator"))
f@0 2187 .append(resources.getString("dir.templates"))
f@0 2188 .append(System.getProperty("file.separator"))
f@0 2189 .append(diagram.getName())
f@0 2190 .append(resources.getString("template.extension"))
f@0 2191 .toString()
f@0 2192 );
f@0 2193 PersistenceManager.encodeDiagramTemplate(diagram,file);
f@0 2194
f@0 2195 }
f@0 2196
f@0 2197 /**
f@0 2198 * Adds a diagram type to the File->New menu.
f@0 2199 *
f@0 2200 * @param diagram the diagram whose nodes and edges definition will be used as a template
f@0 2201 * for new diagrams creation.
f@0 2202 *
f@0 2203 */
f@0 2204 public void addDiagramType(final Diagram diagram){
f@0 2205 /* this is to prevent the user from creating other diagram prototypes with the same name */
f@0 2206 existingTemplateNames.add(diagram.getName());
f@0 2207 existingTemplates.add(diagram);
f@0 2208 JMenuItem newTypeItem = SpeechMenuFactory.getMenuItem(diagram.getName());
f@0 2209 newTypeItem.addActionListener(new ActionListener(){
f@0 2210 @Override
f@0 2211 public void actionPerformed(ActionEvent event){
f@0 2212 Diagram clone = (Diagram)diagram.clone();
f@0 2213 /* find a good unique name for the new tab */
f@0 2214 Pattern pattern = Pattern.compile("new "+clone.getName()+"( \\(([0-9]+)\\))?");
f@0 2215 int maxOpenDiagram = -1;
f@0 2216 for(int i=0;i<editorTabbedPane.getTabCount();i++){
f@0 2217 Matcher matcher = pattern.matcher(editorTabbedPane.getComponentAt(i).getDiagram().getName());
f@0 2218 if(matcher.matches()){
f@0 2219 if(matcher.group(1) == null)
f@0 2220 maxOpenDiagram = 0;
f@0 2221 else
f@0 2222 maxOpenDiagram = Math.max(maxOpenDiagram, Integer.parseInt(matcher.group(2)));
f@0 2223 }
f@0 2224 }
f@0 2225 if(maxOpenDiagram >= 0)
f@0 2226 clone.setName(String.format("new %s (%d)", clone.getName(),++maxOpenDiagram));
f@0 2227 else clone.setName("new "+clone.getName());
f@0 2228 addTab(null, clone);
f@0 2229 iLog("new diagram created of type: "+diagram.getName());
f@0 2230 }
f@0 2231 });
f@0 2232 newMenu.add(newTypeItem);
f@0 2233 }
f@0 2234
f@0 2235 /**
f@0 2236 * Saves the user preferences before exiting.
f@0 2237 */
f@0 2238 public void savePreferences(){
f@0 2239 String recent = "";
f@0 2240 for (int i = 0; i < Math.min(recentFiles.size(), maxRecentFiles); i++){
f@0 2241 if (recent.length() > 0) recent += "|";
f@0 2242 recent += recentFiles.get(i);
f@0 2243 }
f@0 2244 preferences.put("recent", recent);
f@0 2245 }
f@0 2246
f@0 2247 /**
f@0 2248 * Returns the currently selected tab's diagram panel.
f@0 2249 *
f@0 2250 * @return the currently selected tab's diagram panel or {@code null}
f@0 2251 * if no tab is open.
f@0 2252 */
f@0 2253 public DiagramPanel getActiveTab(){
f@0 2254 return (DiagramPanel)editorTabbedPane.getSelectedComponent();
f@0 2255 }
f@0 2256
f@0 2257 /**
f@0 2258 * Set the variable holding the node or edge that would be highlighted if
f@0 2259 * {@link #hHighlight()} is called. The menu item for highlight is also enabled
f@0 2260 * if {@code de} is not {@code null}.
f@0 2261 *
f@0 2262 * @param de the diagram element to be selected by {@code hHighlight()}
f@0 2263 * or {@code null} for no selection.
f@0 2264 */
f@0 2265 public void selectHapticHighligh(DiagramElement de){
f@0 2266 hapticHighlightDiagramElement = de;
f@0 2267 highlightMenuItem.setEnabled(de == null ? false : true);
f@0 2268 }
f@0 2269
f@0 2270 private DiagramPanel addTab(String path, Diagram diagram){
f@0 2271 DiagramPanel diagramPanel = new DiagramPanel(diagram,editorTabbedPane);
f@0 2272 diagramPanel.setFilePath(path);
f@0 2273 diagramPanel.getTree().addTreeSelectionListener(treeSelectionListener);
f@0 2274 diagramPanel.setAwarenessPanelListener(awarenessPanelListener);
f@0 2275 /* update the haptics */
f@0 2276 haptics.addNewDiagram(diagramPanel.getDiagram().getName());
f@0 2277 for(Node n : diagram.getCollectionModel().getNodes())
f@0 2278 haptics.addNode(n.getBounds().getCenterX(), n.getBounds().getCenterY(), System.identityHashCode(n),null);
f@0 2279 for(Edge e : diagram.getCollectionModel().getEdges()){
f@0 2280 Edge.PointRepresentation pr = e.getPointRepresentation();
f@0 2281 haptics.addEdge(System.identityHashCode(e),pr.xs,pr.ys,pr.adjMatrix,pr.nodeStart,e.getStipplePattern(),e.getNameLine(),null);
f@0 2282 }
f@0 2283 /* install the listener that handling the haptics device and the one handling the audio feedback */
f@0 2284 diagram.getCollectionModel().addCollectionListener(hapticTrigger);
f@0 2285 AudioFeedback audioFeedback = new AudioFeedback(diagramPanel.getTree());
f@0 2286 diagram.getCollectionModel().addCollectionListener(audioFeedback);
f@0 2287 diagram.getTreeModel().addDiagramTreeNodeListener(audioFeedback);
f@0 2288
f@0 2289 editorTabbedPane.add(diagramPanel);
f@0 2290 editorTabbedPane.setToolTipTextAt(editorTabbedPane.getTabCount()-1,path);//the new panel is at tabCount -1
f@0 2291 editorTabbedPane.setSelectedIndex(editorTabbedPane.getTabCount()-1);
f@0 2292 /* give the focus to the Content Pane, else it's grabbed by the rootPane
f@0 2293 and it does not work when adding a new tab with the tree focused */
f@0 2294 getContentPane().requestFocusInWindow();
f@0 2295 return diagramPanel;
f@0 2296 }
f@0 2297
f@0 2298 private void diagramPanelEnabledMenuUpdate(DiagramPanel dPanel){
f@0 2299 fileSaveItem.setEnabled(false);
f@0 2300 fileSaveAsItem.setEnabled(false);
f@0 2301 fileSaveCopyItem.setEnabled(false);
f@0 2302 fileCloseItem.setEnabled(false);
f@0 2303 shareDiagramMenuItem.setEnabled(false);
f@0 2304 graphExportItem.setEnabled(false);
f@0 2305 showAwarenessPanelMenuItem.setEnabled(false);
f@0 2306 hideAwarenessPanelMenuItem.setEnabled(false);
f@0 2307 if(dPanel == null)
f@0 2308 return;
f@0 2309
f@0 2310 fileSaveItem.setEnabled(true);
f@0 2311 fileSaveCopyItem.setEnabled(true);
f@0 2312 graphExportItem.setEnabled(true);
f@0 2313 if(dPanel.getDiagram() instanceof NetDiagram){
f@0 2314 if(dPanel.isAwarenessPanelVisible())
f@0 2315 hideAwarenessPanelMenuItem.setEnabled(true);
f@0 2316 else
f@0 2317 showAwarenessPanelMenuItem.setEnabled(true);
f@0 2318 }else{
f@0 2319 /* only enabled for local diagram, otherwise changing the name *
f@0 2320 * of the diagram would messes up the network protocol */
f@0 2321 fileSaveAsItem.setEnabled(true);
f@0 2322 }
f@0 2323
f@0 2324 boolean isSharedDiagram = dPanel.getDiagram() instanceof NetDiagram;
f@0 2325 if(server != null && !isSharedDiagram){
f@0 2326 shareDiagramMenuItem.setEnabled(true);
f@0 2327 }
f@0 2328
f@0 2329 if(!(isSharedDiagram && dPanel.getDiagram().getLabel().endsWith(NetDiagram.LOCALHOST_STRING)))
f@0 2330 fileCloseItem.setEnabled(true);
f@0 2331 }
f@0 2332
f@0 2333 private void treeEnabledMenuUpdate(TreePath path){
f@0 2334 canJumpRef = false;
f@0 2335 insertMenuItem.setEnabled(false);
f@0 2336 deleteMenuItem.setEnabled(false);
f@0 2337 renameMenuItem.setEnabled(false);
f@0 2338 editNotesMenuItem.setEnabled(false);
f@0 2339 bookmarkMenuItem.setEnabled(false);
f@0 2340 jumpMenuItem.setEnabled(false);
f@0 2341 locateMenuItem.setEnabled(false);
f@0 2342 selectMenuItem.setEnabled(false);
f@0 2343 if(path == null)
f@0 2344 return;
f@0 2345
f@0 2346 jumpMenuItem.setEnabled(true);
f@0 2347 editNotesMenuItem.setEnabled(true);
f@0 2348 bookmarkMenuItem.setEnabled(true);
f@0 2349
f@0 2350 /* jump to reference : a reference node must be selected */
f@0 2351 DiagramTreeNode treeNode = (DiagramTreeNode)path.getLastPathComponent();
f@0 2352
f@0 2353 /* root node */
f@0 2354 if((treeNode).getParent() == null)
f@0 2355 return;
f@0 2356
f@0 2357 if(treeNode instanceof EdgeReferenceMutableTreeNode)
f@0 2358 canJumpRef = true;
f@0 2359
f@0 2360 if(treeNode instanceof NodeReferenceMutableTreeNode){
f@0 2361 insertMenuItem.setEnabled(true);
f@0 2362 canJumpRef = true ;
f@0 2363 }
f@0 2364
f@0 2365 /* insert a node : the type node must be selected */
f@0 2366 if(treeNode instanceof TypeMutableTreeNode){
f@0 2367 insertMenuItem.setEnabled(true);
f@0 2368 }
f@0 2369
f@0 2370 /* it's a property node */
f@0 2371 if(treeNode instanceof PropertyMutableTreeNode){
f@0 2372 deleteMenuItem.setEnabled(true);
f@0 2373 renameMenuItem.setEnabled(true);
f@0 2374 insertMenuItem.setEnabled(true);
f@0 2375 }
f@0 2376
f@0 2377 if(treeNode instanceof PropertyTypeMutableTreeNode)
f@0 2378 insertMenuItem.setEnabled(true);
f@0 2379 if(treeNode instanceof DiagramElement){
f@0 2380 deleteMenuItem.setEnabled(true);
f@0 2381 renameMenuItem.setEnabled(true);
f@0 2382 if(HapticsFactory.getInstance().isAlive())
f@0 2383 locateMenuItem.setEnabled(true);
f@0 2384 if(treeNode instanceof Node)
f@0 2385 selectMenuItem.setEnabled(true);
f@0 2386 }
f@0 2387 }
f@0 2388
f@0 2389 private boolean readTemplateFiles(File[] files){
f@0 2390
f@0 2391 /* add the pd diagam type first */
f@0 2392 addDiagramType(new PdDiagram());
f@0 2393
f@0 2394 boolean someFilesNotRead = false;
f@0 2395 for(File file : files){
f@0 2396 try {
f@0 2397 Diagram d = PersistenceManager.decodeDiagramTemplate(file);
f@0 2398 addDiagramType(d);
f@0 2399 } catch (IOException e) {
f@0 2400 someFilesNotRead = true;
f@0 2401 e.printStackTrace();
f@0 2402 }
f@0 2403 }
f@0 2404 return someFilesNotRead;
f@0 2405 }
f@0 2406
f@0 2407 private void iLog(String action,String args){
f@0 2408 InteractionLog.log("TREE",action,args);
f@0 2409 }
f@0 2410
f@0 2411 private void iLog(String message){
f@0 2412 InteractionLog.log(message);
f@0 2413 }
f@0 2414
f@0 2415 private Server server;
f@0 2416 private SocketChannel localSocket;
f@0 2417 private ExceptionHandler netLocalDiagramExceptionHandler;
f@0 2418 private ClientConnectionManager clientConnectionManager;
f@0 2419 private Haptics haptics;
f@0 2420 private ResourceBundle resources;
f@0 2421 public EditorTabbedPane editorTabbedPane;
f@0 2422 private FileService.ChooserService fileService;
f@0 2423 private PreferencesService preferences;
f@0 2424 private HapticTrigger hapticTrigger;
f@0 2425 private DiagramElement hapticHighlightDiagramElement;
f@0 2426 private ArrayList<String> existingTemplateNames;
f@0 2427 private ArrayList<Diagram> existingTemplates;
f@0 2428 private AwarenessPanelEnablingListener awarenessPanelListener;
f@0 2429
f@0 2430 private JMenu newMenu;
f@0 2431 private JMenuItem jumpMenuItem;
f@0 2432 private boolean canJumpRef;
f@0 2433 private JMenuItem fileSaveItem;
f@0 2434 private JMenuItem graphExportItem;
f@0 2435 private JMenuItem fileSaveAsItem;
f@0 2436 private JMenuItem fileSaveCopyItem;
f@0 2437 private JMenuItem fileCloseItem;
f@0 2438 private JMenuItem insertMenuItem;
f@0 2439 private JMenuItem deleteMenuItem;
f@0 2440 private JMenuItem renameMenuItem;
f@0 2441 private JMenuItem selectMenuItem;
f@0 2442 private JMenuItem bookmarkMenuItem;
f@0 2443 private JMenuItem editNotesMenuItem;
f@0 2444 private JMenuItem locateMenuItem;
f@0 2445 private JMenuItem highlightMenuItem;
f@0 2446 private JMenuItem shareDiagramMenuItem;
f@0 2447 private JMenuItem startServerMenuItem;
f@0 2448 private JMenuItem stopServerMenuItem;
f@0 2449 private JMenuItem showAwarenessPanelMenuItem;
f@0 2450 private JMenuItem hideAwarenessPanelMenuItem;
f@0 2451 private TreeSelectionListener treeSelectionListener;
f@0 2452 private ChangeListener tabChangeListener;
f@0 2453 private String defaultExtension;
f@0 2454 private String backupDirPath;
f@0 2455 private ArrayList<String> recentFiles;
f@0 2456 private JMenu recentFilesMenu;
f@0 2457 private int maxRecentFiles = DEFAULT_MAX_RECENT_FILES;
f@0 2458
f@0 2459 private ExtensionFilter extensionFilter;
f@0 2460 private ExtensionFilter exportFilter;
f@0 2461
f@0 2462 private static final int DEFAULT_MAX_RECENT_FILES = 5;
f@0 2463 private static final double GROW_SCALE_FACTOR = Math.sqrt(2);
f@0 2464
f@0 2465 }