fiore@0: /* fiore@0: CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool fiore@3: fiore@0: Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com) fiore@0: Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) fiore@0: fiore@0: This program is free software: you can redistribute it and/or modify fiore@0: it under the terms of the GNU General Public License as published by fiore@0: the Free Software Foundation, either version 3 of the License, or fiore@0: (at your option) any later version. fiore@0: fiore@0: This program is distributed in the hope that it will be useful, fiore@0: but WITHOUT ANY WARRANTY; without even the implied warranty of fiore@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fiore@0: GNU General Public License for more details. fiore@0: fiore@0: You should have received a copy of the GNU General Public License fiore@0: along with this program. If not, see . fiore@3: */ fiore@0: fiore@0: package uk.ac.qmul.eecs.ccmi.gui; fiore@0: fiore@0: import java.awt.BorderLayout; fiore@3: import java.io.IOException; fiore@0: fiore@0: import javax.swing.JPanel; fiore@0: import javax.swing.JScrollPane; fiore@0: import javax.swing.JSplitPane; fiore@0: import javax.swing.event.ChangeEvent; fiore@0: import javax.swing.event.ChangeListener; fiore@0: fiore@3: import uk.ac.qmul.eecs.ccmi.gui.awareness.AwarenessPanel; fiore@3: import uk.ac.qmul.eecs.ccmi.gui.awareness.DisplayFilter; fiore@3: import uk.ac.qmul.eecs.ccmi.network.NetDiagram; fiore@3: fiore@0: /** fiore@0: * It's the panel which displays a diagram. It contains a {@link GraphPanel}, a {@link DiagramTree} fiore@3: * a {@link GraphToolbar} and the {@code AwarenessPanel}. fiore@3: * It's backed up by an instance of {@code Diagram}. fiore@0: */ fiore@0: @SuppressWarnings("serial") fiore@0: public class DiagramPanel extends JPanel{ fiore@0: fiore@3: /** fiore@3: * Creates a new instance of {@code DiagramPanel} holding the diagram passed as argument. fiore@3: * fiore@3: * @param diagram the diagram this panel is backed up by fiore@3: * @param tabbedPane the tabbed pane this DiagramPanel will be added to. This reference fiore@3: * is used to updated the tab label when the diagram is modified or save (in the former fiore@3: * case a star is added to the label, in the latter case the star is removed) fiore@3: */ fiore@3: public DiagramPanel(Diagram diagram, EditorTabbedPane tabbedPane){ fiore@3: this.diagram = diagram; fiore@3: this.tabbedPane = tabbedPane; fiore@0: fiore@3: setName(diagram.getLabel()); fiore@3: setLayout(new BorderLayout()); fiore@0: fiore@3: modelChangeListener = new ChangeListener(){ fiore@3: @Override fiore@3: public void stateChanged(ChangeEvent e) { fiore@3: setModified(true); fiore@3: } fiore@3: }; fiore@3: fiore@3: toolbar = new GraphToolbar(diagram); fiore@3: graphPanel = new GraphPanel(diagram, toolbar); fiore@3: /* the focus must be hold by the tree and the tab panel only */ fiore@3: toolbar.setFocusable(false); fiore@3: graphPanel.setFocusable(false); fiore@3: fiore@3: tree = new DiagramTree(diagram); fiore@3: fiore@3: /* the panel containing the graph and the toolbar and the awareness panel */ fiore@3: visualPanel = new JPanel(new BorderLayout()); fiore@3: visualPanel.add(toolbar, BorderLayout.NORTH); fiore@3: visualPanel.add(new JScrollPane(graphPanel),BorderLayout.CENTER); fiore@3: awarenessSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); fiore@3: awarenessSplitPane.setTopComponent(visualPanel); fiore@3: fiore@3: /* divides the tree from the visual diagram */ fiore@3: JSplitPane treeSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, fiore@3: new JScrollPane(tree), fiore@3: awarenessSplitPane); fiore@3: treeSplitPane.setDividerLocation((int)tree.getPreferredSize().width*2); fiore@3: add(treeSplitPane, BorderLayout.CENTER); fiore@3: diagram.getCollectionModel().addChangeListener(modelChangeListener); fiore@3: } fiore@3: fiore@3: /** fiore@3: * When a diagram is saved on the file system the its path is associated to the diagram panel fiore@3: * and it's shown when the user hover on the its tab title. fiore@3: * fiore@3: * @return the path of the file where this diagram has been saved last time or {@code null} fiore@3: */ fiore@3: public String getFilePath(){ fiore@3: return filePath; fiore@3: } fiore@3: fiore@3: /** fiore@3: * Sets the file path to a new path. This method should be called after the backing diagram has fiore@3: * been saved to a file. fiore@3: * fiore@3: * @param newValue the path of the file where the backing diagram has been saved last time fiore@3: */ fiore@3: public void setFilePath(String newValue){ fiore@3: filePath = newValue; fiore@3: } fiore@3: fiore@3: /** fiore@3: * Returns a reference to the backing diagram of this diagram panel. fiore@3: * fiore@3: * @return a reference to the backing diagram of this diagram panel fiore@3: */ fiore@3: public Diagram getDiagram(){ fiore@3: return diagram; fiore@3: } fiore@3: fiore@3: /** fiore@3: * Enables or disables the awareness panel of this diagram panel. As default the awareness panel fiore@3: * is disabled but if the diagram is shared (either on a local or on a remote server) the awareness fiore@3: * panel gets enabled. In fact, from now on, awareness messages will be received from the server and, fiore@3: * even if the awareness panel is not visible, some messages (username messages) fiore@3: * will still have to be taken into account. fiore@3: * fiore@3: * @param enabled {@code true} if the panel is to be enabled, {@code false} otherwise. fiore@3: */ fiore@3: public void setAwarenessPanelEnabled(boolean enabled){ fiore@3: if(!(diagram instanceof NetDiagram)) fiore@3: return; fiore@3: /* if the display filter has not been created yet, do create it */ fiore@3: DisplayFilter filter = DisplayFilter.getInstance(); fiore@3: if(filter == null) fiore@3: try{ fiore@3: filter = DisplayFilter.createInstance(); fiore@3: }catch(IOException ioe){ fiore@3: SpeechOptionPane.showMessageDialog(this, ioe.getLocalizedMessage()); fiore@3: return; fiore@3: } fiore@3: fiore@3: NetDiagram netDiagram = (NetDiagram)diagram; fiore@3: if(enabled){ fiore@3: awarenessPanel = new AwarenessPanel(diagram.getName()); fiore@3: awarenessPanelScrollPane = new JScrollPane(awarenessPanel); fiore@3: netDiagram.enableAwareness(awarenessPanel); fiore@3: if(awarenessPanelListener != null) fiore@3: awarenessPanelListener.awarenessPanelEnabled(true); fiore@3: }else{ //disabled fiore@3: netDiagram.disableAwareness(awarenessPanel); fiore@3: if(awarenessSplitPane.getRightComponent() != null){ fiore@3: // hide the panel fiore@3: awarenessSplitPane.remove(awarenessPanelScrollPane); fiore@3: } fiore@3: awarenessPanelScrollPane = null; fiore@3: awarenessPanel = null; fiore@3: awarenessSplitPane.validate(); fiore@3: if(awarenessPanelListener != null) fiore@3: awarenessPanelListener.awarenessPanelEnabled(false); fiore@3: } fiore@3: } fiore@3: fiore@3: /** fiore@3: * Makes the awareness panel visible or invisible, assuming that it has been enabled beforehand. If the fiore@3: * awareness panel hasn't been enables this call has no effect. fiore@3: * fiore@3: * @param visible {@code true} if the panel is to be made visible, {@code false} otherwise. fiore@3: */ fiore@3: public void setAwarenessPanelVisible(boolean visible){ fiore@3: if(awarenessPanelScrollPane == null) fiore@3: return; fiore@3: if(visible){ fiore@3: awarenessSplitPane.setRightComponent(awarenessPanelScrollPane); fiore@3: awarenessSplitPane.setDividerLocation(0.8); fiore@3: awarenessSplitPane.setResizeWeight(1.0); fiore@3: awarenessSplitPane.validate(); fiore@3: if(awarenessPanelListener != null) fiore@3: awarenessPanelListener.awarenessPanelVisible(true); fiore@3: }else{ fiore@3: awarenessSplitPane.remove(awarenessPanelScrollPane); fiore@3: awarenessSplitPane.validate(); fiore@3: if(awarenessPanelListener != null) fiore@3: awarenessPanelListener.awarenessPanelVisible(false); fiore@3: } fiore@3: } fiore@3: fiore@3: /** fiore@3: * Queries the diagram panel on whether the awareness panel is currently visible. fiore@3: * fiore@3: * @return {@code true} if the awareness panel is currently visible, {@code false} otherwise. fiore@3: */ fiore@3: public boolean isAwarenessPanelVisible(){ fiore@3: return (awarenessSplitPane.getRightComponent() != null); fiore@3: } fiore@3: fiore@3: /** fiore@3: * Returns a reference to the inner awareness panel. fiore@3: * fiore@3: * @return the inner awareness panel if it has been enabled of {@code null} otherwise fiore@3: */ fiore@3: public AwarenessPanel getAwarenessPanel(){ fiore@3: return awarenessPanel; fiore@3: } fiore@3: fiore@3: /** fiore@5: * Sets the backing up delegate diagram for this panel. This method is used when a diagram is shared fiore@3: * (or reverted). A shared diagram has a different way of updating the fiore@3: * The modified status is changed according to fiore@3: * the modified status of the {@code DiagramModel} internal to the new {@code Diagram} fiore@3: * fiore@5: * @param diagram the backing up delegate diagram fiore@3: */ fiore@3: public void setDiagram(Diagram diagram){ fiore@3: /* remove the listener from the old model */ fiore@3: this.diagram.getCollectionModel().removeChangeListener(modelChangeListener); fiore@3: diagram.getCollectionModel().addChangeListener(modelChangeListener); fiore@3: fiore@3: this.diagram = diagram; fiore@3: tree.setDiagram(diagram); fiore@3: graphPanel.setModelUpdater(diagram.getModelUpdater()); fiore@3: setName(diagram.getLabel()); fiore@3: /* set the * according to the new diagram's model modification status */ fiore@3: setModified(isModified()); fiore@3: } fiore@3: fiore@3: /** fiore@3: * Returns a reference to the graph panel in this diagram panel. The tree's model is the fiore@3: * model returned by a calling {@code getTreeModel()} on the backing diagram. fiore@3: * fiore@3: * @return the graph panel contained by this diagram panel fiore@3: */ fiore@3: public GraphPanel getGraphPanel(){ fiore@3: return graphPanel; fiore@3: } fiore@3: fiore@3: /** fiore@3: * Returns a reference to the tree in this diagram panel. The graph model is the fiore@3: * model returned by a calling {@code getCollectionModel()} on the backing diagram. fiore@3: * fiore@3: * @return the tree contained by this diagram panel fiore@3: */ fiore@3: public DiagramTree getTree(){ fiore@3: return tree; fiore@3: } fiore@3: fiore@3: /** fiore@3: * Changes the {@code modified} status of the backing diagram of this panel. If set to {@code true} fiore@3: * then a star will appear after the name of the diagram, returned by {@code getName()}. fiore@3: * fiore@3: * When called passing false as argument (which should be done after the diagram is saved on a file) fiore@3: * listeners are notified that the diagram has been saved. fiore@3: * fiore@3: * @param modified {@code true} when the diagram has been modified, {@code false} when it has been saved fiore@3: */ fiore@3: public void setModified(boolean modified){ fiore@3: if(!modified) fiore@3: diagram.getCollectionModel().setUnmodified(); fiore@3: /* add an asterisk to notify that the diagram has changed */ fiore@3: if(modified) fiore@3: setName(getName()+"*"); fiore@3: else fiore@3: setName(diagram.getLabel()); fiore@3: tabbedPane.refreshComponentTabTitle(this); fiore@3: } fiore@3: fiore@3: /** fiore@3: * Whether the backing diagram has been modified. The diagram is modified as a result of changes fiore@3: * to the {@code TreeModel} or {@code CollectionModel} it contains. To change the {@code modified} fiore@3: * status of the diagram (and of its models) {@code setModified()} must be used. fiore@3: * fiore@5: * @return {@code true} if the diagram is modified, {@code false} otherwise fiore@3: */ fiore@3: public boolean isModified(){ fiore@3: return diagram.getCollectionModel().isModified(); fiore@3: } fiore@3: fiore@3: void setAwarenessPanelListener(AwarenessPanelEnablingListener listener){ fiore@3: awarenessPanelListener = listener; fiore@3: } fiore@3: fiore@3: private Diagram diagram; fiore@3: private GraphPanel graphPanel; fiore@3: private JSplitPane awarenessSplitPane; fiore@3: private DiagramTree tree; fiore@3: private JPanel visualPanel; fiore@3: private GraphToolbar toolbar; fiore@3: private AwarenessPanel awarenessPanel; fiore@3: private JScrollPane awarenessPanelScrollPane; fiore@3: private String filePath; fiore@3: private ChangeListener modelChangeListener; fiore@3: private EditorTabbedPane tabbedPane; fiore@3: private AwarenessPanelEnablingListener awarenessPanelListener; fiore@0: } fiore@3: fiore@3: interface AwarenessPanelEnablingListener { fiore@3: public void awarenessPanelEnabled(boolean enabled); fiore@3: public void awarenessPanelVisible(boolean visible); fiore@3: } fiore@3: