PageRenderTime 25ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/release-4_4_2/src/groove/gui/JGraphPanel.java

https://bitbucket.org/wthys/groove
Java | 419 lines | 239 code | 37 blank | 143 comment | 28 complexity | 35e7788da1b42823d9001ec777c114b1 MD5 | raw file
  1. /*
  2. * GROOVE: GRaphs for Object Oriented VErification Copyright 2003--2007
  3. * University of Twente
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  6. * use this file except in compliance with the License. You may obtain a copy of
  7. * the License at http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. * License for the specific language governing permissions and limitations under
  13. * the License.
  14. *
  15. * $Id: JGraphPanel.java,v 1.20 2008-01-30 09:33:36 iovka Exp $
  16. */
  17. package groove.gui;
  18. import static groove.gui.jgraph.JGraphMode.PAN_MODE;
  19. import groove.gui.jgraph.GraphJGraph;
  20. import groove.gui.jgraph.GraphJModel;
  21. import groove.util.Pair;
  22. import groove.view.aspect.AspectGraph;
  23. import java.awt.BorderLayout;
  24. import java.awt.Color;
  25. import java.awt.Component;
  26. import java.awt.Dimension;
  27. import java.awt.event.ItemEvent;
  28. import java.awt.event.ItemListener;
  29. import java.beans.PropertyChangeEvent;
  30. import java.beans.PropertyChangeListener;
  31. import java.util.LinkedList;
  32. import java.util.List;
  33. import javax.accessibility.AccessibleState;
  34. import javax.swing.Box;
  35. import javax.swing.JComponent;
  36. import javax.swing.JLabel;
  37. import javax.swing.JMenuItem;
  38. import javax.swing.JPanel;
  39. import javax.swing.JScrollPane;
  40. import javax.swing.JSplitPane;
  41. import javax.swing.JToolBar;
  42. import org.jgraph.JGraph;
  43. import org.jgraph.event.GraphSelectionEvent;
  44. import org.jgraph.event.GraphSelectionListener;
  45. /**
  46. * A panel that combines a {@link groove.gui.jgraph.GraphJGraph}and (optionally) a
  47. * {@link groove.gui.LabelTree}.
  48. *
  49. * @author Arend Rensink, updated by Carel van Leeuwen
  50. * @version $Revision$
  51. */
  52. public class JGraphPanel<JG extends GraphJGraph> extends JPanel {
  53. /**
  54. * Constructs a view upon a given jgraph, possibly with a status bar.
  55. *
  56. * @param jGraph the jgraph on which this panel is a view
  57. * @param withStatusBar <tt>true</tt> if a status bar should be added to the
  58. * panel
  59. * @ensure <tt>getJGraph() == jGraph</tt>
  60. */
  61. public JGraphPanel(JG jGraph, boolean withStatusBar) {
  62. super(false);
  63. this.simulatorModel = jGraph.getSimulatorModel();
  64. setFocusable(false);
  65. setFocusCycleRoot(true);
  66. // right now we always want label panels; keep this option
  67. this.jGraph = jGraph;
  68. this.options = jGraph.getOptions();
  69. this.statusBar = withStatusBar ? new JLabel(" ") : null;
  70. }
  71. /**
  72. * Initialises the GUI.
  73. * Should be called immediately after the constructor.
  74. */
  75. public void initialise() {
  76. // a JGraphPanel consists of an optional tool bar,
  77. // a main pane containing the graph, label tree and (possibly)
  78. // error panel, and an optional status bar.
  79. setLayout(new BorderLayout());
  80. add(createMainPane());
  81. if (this.statusBar != null) {
  82. add(this.statusBar, BorderLayout.SOUTH);
  83. }
  84. installListeners();
  85. setEnabled(false);
  86. }
  87. /** Callback method that adds the required listeners to this panel. */
  88. protected void installListeners() {
  89. addRefreshListener(Options.SHOW_BACKGROUND_OPTION);
  90. getJGraph().addGraphSelectionListener(new GraphSelectionListener() {
  91. @Override
  92. public void valueChanged(GraphSelectionEvent e) {
  93. getLabelTree().clearSelection();
  94. }
  95. });
  96. getJGraph().addJGraphModeListener(new PropertyChangeListener() {
  97. @Override
  98. public void propertyChange(PropertyChangeEvent evt) {
  99. getScrollPane().setWheelScrollingEnabled(
  100. evt.getNewValue() != PAN_MODE);
  101. }
  102. });
  103. }
  104. /** Component on which the graph and the label tree are displayed. */
  105. protected JComponent createMainPane() {
  106. // set up the split editor pane
  107. JSplitPane result =
  108. new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, createGraphPane(),
  109. createLabelPane());
  110. result.setOneTouchExpandable(true);
  111. result.setResizeWeight(1.0);
  112. result.setBorder(null);
  113. return result;
  114. }
  115. /** Creates the right hand side label pane. */
  116. protected JComponent createLabelPane() {
  117. JPanel result = new JPanel(new BorderLayout(), false);
  118. Box labelPaneTop = Box.createVerticalBox();
  119. JLabel labelPaneTitle =
  120. new JLabel(" " + Options.LABEL_PANE_TITLE + " ");
  121. labelPaneTitle.setAlignmentX(LEFT_ALIGNMENT);
  122. labelPaneTop.add(labelPaneTitle);
  123. JToolBar labelTreeToolbar = getLabelTree().createToolBar();
  124. if (labelTreeToolbar != null) {
  125. labelTreeToolbar.setAlignmentX(LEFT_ALIGNMENT);
  126. labelPaneTop.add(labelTreeToolbar);
  127. }
  128. result.add(labelPaneTop, BorderLayout.NORTH);
  129. result.add(createLabelScrollPane(getLabelTree()), BorderLayout.CENTER);
  130. return result;
  131. }
  132. /** Creates a scroll pane of the right dimensions around a given component. */
  133. protected JScrollPane createLabelScrollPane(Component component) {
  134. return new JScrollPane(component) {
  135. @Override
  136. public Dimension getMinimumSize() {
  137. return new Dimension(MINIMUM_LABEL_PANE_WIDTH, 0);
  138. }
  139. };
  140. }
  141. /**
  142. * Creates and returns a new pane, on which only the jgraph is shown (and no
  143. * label list).
  144. */
  145. protected JComponent createGraphPane() {
  146. // set up the real editor pane
  147. JScrollPane result = getScrollPane();
  148. result.setDoubleBuffered(false);
  149. result.setPreferredSize(new Dimension(500, 400));
  150. return result;
  151. }
  152. /**
  153. * Lazily creates and returns the scroll pane within which the {@link JGraph}
  154. * is displayed.
  155. */
  156. protected JScrollPane getScrollPane() {
  157. if (this.scrollPane == null) {
  158. this.scrollPane = new JScrollPane(getJGraph());
  159. this.scrollPane.getVerticalScrollBar().setUnitIncrement(10);
  160. }
  161. return this.scrollPane;
  162. }
  163. /**
  164. * Returns the underlying {@link GraphJGraph}.
  165. */
  166. public JG getJGraph() {
  167. return this.jGraph;
  168. }
  169. /** Returns the label tree displayed on this panel. */
  170. public LabelTree getLabelTree() {
  171. return getJGraph().getLabelTree();
  172. }
  173. /**
  174. * Changes the graph model displayed in this panel.
  175. * Also enables the panel if the new model is not {@code null},
  176. * and disables it otherwise.
  177. */
  178. public void setJModel(GraphJModel<?,?> jModel) {
  179. if (jModel == getJModel()) {
  180. getJGraph().getGraphLayoutCache().reload();
  181. getJGraph().repaint();
  182. } else {
  183. getJGraph().setModel(jModel);
  184. }
  185. boolean enabled = jModel != null;
  186. setEnabled(enabled);
  187. refreshStatus();
  188. }
  189. /**
  190. * Returns the underlying {@link GraphJModel}, or <code>null</code> if the jgraph
  191. * is currently disabled.
  192. */
  193. public GraphJModel<?,?> getJModel() {
  194. if (isEnabled()) {
  195. return this.jGraph.getModel();
  196. } else {
  197. return null;
  198. }
  199. }
  200. /**
  201. * Returns the graph that is currently selected (either one of the
  202. * grammar view's host graphs, or a state).
  203. */
  204. public AspectGraph getGraph() {
  205. return getJModel() == null ? null
  206. : (AspectGraph) getJModel().getGraph();
  207. }
  208. /**
  209. * Returns the status bar of this panel, if any.
  210. */
  211. private JLabel getStatusBar() {
  212. return this.statusBar;
  213. }
  214. /**
  215. * Delegates the method to the content pane and to super.
  216. * Also sets the background appropriately.
  217. * @see #getEnabledBackground()
  218. */
  219. @Override
  220. public void setEnabled(boolean enabled) {
  221. this.jGraph.setEnabled(enabled);
  222. getLabelTree().setEnabled(enabled);
  223. if (getStatusBar() != null) {
  224. getStatusBar().setEnabled(enabled);
  225. }
  226. super.setEnabled(enabled);
  227. Color background = enabled ? getEnabledBackground() : null;
  228. getJGraph().setBackground(background);
  229. getLabelTree().setBackground(background);
  230. }
  231. /** Callback method to return the background colour in case the panel is enabled.
  232. * This is {@link Color#WHITE} by default, but may be changed by a call to
  233. * #setEnabledBackgound.
  234. * The background colour for an enabled panel; non-{@code null}
  235. */
  236. protected Color getEnabledBackground() {
  237. return this.enabledBackground;
  238. }
  239. /** Sets the background colour for an enabled panel. */
  240. protected void setEnabledBackground(Color enabledBackground) {
  241. this.enabledBackground = enabledBackground;
  242. }
  243. /**
  244. * Readies the panel for garbage collection, in particular unregistering all
  245. * listeners.
  246. */
  247. public void dispose() {
  248. removeOptionListeners();
  249. }
  250. /**
  251. * Adds a refresh listener to the menu item associated with for an option
  252. * with a given name.
  253. * @see #getRefreshListener()
  254. */
  255. protected void addRefreshListener(String option) {
  256. addOptionListener(option, getRefreshListener());
  257. }
  258. /**
  259. * Returns the refresh listener for this panel. Lazily creates the listener.
  260. */
  261. protected final RefreshListener getRefreshListener() {
  262. if (this.refreshListener == null) {
  263. this.refreshListener = new RefreshListener();
  264. }
  265. return this.refreshListener;
  266. }
  267. /**
  268. * Adds a listener to the menu item associated with for an option with a
  269. * given name. Throws an exception if no such option was in the options
  270. * object passed in at construction time.
  271. */
  272. private void addOptionListener(String option, RefreshListener listener) {
  273. JMenuItem optionItem = getOptionsItem(option);
  274. if (optionItem == null) {
  275. throw new IllegalArgumentException(String.format(
  276. "Unknown option: %s", option));
  277. }
  278. optionItem.addItemListener(listener);
  279. optionItem.addPropertyChangeListener(listener);
  280. this.listeners.add(new Pair<JMenuItem,ItemListener>(optionItem,
  281. listener));
  282. }
  283. /**
  284. * Removes all listeners added by
  285. * {@link #addOptionListener(String, RefreshListener)}.
  286. */
  287. private void removeOptionListeners() {
  288. for (Pair<JMenuItem,ItemListener> record : this.listeners) {
  289. record.one().removeItemListener(record.two());
  290. }
  291. this.listeners.clear();
  292. }
  293. /**
  294. * Refreshes everything on the panel, for instance in reaction to a change
  295. * in one of the visualisation options. This implementation calls
  296. * {@link GraphJGraph#refreshAllCells()} and {@link #refreshStatus()}.
  297. */
  298. protected void refresh() {
  299. getJGraph().refreshAllCells();
  300. getJGraph().setEnabled(getJModel() != null);
  301. getLabelTree().updateModel();
  302. refreshStatus();
  303. }
  304. /**
  305. * If the panel has a status bar, refreshes it with the text obtained from
  306. * {@link #getStatusText()}.
  307. */
  308. protected void refreshStatus() {
  309. if (getStatusBar() != null) {
  310. getStatusBar().setText(getStatusText());
  311. }
  312. }
  313. /**
  314. * Callback method from {@link #refreshStatus()} to obtain the current
  315. * status text, which is to be printed on the status bar (if any). This
  316. * implementation returns the empty string.
  317. */
  318. protected String getStatusText() {
  319. return "";
  320. }
  321. /**
  322. * Returns the options object passed in at construction time.
  323. */
  324. protected final Options getOptions() {
  325. return this.options;
  326. }
  327. /**
  328. * Retrieves the options item for a given option name, creating it first if
  329. * necessary.
  330. */
  331. protected JMenuItem getOptionsItem(String option) {
  332. Options options = getOptions();
  333. return options == null ? null : options.getItem(option);
  334. }
  335. /** Convenience method to retrieve the simulator model. */
  336. protected final SimulatorModel getSimulatorModel() {
  337. return this.simulatorModel;
  338. }
  339. private final SimulatorModel simulatorModel;
  340. /**
  341. * The {@link GraphJGraph}on which this panel provides a view.
  342. */
  343. private final JG jGraph;
  344. /** The background colour in case the panel is enabled. */
  345. private Color enabledBackground = Color.WHITE;
  346. /** Options for this panel. */
  347. private final Options options;
  348. /** Change listener that calls {@link #refresh()} when activated. */
  349. private RefreshListener refreshListener;
  350. /**
  351. * Panel for showing status messages
  352. */
  353. private final JLabel statusBar;
  354. private final List<Pair<JMenuItem,ItemListener>> listeners =
  355. new LinkedList<Pair<JMenuItem,ItemListener>>();
  356. /**
  357. * The scroll pane in which the JGraph is displayed.
  358. */
  359. private JScrollPane scrollPane;
  360. /**
  361. * The minimum width of the label pane. If the label list is empty, the
  362. * preferred width is set to the minimum width.
  363. */
  364. public final static int MINIMUM_LABEL_PANE_WIDTH = 100;
  365. private class RefreshListener implements ItemListener,
  366. PropertyChangeListener {
  367. public void itemStateChanged(ItemEvent e) {
  368. if (isEnabled()) {
  369. refresh();
  370. }
  371. }
  372. @Override
  373. public void propertyChange(PropertyChangeEvent evt) {
  374. if (evt.getPropertyName().equals(
  375. AccessibleState.ENABLED.toDisplayString())) {
  376. if (isEnabled()) {
  377. refresh();
  378. }
  379. }
  380. }
  381. }
  382. }