//============================================================================== // // Copyright (c) 2002- // Authors: // * Andrew Hinton (University of Birmingham) // * Dave Parker (University of Oxford, formerly University of Birmingham) // //------------------------------------------------------------------------------ // // This file is part of PRISM. // // PRISM is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PRISM is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with PRISM; if not, write to the Free Software Foundation, // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //============================================================================== package userinterface; //Java Packages import java.util.*; import java.io.*; import java.net.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.*; import javax.swing.plaf.metal.*; //Prism Packages import prism.*; import userinterface.util.*; /** PRISM Graphical User Interface * This class is the top level class for the PRISM GUI. It acts as a container * for GUIPlugin objects. The method getPluginArray() is defined to allow any * class that implements this interface to be added to the user interface. The aim * of this is to make transferral between different versions of the system easier. * As a top level class, GUIPrism contains some utility methods, that can be used * via the GUIPlugin class. This class is also a container of a Prism object, * which acts as the link between the user interface and the underlying PRISM code. * @author Andrew Hinton * @since 20/10/03 */ public class GUIPrism extends JFrame { //CONSTANTS //defaults /** The default width of the main window */ public static final int DEFAULT_WINDOW_WIDTH = 1024; /** The default height of the main window */ public static final int DEFAULT_WINDOW_HEIGHT = 640; /** The minimum width of the main window */ public static final int MINIMUM_WINDOW_WIDTH = 10; /** The minimum height of the main window. */ public static final int MINIMUM_WINDOW_HEIGHT = 10; //STATIC MEMBERS private static GUIPrismSplash splash; private static GUIPrism gui; private boolean doExit; private static GUIClipboard clipboardPlugin; //STATIC METHODS /** The entry point of the program from the command line. * @param args the command line arguments */ public static void main(String[] args) { try { //Show the splash screen splash = new GUIPrismSplash("images/splash.png"); splash.display(); //new GUIPrismSplash().show(); gui = new GUIPrism(); gui.show(); EventQueue.invokeLater(new GUIPrism.SplashScreenCloser()); gui.passCLArgs(args); } catch(GUIException e) { System.err.println("Error: Could not load the PRISM GUI: "+e.getMessage()); System.exit(1); } catch(PrismException e) { System.err.println("Error: "+e.getMessage()); System.exit(1); } } /** Define this method to add plugins to the GUI. Returns an ArrayList of * Plugin objects. * @param g The instance of GUIPrism to associate with the GUIPlugin objects. * @return An ArrayList of GUIPlugins to be included in the user interface */ public static ArrayList getPluginArray(GUIPrism g) { ArrayList plugs = new ArrayList(); //Define Plugins here plugs.add(new userinterface.GUIFileMenu(g)); // File menu clipboardPlugin = new GUIClipboard(g); plugs.add(clipboardPlugin); // Clipboard plugs.add(new userinterface.model.GUIMultiModel(g)); userinterface.simulator.GUISimulator sim = new userinterface.simulator.GUISimulator(g); plugs.add(new userinterface.properties.GUIMultiProperties(g, sim)); plugs.add(sim); plugs.add(new userinterface.log.GUILog(g)); plugs.add(new userinterface.GUINetwork(g)); return plugs; } public static GUIPrism getGUI() { return gui; } //ATTRIBUTES //properties private Prism prism; private PrismLog theLog; //gui components private ArrayList plugs; private JTabbedPane theTabs; private GUIPlugin logPlug; private GUIEventHandler eventHandle; private GUIOptionsDialog options; private JFileChooser choose; private JProgressBar progress; private GUITaskBar taskbar; private Action prismOptions; private Action fontIncrease; private Action fontDecrease; //CONSTRUCTORS AND INITIALISATION METHODS /** Creates a new instance of GUIPrism. By calling setupResources(), setupPrism() * and then initComponents(). * @throws GUIException Thrown if there is an error in initialising the user interface. */ public GUIPrism() throws GUIException, PrismException { super(); setupResources(); setupPrism(); initComponents(); prism.getSettings().notifySettingsListeners(); } /** * Sets the URL for images properly, so that they are loaded from the * correct directory. This also loads in all of the resouces for * internationalization support. Also sets up the file chooser and event handler. * Throws a GUIException if there is a problem with the resources. */ private void setupResources() throws GUIException { try { MetalTheme theme = new PresentationMetalTheme(0); MetalLookAndFeel.setCurrentTheme(theme); UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch(Exception e) { throw new GUIException("Failed to Initialise:\nLook and Feel Invalid"); } // Create new file chooser which starts in current directory choose = new JFileChooser(); File currentDir = new File("."); // If current directory is the bin directory, go up one level (mainly for Windows version) try { currentDir = currentDir.getCanonicalFile(); if (currentDir.getName().equals("bin")) currentDir = currentDir.getParentFile(); } catch (IOException e) { currentDir = new File("."); } choose.setCurrentDirectory(currentDir); logPlug = null; eventHandle = new GUIEventHandler(this); //Load resources here /********************/ //This will provide for internationalisation support, if necessary //optimism zone } private void setupPrism() throws PrismException { theLog = new userinterface.log.GUIWindowLog(); prism = new Prism(theLog, new PrismFileLog("stdout")); prism.initialise(); } /** * This initialises all of the graphical user interface componenets. * The structure of the user interface should be * */ private void initComponents() throws GUIException { JMenuBar menuBar = new JMenuBar(); JPanel toolPanel = new JPanel(); toolPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); options = new GUIOptionsDialog(this); //options.addPanel(new GUIPrismOptionsPanel(prism)); JPanel thePanel = new JPanel(); // panel to store tabs theTabs = new JTabbedPane(); theTabs.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { clipboardPlugin.pluginChanged(getFocussedPlugin()); } }); //Setup pluggable screens in here plugs = getPluginArray(this); for(int i = 0; i < plugs.size(); i++) { GUIPlugin plug = (GUIPlugin)plugs.get(i); if(plug.displaysTab()) { theTabs.addTab(plug.getTabText(), plug); theTabs.setEnabledAt(theTabs.getComponentCount()-1, plug.isEnabled()); } if(plug.getMenu() != null) { menuBar.add(plug.getMenu()); } if(plug.getToolBar() != null) { toolPanel.add(plug.getToolBar()); } if(plug.getOptions() != null) { options.addPanel(plug.getOptions()); } if(plug instanceof userinterface.log.GUILog) { logPlug = (userinterface.log.GUILog)plug; } prism.getSettings().addSettingsListener(plug); } theTabs.setTabPlacement(JTabbedPane.BOTTOM); thePanel.setLayout(new BorderLayout()); thePanel.setPreferredSize(new Dimension(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); thePanel.setMinimumSize(new Dimension(MINIMUM_WINDOW_WIDTH, MINIMUM_WINDOW_HEIGHT)); thePanel.add(theTabs, BorderLayout.CENTER); JMenu optionsMenu = new JMenu("Options"); menuBar.add(optionsMenu); Action tabSwapper = new AbstractAction() { public void actionPerformed(ActionEvent e) { nextTab(); } }; tabSwapper.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_MASK)); prismOptions = new AbstractAction() { public void actionPerformed(ActionEvent e) { options.show(); } }; prismOptions.putValue(Action.LONG_DESCRIPTION, "Brings up an option dialog for setting PRISM and user interface parameters."); prismOptions.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_O)); prismOptions.putValue(Action.NAME, "Options"); prismOptions.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallOptions.png")); optionsMenu.add(prismOptions); optionsMenu.setMnemonic('O'); fontIncrease = new AbstractAction() { public void actionPerformed(ActionEvent e) { adjustFont(1); } }; fontIncrease.putValue(Action.LONG_DESCRIPTION, "Increase the application font size."); fontIncrease.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_I)); fontIncrease.putValue(Action.NAME, "Increase font size"); fontIncrease.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFontIncrease.png")); fontIncrease.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)); optionsMenu.add(fontIncrease); optionsMenu.setMnemonic('I'); fontDecrease = new AbstractAction() { public void actionPerformed(ActionEvent e) { adjustFont(-1); } }; fontDecrease.putValue(Action.LONG_DESCRIPTION, "Decrease the application font size."); fontDecrease.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_D)); fontDecrease.putValue(Action.NAME, "Decrease font size"); fontDecrease.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFontDecrease.png")); fontDecrease.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, InputEvent.CTRL_MASK)); optionsMenu.add(fontDecrease); optionsMenu.setMnemonic('D'); JPanel bottomPanel = new JPanel(); { progress = new JProgressBar(0,100); progress.setBorder(null); taskbar = new GUITaskBar(); taskbar.setText("Welcome to PRISM..."); bottomPanel.setBorder(new javax.swing.border.EtchedBorder()); bottomPanel.setLayout(new BorderLayout()); bottomPanel.add(progress, BorderLayout.EAST); bottomPanel.add(taskbar, BorderLayout.CENTER); } setJMenuBar(menuBar); setTitle("PRISM "+ prism.getVersion()); setDefaultCloseOperation(javax.swing.JFrame.DO_NOTHING_ON_CLOSE); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent evt) { exit(); } }); getContentPane().add(toolPanel, java.awt.BorderLayout.NORTH); getContentPane().add(thePanel, java.awt.BorderLayout.CENTER); getContentPane().add(bottomPanel, java.awt.BorderLayout.SOUTH); setIconImage(GUIPrism.getIconFromImage("smallPrism.png").getImage()); getContentPane().setSize(new java.awt.Dimension(800,600)); pack(); } public void passCLArgs(String args[]) { // just before we get started, pass any command-line args to all plugins for(int i = 0; i < plugs.size(); i++) { GUIPlugin plug = (GUIPlugin)plugs.get(i); plug.takeCLArgs(args); } } /** * Adjust the main font for the GUI (+1 = one size up, -1 = one size down) */ public void adjustFont(int adjust) { Object[] objs = UIManager.getLookAndFeel().getDefaults().keySet().toArray(); for (int i = 0; i < objs.length; i++) { if (objs[i].toString().toUpperCase().indexOf(".FONT") != -1) { Font font = UIManager.getFont(objs[i]); font = font.deriveFont((float)(font.getSize() + adjust)); UIManager.put(objs[i], new FontUIResource(font)); } } SwingUtilities.updateComponentTreeUI(this); SwingUtilities.updateComponentTreeUI(choose); SwingUtilities.updateComponentTreeUI(options); repaint(); } //EXITERS public void exit() { doExit = true; notifyEventListeners(new GUIExitEvent(GUIExitEvent.REQUEST_EXIT)); if(prism.getSettings().isModified()) { String[] selection = {"Yes", "No", "Cancel"}; int selectionNo = -1; selectionNo = JOptionPane.showOptionDialog(this, "Prism settings have been modified. Do you wish to save them?", "Save Settings", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, selection, selection[0]); switch(selectionNo) { case 0: { try { prism.getSettings().saveSettingsFile(); break; } catch(PrismException e) { errorDialog("Error: Could not save settings",e.getMessage()); } } case 1: {break;} case 2: doExit = false; default: doExit = false; } } if (doExit) System.exit(0); } //ACCESS METHODS /** Utility access method to access the functionality contained within 'prism'. * @return The Prism object contained within this class. */ public Prism getPrism() { return prism; } /** Utility access method to acquire information stored within 'options'. * @return The GUIOptionsDialog object contained within this class. */ public GUIOptionsDialog getOptions() { return options; } /** Utility Access method access the event handler. * @return The event handler. */ public GUIEventHandler getEventHandler() { return eventHandle; } /** Utility access method which takes in a string filename and attempts to retrieve * the file from the images directory. * @return An ImageIcon * @param file The name of the file to be loaded. */ public static ImageIcon getIconFromImage(String file) { URL url = ClassLoader.getSystemResource("images/" + file); if (url == null) { System.out.println("Warning: Failed to load icon file \"" + file + "\""); return null; } return new ImageIcon(url); } /** Utility access method for the file chooser used in the user interface. * @return The file chooser for the PRISM GUI. */ public JFileChooser getChooser() { return choose; } /** Access method to return the GUIPlugin object that is currently viewable on the * screen. * @return The foccused GUIPlugin object. */ public GUIPlugin getFocussedPlugin() { return (GUIPlugin)theTabs.getComponentAt(theTabs.getSelectedIndex()); } /** Access utility method for the PrismLog. * @return The PrismLog contained within this class. */ public PrismLog getLog() { return theLog; } //UPDATE METHODS /** Utility method to notify the event handler of a GUIEvent. * @param e The GUIEvent to be handled. */ public void notifyEventListeners(GUIEvent e) { getEventHandler().notifyListeners(e); } /** Responsible for passing on user interface events to the options panel. * @param e A GUIEvent. */ public boolean processGUIEvent(GUIEvent e) { if(e instanceof GUIComputationEvent) { if(e.getID() == GUIComputationEvent.COMPUTATION_START) { prismOptions.setEnabled(false); } else if(e.getID() == GUIComputationEvent.COMPUTATION_DONE) { prismOptions.setEnabled(true); } else if(e.getID() == GUIComputationEvent.COMPUTATION_ERROR) { prismOptions.setEnabled(true); } } else if (e instanceof GUIExitEvent) { if(e.getID() == GUIExitEvent.CANCEL_EXIT) { doExit = false; } } return false; } /** Update method that takes the GUIPlugin object 'tab' and either disables its * userinterface, or enables it according to 'enable' * @param tab The GUIPlugin object to change * @param enable Should it be enabled? */ public void enableTab(GUIPlugin tab, boolean enable) { for(int i = 0; i < theTabs.getComponentCount(); i++) { Component c = theTabs.getComponentAt(i); if(c instanceof GUIPlugin) { GUIPlugin pl = (GUIPlugin)c; if(pl == tab) { theTabs.setEnabledAt(i, enable); break; } } } } /** Moves the view to the next GUIPlugin component which has a tab. If the last one * has been reached, this sets the view back to the first one. */ public void nextTab() { theTabs.setSelectedIndex((theTabs.getSelectedIndex()+1)%theTabs.getComponentCount()); } /** Moves the view to the given GUIPlugin object. * @param tab The GUIPlugin object to view. */ public void showTab(GUIPlugin tab) { for(int i=0; i