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    }