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