001 /** 002 * planningtool - A Planning Tool with Critiquing Support. 003 * 004 * Copyright (C) 2006 olale 005 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU General Public License 008 * as published by the Free Software Foundation; either version 2 009 * of the License, or (at your option) any later version. 010 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 016 * You should have received a copy of the GNU General Public License 017 * along with this program; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 020 * Contact information: 021 * E-mail: olale@ida.liu.se 022 * olale@lysator.liu.se 023 */ 024 package se.liu.ida.critiquer.gui; 025 026 import java.awt.BorderLayout; 027 import java.awt.Container; 028 import java.awt.Dimension; 029 import java.awt.FileDialog; 030 import java.awt.Graphics2D; 031 import java.awt.GridLayout; 032 import java.awt.HeadlessException; 033 import java.awt.Menu; 034 import java.awt.MenuBar; 035 import java.awt.MenuItem; 036 import java.awt.MenuShortcut; 037 import java.awt.Rectangle; 038 import java.awt.event.ActionEvent; 039 import java.awt.event.ActionListener; 040 import java.awt.event.KeyEvent; 041 import java.io.File; 042 import java.io.FileInputStream; 043 import java.io.FileNotFoundException; 044 import java.io.FileOutputStream; 045 import java.io.IOException; 046 import java.io.ObjectInput; 047 import java.io.ObjectInputStream; 048 import java.io.ObjectOutput; 049 import java.io.ObjectOutputStream; 050 import java.util.List; 051 import java.util.Vector; 052 053 import javax.swing.BorderFactory; 054 import javax.swing.JFrame; 055 import javax.swing.JLabel; 056 import javax.swing.JPanel; 057 058 import org.tigris.gef.base.LayerManager; 059 import org.tigris.gef.base.LayerPerspective; 060 import org.tigris.gef.event.GraphSelectionEvent; 061 import org.tigris.gef.event.GraphSelectionListener; 062 import org.tigris.gef.graph.GraphModel; 063 import org.tigris.gef.graph.presentation.DefaultGraphModel; 064 import org.tigris.gef.graph.presentation.JGraph; 065 import org.tigris.gef.graph.presentation.NetEdge; 066 import org.tigris.gef.graph.presentation.NetNode; 067 068 import se.liu.ida.critiquer.activities.Activity; 069 import se.liu.ida.critiquer.activities.ActivityUtils; 070 import se.liu.ida.critiquer.activities.parameters.AbstractTaskViewParameter; 071 import se.liu.ida.critiquer.activities.parameters.ActivityParameter; 072 import se.liu.ida.critiquer.activities.parameters.Commanders; 073 import se.liu.ida.critiquer.activities.parameters.TaskViewParameter; 074 import se.liu.ida.critiquer.gui.graph.ActivityEditor; 075 import se.liu.ida.critiquer.gui.graph.ActivityFigNode; 076 import se.liu.ida.critiquer.gui.graph.ActivityNode; 077 import se.liu.ida.critiquer.gui.graph.GraphRenderingListener; 078 import se.liu.ida.critiquer.gui.graph.InheritanceEdge; 079 import se.liu.ida.critiquer.gui.graph.InheritanceFigEdge; 080 import se.liu.ida.critiquer.gui.graph.OrderingEdge; 081 import se.liu.ida.critiquer.gui.graph.OrderingFigEdge; 082 import se.liu.ida.critiquer.mics.ReferenceHolder; 083 import se.liu.ida.critiquer.mics.Utils; 084 import se.liu.ida.critiquer.resources.Agent; 085 086 public class TaskView extends AbstractView implements GraphRenderingListener { 087 088 /** 089 * 090 */ 091 private static final long serialVersionUID = 1L; 092 093 private final class ResetItem implements ActionListener { 094 public void actionPerformed(ActionEvent e) { 095 if (graph.getEditor().getLayerManager().getActiveLayer() instanceof LayerPerspective) { 096 DefaultGraphModel model = (DefaultGraphModel) graph.getGraphModel(); 097 deleteNodes(model); 098 deleteEdges(model); 099 100 } else { 101 System.err.println("No active LayerPerspective"); 102 } 103 } 104 } 105 106 private final class OpenItem implements ActionListener { 107 public void actionPerformed(ActionEvent e) { 108 FileDialog fileDialog = new FileDialog(mainWindow, "Open plan", FileDialog.LOAD); 109 fileDialog.setVisible(true); 110 111 String fileName = fileDialog.getDirectory() + System.getProperty("file.separator") + fileDialog.getFile(); 112 113 if (fileName != null) { 114 File file = new File(fileName); 115 try { 116 ObjectInput is = new ObjectInputStream(new FileInputStream(file)); 117 118 ReferenceHolder.commanders = (Commanders) is.readObject(); 119 DefaultGraphModel model = (DefaultGraphModel) is.readObject(); 120 121 DefaultGraphModel oldModel = (DefaultGraphModel) graph.getGraphModel(); 122 /** 123 * Clear the old model and replace it with a new one by 124 * hand.. 125 */ 126 deleteNodes(oldModel); 127 deleteEdges(oldModel); 128 addNodes(model, oldModel); 129 addEdges(model, oldModel); 130 ReferenceHolder.organizationModel.setRootAgent((Agent) is.readObject()); 131 132 } catch (Exception exception) { 133 exception.printStackTrace(); 134 } 135 } 136 137 } 138 } 139 140 private final class SaveItem implements ActionListener { 141 public void actionPerformed(ActionEvent e) { 142 FileDialog fileDialog = new FileDialog(mainWindow, "Save plan", FileDialog.SAVE); 143 fileDialog.setVisible(true); 144 String fileName = fileDialog.getDirectory() + System.getProperty("file.separator") + fileDialog.getFile(); 145 if (fileName != null) { 146 147 try { 148 149 File file = new File(fileName); 150 boolean canCreate = file.createNewFile(); 151 if (!canCreate) { 152 System.err.println("Couldn't create file " + fileName); 153 } 154 ObjectOutput os = new ObjectOutputStream(new FileOutputStream(file)); 155 /** 156 * Write the commanders first so they are available when the 157 * activities are loaded back 158 */ 159 os.writeObject(ReferenceHolder.commanders); 160 161 DefaultGraphModel model = (DefaultGraphModel) graph.getGraphModel(); 162 List activityNodes = model.getNodes(); 163 for (Object object : activityNodes) { 164 if (object instanceof ActivityNode) { 165 ActivityNode activityNode = (ActivityNode) object; 166 activityNode.preSave(); 167 } 168 } 169 os.writeObject(model); 170 os.writeObject(ReferenceHolder.organizationModel.getRoot()); 171 172 } catch (FileNotFoundException e1) { 173 // TODO Auto-generated catch block 174 e1.printStackTrace(); 175 } catch (IOException e1) { 176 // TODO Auto-generated catch block 177 e1.printStackTrace(); 178 } 179 } 180 } 181 } 182 183 private static class ActivityAddItem extends MenuItem implements ActionListener { 184 185 /** 186 * 187 */ 188 private static final long serialVersionUID = 1L; 189 190 /** 191 * This is used to create subsequent keyboard shortcut entries 192 */ 193 private static int[] keyboardShortcuts = new int[] { KeyEvent.VK_1, 194 KeyEvent.VK_2, 195 KeyEvent.VK_3, 196 KeyEvent.VK_4, 197 KeyEvent.VK_5, 198 KeyEvent.VK_6, 199 KeyEvent.VK_7, 200 KeyEvent.VK_8, 201 KeyEvent.VK_9 }; 202 203 private static int index = 0; 204 205 private Class activityClass; 206 207 private TaskView graphView; 208 209 public ActivityAddItem(TaskView g, Class activityClass, String label) throws HeadlessException { 210 super(label); 211 this.graphView = g; 212 this.activityClass = activityClass; 213 addActionListener(this); 214 if (index < 10) { 215 setShortcut(new MenuShortcut(keyboardShortcuts[index++])); 216 } // We could add Shift and Alt-modifiers to increase the list of 217 // shortcuts if necessary 218 219 } 220 221 public void actionPerformed(ActionEvent e) { 222 graphView.createNode(activityClass); 223 } 224 225 } 226 227 private JGraph graph; 228 229 private JFrame mainWindow; 230 231 private Container pane; 232 233 private JPanel activityPane; 234 235 private JLabel defaultLabel; 236 237 protected Vector selection = new Vector(); 238 239 private JPanel southPane; 240 241 /** 242 * This constructor is to be used from within the planning tool 243 */ 244 245 public TaskView() { 246 setLayout(new BorderLayout()); 247 ActivityEditor activityEditor = new ActivityEditor(ReferenceHolder.getGraphModel(), null); 248 activityEditor.addRenderingListener(this); 249 this.graph = new JGraph(activityEditor); 250 // graph.getEditor().setGridHidden(true); 251 252 defaultLabel = new JLabel("Select an activity to display its parameters"); 253 GraphSelectionListener activityPaneUpdater = new GraphSelectionListener() { 254 255 /** 256 * Update the activity panel if there is one node selected and it is 257 * an activity fig node 258 */ 259 public void selectionChanged(GraphSelectionEvent gse) { 260 selection = gse.getSelections(); 261 activityPane.removeAll(); 262 if (selection.size() == 1 && selection.get(0) instanceof ActivityFigNode) { 263 ActivityFigNode activityFigNode = (ActivityFigNode) selection.get(0); 264 activityPane.add(getActivityPanel(activityFigNode.getActivityNode().getActivity())); 265 266 } else { 267 activityPane.add(defaultLabel); 268 } 269 activityPane.revalidate(); 270 activityPane.repaint(); 271 } 272 273 }; 274 275 TaskView.addGraphSelectionListener(graph, activityPaneUpdater); 276 Activity.addNameChangeListener(new NameChangeListener() { 277 278 public void nameChanged() { 279 graph.revalidate(); 280 graph.repaint(); 281 } 282 283 }); 284 this.activityPane = new JPanel(new GridLayout(0, 1)); 285 286 Rectangle bounds = new Rectangle(10, 10, 500, 500); 287 LayerManager lm = graph.getEditor().getLayerManager(); 288 Dimension preferredSize = new Dimension(500, 500); 289 graph.setDrawingSize(preferredSize); 290 graph.setPreferredSize(preferredSize); 291 LayerPerspective perspective = (LayerPerspective) lm.getActiveLayer(); 292 perspective.addNodeTypeRegion(ActivityNode.class, bounds); 293 294 mainWindow = ReferenceHolder.topFrame; 295 addFileMenu(); 296 addActivityMenu(); 297 addEditMenu(); 298 add(graph, BorderLayout.CENTER); 299 southPane = new JPanel(new BorderLayout()); 300 southPane.add(activityPane, BorderLayout.CENTER); 301 southPane.add(critiqueComponent.getPanel(), BorderLayout.SOUTH); 302 303 add(southPane, BorderLayout.SOUTH); 304 } 305 306 private void addEditMenu() { 307 Menu editMenu = new Menu("Edit"); 308 mainWindow.getMenuBar().add(editMenu); 309 MenuItem deleteItem = new MenuItem("Delete selected"); 310 editMenu.add(deleteItem); 311 deleteItem.addActionListener(new ActionListener() { 312 313 public void actionPerformed(ActionEvent e) { 314 if (selection.size() == 1) { 315 // There is something to remove.. 316 Object elt = selection.get(0); 317 if (elt instanceof OrderingFigEdge) { 318 OrderingFigEdge figEdge = (OrderingFigEdge) elt; 319 OrderingEdge edge = (OrderingEdge) figEdge.getOwner(); 320 321 edge.deleteFromModel(); 322 } else if (elt instanceof InheritanceFigEdge) { 323 InheritanceFigEdge figEdge = (InheritanceFigEdge) elt; 324 InheritanceEdge edge = (InheritanceEdge) figEdge.getOwner(); 325 326 edge.deleteFromModel(); 327 } else if (elt instanceof ActivityFigNode) { 328 ActivityFigNode figNode = (ActivityFigNode) elt; 329 ActivityNode activityNode = (ActivityNode) figNode.getOwner(); 330 activityNode.deleteFromModel(); 331 } else { 332 System.err.println("Currently unsupported operation to remove " + elt.getClass() 333 .getSimpleName()); 334 } 335 336 } 337 } 338 339 }); 340 deleteItem.setShortcut(new MenuShortcut(KeyEvent.VK_DELETE, false)); 341 } 342 343 /** 344 * Use this constructor for stand-alone tests of the graph view 345 */ 346 347 public TaskView(JFrame mainWindow) { 348 this.graph = new JGraph(); 349 graph.setBounds(new Rectangle(10, 10, 300, 300)); 350 LayerManager lm = graph.getEditor().getLayerManager(); 351 352 LayerPerspective perspective = (LayerPerspective) lm.getActiveLayer(); 353 perspective.addNodeTypeRegion(ActivityNode.class, new Rectangle(10, 10, 300, 300)); 354 355 this.mainWindow = mainWindow; 356 initStandaloneMenu(); 357 pane = mainWindow.getContentPane(); 358 pane.add(graph, BorderLayout.CENTER); 359 mainWindow.setSize(300, 300); 360 mainWindow.setVisible(true); 361 } 362 363 public static void addGraphSelectionListener(JGraph graph, GraphSelectionListener l) { 364 graph.getEditor().getSelectionManager().addGraphSelectionListener(l); 365 } 366 367 private void initStandaloneMenu() { 368 MenuBar mb = new MenuBar(); 369 mainWindow.setMenuBar(mb); 370 addActivityMenu(); 371 addFileMenu(); 372 373 } 374 375 /** 376 * 377 * This is used instead of DefaultGraphModel.removeAll which does not seem 378 * to behave as expected. 379 * 380 */ 381 private void deleteNodes(DefaultGraphModel oldModel) { 382 List nodes = oldModel.getNodes(); 383 int size = nodes.size(); 384 for (int i = 0; i < size; i++) { 385 Object object = nodes.get(0); 386 if (object instanceof NetNode) { 387 NetNode node = (NetNode) object; 388 node.deleteFromModel(); 389 } else { 390 System.err.println("Fishy: node is not netnode: " + object); 391 } 392 } 393 394 } 395 396 /** 397 * 398 * @see deleteNodes 399 */ 400 401 private void deleteEdges(DefaultGraphModel oldModel) { 402 List edges = oldModel.getEdges(); 403 int size = edges.size(); 404 for (int i = 0; i < size; i++) { 405 Object object = edges.get(0); 406 if (object instanceof NetEdge) { 407 NetEdge edge = (NetEdge) object; 408 edge.deleteFromModel(); 409 } else { 410 System.err.println("Fishy: edge is not NetEdge: " + object); 411 } 412 } 413 } 414 415 private JPanel getActivityPanel(Activity a) { 416 417 /** 418 * An activity can also have conditions, or? 419 * 420 */ 421 GridLayout layout = new GridLayout(0, 1); 422 JPanel activityPane = new JPanel(layout); 423 activityPane.setBorder(BorderFactory.createTitledBorder("Create an activity")); 424 activityPane.add(ActivityUtils.getNameParameter(a).getComponent()); 425 for (ActivityParameter param : a.getParams()) { 426 if (param instanceof AbstractTaskViewParameter) { 427 TaskViewParameter taskParam = (TaskViewParameter) param; 428 activityPane.add(taskParam.getComponent()); 429 } 430 } 431 return activityPane; 432 433 } 434 435 public Rectangle getActivityArea(Activity a) { 436 return TaskView.getActivityArea(graph, a); 437 } 438 439 public static Rectangle getActivityArea(JGraph graph, Activity a) { 440 LayerPerspective layer = (LayerPerspective) graph.getEditor().getLayerManager().getActiveLayer(); 441 List figNodes = layer.getContentsNoEdges(); 442 Rectangle bounds = null; 443 for (Object object : figNodes) { 444 if (object instanceof ActivityFigNode) { 445 ActivityFigNode figNode = (ActivityFigNode) object; 446 if (figNode.getActivityNode().getActivity().equals(a)) { 447 bounds = figNode.getBounds(); 448 break; 449 } 450 } else { 451 System.err.println("Wrong type of node: " + object.getClass()); 452 } 453 } 454 return bounds; 455 } 456 457 private void addFileMenu() { 458 MenuBar mb = mainWindow.getMenuBar(); 459 Menu fileMenu = new Menu("File"); 460 mb.add(fileMenu); 461 MenuItem saveItem = new MenuItem("Save"); 462 saveItem.setShortcut(new MenuShortcut(KeyEvent.VK_S)); 463 saveItem.addActionListener(new SaveItem()); 464 fileMenu.add(saveItem); 465 MenuItem openItem = new MenuItem("Open"); 466 openItem.setShortcut(new MenuShortcut(KeyEvent.VK_O)); 467 fileMenu.add(openItem); 468 openItem.addActionListener(new OpenItem()); 469 470 MenuItem resetItem = new MenuItem("Reset graph"); 471 fileMenu.add(resetItem); 472 resetItem.addActionListener(new ResetItem()); 473 474 } 475 476 private void addActivityMenu() { 477 MenuBar mb = mainWindow.getMenuBar(); 478 Menu activityMenu = new Menu("Activity menu"); 479 mb.add(activityMenu); 480 for (Class activityClass : ReferenceHolder.activityClasses) { 481 activityMenu.add(new ActivityAddItem(this, activityClass, "Add " + activityClass.getSimpleName())); 482 } 483 } 484 485 private void createNode(Class activityClass) { 486 GraphModel model = graph.getGraphModel(); 487 if (model instanceof DefaultGraphModel) { 488 DefaultGraphModel defaultGraphModel = (DefaultGraphModel) model; 489 ActivityNode activityNode = new ActivityNode(activityClass); 490 activityNode.initialize(null); 491 defaultGraphModel.addNode(activityNode); 492 } else { 493 System.err.println("Wrong graph model used!"); 494 } 495 } 496 497 /** 498 * These methods are replacements for JGraph.setModel which doesn't seem to 499 * behave as expected 500 * 501 */ 502 private void addEdges(DefaultGraphModel model, DefaultGraphModel oldModel) { 503 List edges = model.getEdges(); 504 for (Object object : edges) { 505 if (object instanceof NetEdge) { 506 NetEdge edge = (NetEdge) object; 507 oldModel.addEdge(edge); 508 } else { 509 System.err.println("Edge to add is not NetEdge: " + object); 510 } 511 } 512 } 513 514 private void addNodes(DefaultGraphModel model, DefaultGraphModel oldModel) { 515 List nodes = model.getNodes(); 516 for (Object object : nodes) { 517 if (object instanceof ActivityNode) { 518 ActivityNode node = (ActivityNode) object; 519 oldModel.addNode(node); 520 node.postLoad(); 521 } else { 522 System.err.println("Node to add is not an ActivityNode: " + object); 523 } 524 } 525 } 526 527 /** 528 * @param args 529 */ 530 public static void main(String[] args) { 531 new TaskView(new JFrame()); 532 } 533 534 public List<Activity> getEvaluationActivities() { 535 return Utils.toVector(ReferenceHolder.allActivities); 536 } 537 538 /** 539 * 540 * Notify critics that the view has been updated. 541 * 542 * @see se.liu.ida.critiquer.gui.graph.GraphRenderingListener#graphRendered() 543 */ 544 public void graphRendered() { 545 updateView((Graphics2D) getGraphics()); 546 /** 547 * The editor pane may stretch below the surface of the graph so we may 548 * interfere with the south pane when drawing around activities, 549 * therefore it needs to be redrawn. This could be fixed by letting the 550 * activity areas of this panel be relative to the graph and sending 551 * updateView the graphics context of the graph instead of the view, 552 * just as in the time view. This has the disadvantage that we cannot 553 * draw between activities and their parameters however.. 554 * 555 */ 556 southPane.repaint(); 557 } 558 559 }