/* * JaamSim Discrete Event Simulation * Copyright (C) 2002-2011 Ausenco Engineering Canada Inc. * Copyright (C) 2016 JaamSim Software Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jaamsim.ui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.FontMetrics; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Insets; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.geom.Rectangle2D; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; import java.util.prefs.Preferences; import java.util.regex.Pattern; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JCheckBoxMenuItem; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JProgressBar; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.JWindow; import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import javax.swing.filechooser.FileNameExtensionFilter; import com.jaamsim.Graphics.DisplayEntity; import com.jaamsim.basicsim.Entity; import com.jaamsim.basicsim.ErrorException; import com.jaamsim.basicsim.Simulation; import com.jaamsim.controllers.RateLimiter; import com.jaamsim.controllers.RenderManager; import com.jaamsim.events.EventErrorListener; import com.jaamsim.events.EventManager; import com.jaamsim.events.EventTimeListener; import com.jaamsim.input.Input; import com.jaamsim.input.InputAgent; import com.jaamsim.input.InputErrorException; import com.jaamsim.input.KeywordIndex; import com.jaamsim.input.Parser; import com.jaamsim.math.Vec3d; import com.jaamsim.units.DistanceUnit; import com.jaamsim.units.TimeUnit; import com.jaamsim.units.Unit; /** * The main window for a Graphical Simulation. It provides the controls for managing then * EventManager (run, pause, ...) and the graphics (zoom, pan, ...) */ public class GUIFrame extends JFrame implements EventTimeListener, EventErrorListener { private static GUIFrame instance; // global shutdown flag static private AtomicBoolean shuttingDown; private JMenu fileMenu; private JMenu viewMenu; private JMenu windowMenu; private JMenu windowList; private JMenu optionMenu; private JMenu helpMenu; private static JCheckBoxMenuItem snapToGrid; private static JCheckBoxMenuItem xyzAxis; private static JCheckBoxMenuItem grid; private JCheckBoxMenuItem alwaysTop; private JCheckBoxMenuItem graphicsDebug; private JMenuItem printInputItem; private JMenuItem saveConfigurationMenuItem; // "Save" private JLabel clockDisplay; private JLabel speedUpDisplay; private JLabel remainingDisplay; private JToggleButton controlRealTime; private JSpinner spinner; private JToggleButton showLinks; private JToggleButton createLinks; private RoundToggleButton controlStartResume; private ImageIcon runPressedIcon; private ImageIcon pausePressedIcon; private RoundToggleButton controlStop; private JTextField pauseTime; private JLabel locatorPos; private JLabel locatorLabel; //JButton toolButtonIsometric; private JToggleButton lockViewXYPlane; private int lastValue = -1; private JProgressBar progressBar; private static Image iconImage; private static final RateLimiter rateLimiter; private static boolean SAFE_GRAPHICS; // Collection of default window parameters public static int DEFAULT_GUI_WIDTH = 1160; public static int COL1_WIDTH; public static int COL2_WIDTH; public static int COL3_WIDTH; public static int COL4_WIDTH; public static int COL1_START; public static int COL2_START; public static int COL3_START; public static int COL4_START; public static int HALF_TOP; public static int HALF_BOTTOM; public static int TOP_START; public static int BOTTOM_START; public static int LOWER_HEIGHT; public static int LOWER_START; public static int VIEW_HEIGHT; public static int VIEW_WIDTH; public static int VIEW_OFFSET = 50; private static final String RUN_TOOLTIP = GUIFrame.formatToolTip("Run", "Starts or resumes the simulation run."); private static final String PAUSE_TOOLTIP = "<html><b>Pause</b></html>"; // Use a small tooltip for Pause so that it does not block the simulation time display static { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { LogBox.logLine("Unable to change look and feel."); } try { URL file = GUIFrame.class.getResource("/resources/images/icon.png"); iconImage = Toolkit.getDefaultToolkit().getImage(file); } catch (Exception e) { LogBox.logLine("Unable to load icon file."); iconImage = null; } shuttingDown = new AtomicBoolean(false); rateLimiter = RateLimiter.create(60); } private GUIFrame() { super(); getContentPane().setLayout( new BorderLayout() ); setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE ); this.addWindowListener(new CloseListener()); // Initialize the working environment initializeMenus(); initializeMainToolBars(); this.setIconImage(GUIFrame.getWindowIcon()); //Set window size setResizable( true ); //FIXME should be false, but this causes the window to be sized // and positioned incorrectly in the Windows 7 Aero theme pack(); setSize(DEFAULT_GUI_WIDTH, getPreferredSize().height); controlStartResume.setSelected( false ); controlStartResume.setEnabled( false ); controlStop.setSelected( false ); controlStop.setEnabled( false ); setProgress( 0 ); ToolTipManager.sharedInstance().setLightWeightPopupEnabled( false ); JPopupMenu.setDefaultLightWeightPopupEnabled( false ); } public static synchronized GUIFrame getInstance() { return instance; } private static synchronized GUIFrame createInstance() { instance = new GUIFrame(); GUIFrame.registerCallback(new Runnable() { @Override public void run() { SwingUtilities.invokeLater(new UIUpdater(instance)); } }); return instance; } public static final void registerCallback(Runnable r) { rateLimiter.registerCallback(r); } public static final void updateUI() { rateLimiter.queueUpdate(); } /** * Listens for window events for the GUI. * */ private class CloseListener extends WindowAdapter implements ActionListener { @Override public void windowClosing(WindowEvent e) { GUIFrame.this.close(); } @Override public void actionPerformed( ActionEvent event ) { GUIFrame.this.close(); } @Override public void windowDeiconified(WindowEvent e) { // Re-open the view windows for (View v : View.getAll()) { if (v.showWindow()) RenderManager.inst().createWindow(v); } // Re-open the tools Simulation.showActiveTools(); FrameBox.reSelectEntity(); } @Override public void windowIconified(WindowEvent e) { // Close all the tools Simulation.closeAllTools(); // Save whether each window is open or closed for (View v : View.getAll()) { v.setKeepWindowOpen(v.showWindow()); } // Close all the view windows RenderManager.clear(); } @Override public void windowActivated(WindowEvent e) { // Re-open the view windows for (int i=0; i<View.getAll().size(); i++) { View v = View.getAll().get(i); if (v != null && v.showWindow()) RenderManager.inst().createWindow(v); } // Re-open the tools Simulation.showActiveTools(); FrameBox.reSelectEntity(); } } /** * Perform exit window duties */ void close() { // check for unsaved changes if (InputAgent.isSessionEdited()) { boolean confirmed = GUIFrame.showSaveChangesDialog(this); if (!confirmed) return; } InputAgent.closeLogFile(); GUIFrame.shutdown(0); } /** * Clears the simulation and user interface prior to loading a new model */ public void clear() { currentEvt.clear(); currentEvt.setTraceListener(null); // Clear the simulation Simulation.clear(); FrameBox.clear(); EntityPallet.clear(); RenderManager.clear(); this.updateForSimulationState(GUIFrame.SIM_STATE_LOADED); // Clear the title bar setTitle(Simulation.getModelName()); // Clear the status bar setProgress( 0 ); speedUpDisplay.setText("0"); remainingDisplay.setText("-"); locatorPos.setText( "-" ); // Read the autoload configuration file InputAgent.clear(); InputAgent.setRecordEdits(false); InputAgent.readResource("<res>/inputs/autoload.cfg"); InputAgent.setPreDefinedEntityCount( Entity.getAll().get( Entity.getAll().size() - 1 ).getEntityNumber()); } /** * Sets up the Control Panel's menu bar. */ private void initializeMenus() { // Set up the individual menus this.initializeFileMenu(); this.initializeViewMenu(); this.initializeWindowMenu(); this.initializeOptionsMenu(); this.initializeHelpMenu(); // Add the individual menu to the main menu JMenuBar mainMenuBar = new JMenuBar(); mainMenuBar.add( fileMenu ); mainMenuBar.add( viewMenu ); mainMenuBar.add( windowMenu ); mainMenuBar.add( optionMenu ); mainMenuBar.add( helpMenu ); // Add main menu to the window setJMenuBar( mainMenuBar ); } // ****************************************************************************************************** // MENU BAR // ****************************************************************************************************** /** * Sets up the File menu in the Control Panel's menu bar. */ private void initializeFileMenu() { // File menu creation fileMenu = new JMenu( "File" ); fileMenu.setMnemonic( 'F' ); fileMenu.setEnabled( false ); // 1) "New" menu item JMenuItem newMenuItem = new JMenuItem( "New" ); newMenuItem.setMnemonic( 'N' ); newMenuItem.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { currentEvt.pause(); // check for unsaved changes if (InputAgent.isSessionEdited()) { boolean confirmed = GUIFrame.showSaveChangesDialog(GUIFrame.this); if (!confirmed) { return; } } clear(); InputAgent.setRecordEdits(true); InputAgent.loadDefault(); displayWindows(); FrameBox.setSelectedEntity(Simulation.getInstance(), false); } } ); fileMenu.add( newMenuItem ); // 2) "Open" menu item JMenuItem configMenuItem = new JMenuItem( "Open..." ); configMenuItem.setMnemonic( 'O' ); configMenuItem.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { currentEvt.pause(); // check for unsaved changes if (InputAgent.isSessionEdited()) { boolean confirmed = GUIFrame.showSaveChangesDialog(GUIFrame.this); if (!confirmed) { return; } } GUIFrame.this.load(); } } ); fileMenu.add( configMenuItem ); // 3) "Save" menu item saveConfigurationMenuItem = new JMenuItem( "Save" ); saveConfigurationMenuItem.setMnemonic( 'S' ); saveConfigurationMenuItem.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { GUIFrame.this.save(); } } ); fileMenu.add( saveConfigurationMenuItem ); // 4) "Save As..." menu item JMenuItem saveConfigurationAsMenuItem = new JMenuItem( "Save As..." ); saveConfigurationAsMenuItem.setMnemonic( 'V' ); saveConfigurationAsMenuItem.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { GUIFrame.this.saveAs(); } } ); fileMenu.add( saveConfigurationAsMenuItem ); // 5) "Import..." menu item JMenu importGraphicsMenuItem = new JMenu( "Import..." ); importGraphicsMenuItem.setMnemonic( 'I' ); JMenuItem importImages = new JMenuItem( "Images..." ); importImages.addActionListener(new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { DisplayEntityFactory.importImages(GUIFrame.this); } } ); importGraphicsMenuItem.add( importImages ); JMenuItem import3D = new JMenuItem( "3D Assets..." ); import3D.addActionListener(new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { DisplayEntityFactory.import3D(GUIFrame.this); } } ); importGraphicsMenuItem.add( import3D ); fileMenu.add( importGraphicsMenuItem ); // 6) "Print Input Report" menu item printInputItem = new JMenuItem( "Print Input Report" ); printInputItem.setMnemonic( 'I' ); printInputItem.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { InputAgent.printInputFileKeywords(); } } ); fileMenu.add( printInputItem ); // 7) "Exit" menu item JMenuItem exitMenuItem = new JMenuItem( "Exit" ); exitMenuItem.setMnemonic( 'x' ); exitMenuItem.addActionListener(new CloseListener()); fileMenu.add( exitMenuItem ); } /** * Sets up the View menu in the Control Panel's menu bar. */ private void initializeViewMenu() { // View menu creation viewMenu = new JMenu( "Tools" ); viewMenu.setMnemonic( 'T' ); // 1) "Show Basic Tools" menu item JMenuItem showBasicToolsMenuItem = new JMenuItem( "Show Basic Tools" ); showBasicToolsMenuItem.setMnemonic( 'B' ); showBasicToolsMenuItem.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { Simulation sim = Simulation.getInstance(); InputAgent.applyArgs(sim, "ShowModelBuilder", "TRUE"); InputAgent.applyArgs(sim, "ShowObjectSelector", "TRUE"); InputAgent.applyArgs(sim, "ShowInputEditor", "TRUE"); InputAgent.applyArgs(sim, "ShowOutputViewer", "TRUE"); } } ); viewMenu.add( showBasicToolsMenuItem ); // 2) "Close All Tools" menu item JMenuItem closeAllToolsMenuItem = new JMenuItem( "Close All Tools" ); closeAllToolsMenuItem.setMnemonic( 'C' ); closeAllToolsMenuItem.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { Simulation sim = Simulation.getInstance(); InputAgent.applyArgs(sim, "ShowModelBuilder", "FALSE"); InputAgent.applyArgs(sim, "ShowObjectSelector", "FALSE"); InputAgent.applyArgs(sim, "ShowInputEditor", "FALSE"); InputAgent.applyArgs(sim, "ShowOutputViewer", "FALSE"); InputAgent.applyArgs(sim, "ShowPropertyViewer", "FALSE"); InputAgent.applyArgs(sim, "ShowLogViewer", "FALSE"); } } ); viewMenu.add( closeAllToolsMenuItem ); // 3) "Model Builder" menu item JMenuItem objectPalletMenuItem = new JMenuItem( "Model Builder" ); objectPalletMenuItem.setMnemonic( 'O' ); objectPalletMenuItem.addActionListener(new SimulationMenuAction("ShowModelBuilder", "TRUE")); viewMenu.add( objectPalletMenuItem ); // 4) "Object Selector" menu item JMenuItem objectSelectorMenuItem = new JMenuItem( "Object Selector" ); objectSelectorMenuItem.setMnemonic( 'S' ); objectSelectorMenuItem.addActionListener(new SimulationMenuAction("ShowObjectSelector", "TRUE")); viewMenu.add( objectSelectorMenuItem ); // 5) "Input Editor" menu item JMenuItem inputEditorMenuItem = new JMenuItem( "Input Editor" ); inputEditorMenuItem.setMnemonic( 'I' ); inputEditorMenuItem.addActionListener(new SimulationMenuAction("ShowInputEditor", "TRUE")); viewMenu.add( inputEditorMenuItem ); // 6) "Output Viewer" menu item JMenuItem outputMenuItem = new JMenuItem( "Output Viewer" ); outputMenuItem.setMnemonic( 'U' ); outputMenuItem.addActionListener(new SimulationMenuAction("ShowOutputViewer", "TRUE")); viewMenu.add( outputMenuItem ); // 7) "Property Viewer" menu item JMenuItem propertiesMenuItem = new JMenuItem( "Property Viewer" ); propertiesMenuItem.setMnemonic( 'P' ); propertiesMenuItem.addActionListener(new SimulationMenuAction("ShowPropertyViewer", "TRUE")); viewMenu.add( propertiesMenuItem ); // 8) "Log Viewer" menu item JMenuItem logMenuItem = new JMenuItem( "Log Viewer" ); logMenuItem.setMnemonic( 'L' ); logMenuItem.addActionListener(new SimulationMenuAction("ShowLogViewer", "TRUE")); viewMenu.add( logMenuItem ); } private static final class SimulationMenuAction implements ActionListener { final String keyword; final String args; SimulationMenuAction(String k, String a) { keyword = k; args = a; } @Override public void actionPerformed(ActionEvent e) { InputAgent.applyArgs(Simulation.getInstance(), keyword, args); } } /** * Sets up the Window menu in the Control Panel's menu bar. */ private void initializeWindowMenu() { // Window menu creation windowMenu = new NewRenderWindowMenu("Views"); windowMenu.setMnemonic( 'V' ); // Initialize list of windows windowList = new WindowMenu("Select Window"); windowList.setMnemonic( 'S' ); } /** * Sets up the Options menu in the Control Panel's menu bar. */ private void initializeOptionsMenu() { optionMenu = new JMenu( "Options" ); optionMenu.setMnemonic( 'O' ); // 1) "Snap to Grid" check box snapToGrid = new JCheckBoxMenuItem( "Snap to Grid", false ); snapToGrid.setMnemonic( 'S' ); optionMenu.add( snapToGrid ); snapToGrid.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { if (snapToGrid.isSelected()) InputAgent.applyArgs(Simulation.getInstance(), "SnapToGrid", "TRUE"); else InputAgent.applyArgs(Simulation.getInstance(), "SnapToGrid", "FALSE"); } } ); // 2) "Show Axes" check box xyzAxis = new JCheckBoxMenuItem( "Show Axes", true ); xyzAxis.setMnemonic( 'X' ); optionMenu.add( xyzAxis ); xyzAxis.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { DisplayEntity ent = (DisplayEntity) Entity.getNamedEntity("XYZ-Axis"); if (ent != null) { if (xyzAxis.isSelected()) InputAgent.applyArgs(ent, "Show", "TRUE"); else InputAgent.applyArgs(ent, "Show", "FALSE"); } } } ); // 3) "Show Grid" check box grid = new JCheckBoxMenuItem( "Show Grid", true ); grid.setMnemonic( 'G' ); optionMenu.add( grid ); grid.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { DisplayEntity ent = (DisplayEntity) Entity.getNamedEntity("XY-Grid"); if (ent != null) { if (grid.isSelected()) InputAgent.applyArgs(ent, "Show", "TRUE"); else InputAgent.applyArgs(ent, "Show", "FALSE"); } } } ); // 4) "Always on top" check box alwaysTop = new JCheckBoxMenuItem( "Always on top", false ); alwaysTop.setMnemonic( 'A' ); optionMenu.add( alwaysTop ); alwaysTop.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { if( GUIFrame.this.isAlwaysOnTop() ) { GUIFrame.this.setAlwaysOnTop( false ); } else { GUIFrame.this.setAlwaysOnTop( true ); } } } ); // 5) "Graphics Debug Info" check box graphicsDebug = new JCheckBoxMenuItem( "Graphics Debug Info", false ); graphicsDebug.setMnemonic( 'D' ); optionMenu.add( graphicsDebug ); graphicsDebug.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { RenderManager.setDebugInfo(((JCheckBoxMenuItem)e.getSource()).getState()); } }); } /** * Sets up the Help menu in the Control Panel's menu bar. */ private void initializeHelpMenu() { // Help menu creation helpMenu = new JMenu( "Help" ); helpMenu.setMnemonic( 'H' ); // 1) "About" menu item JMenuItem aboutMenu = new JMenuItem( "About" ); aboutMenu.setMnemonic( 'A' ); aboutMenu.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { AboutBox.instance().setVisible(true); } } ); helpMenu.add( aboutMenu ); } /** * Returns the pixel length of the string with specified font */ private static int getPixelWidthOfString_ForFont(String str, Font font) { FontMetrics metrics = new FontMetrics(font) {}; Rectangle2D bounds = metrics.getStringBounds(str, null); return (int)bounds.getWidth(); } // ****************************************************************************************************** // TOOL BAR // ****************************************************************************************************** /** * Sets up the Control Panel's main tool bar. */ public void initializeMainToolBars() { // Insets used in setting the toolbar components Insets noMargin = new Insets( 0, 0, 0, 0 ); Insets smallMargin = new Insets( 1, 1, 1, 1 ); // Initilize the main toolbar JToolBar mainToolBar = new JToolBar(); mainToolBar.setMargin( smallMargin ); mainToolBar.setFloatable(false); mainToolBar.setLayout( new FlowLayout( FlowLayout.LEFT, 0, 0 ) ); // 1) Run/Pause button runPressedIcon = new ImageIcon(GUIFrame.class.getResource("/resources/images/run-pressed-24.png")); pausePressedIcon = new ImageIcon(GUIFrame.class.getResource("/resources/images/pause-pressed-24.png")); controlStartResume = new RoundToggleButton(new ImageIcon(GUIFrame.class.getResource("/resources/images/run-24.png"))); controlStartResume.setRolloverEnabled(true); controlStartResume.setRolloverIcon(new ImageIcon(GUIFrame.class.getResource("/resources/images/run-rollover-24.png"))); controlStartResume.setPressedIcon(runPressedIcon); controlStartResume.setSelectedIcon( new ImageIcon(GUIFrame.class.getResource("/resources/images/pause-24.png"))); controlStartResume.setRolloverSelectedIcon( new ImageIcon(GUIFrame.class.getResource("/resources/images/pause-rollover-24.png"))); controlStartResume.setToolTipText(RUN_TOOLTIP); controlStartResume.setMargin( noMargin ); controlStartResume.setEnabled( false ); controlStartResume.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { JToggleButton startResume = (JToggleButton)event.getSource(); startResume.setEnabled(false); if(startResume.isSelected()) { GUIFrame.this.startSimulation(); controlStartResume.setPressedIcon(pausePressedIcon); } else { GUIFrame.this.pauseSimulation(); controlStartResume.setPressedIcon(runPressedIcon); } controlStartResume.grabFocus(); } } ); mainToolBar.add( controlStartResume ); // 2) Stop button controlStop = new RoundToggleButton(new ImageIcon(GUIFrame.class.getResource("/resources/images/reset-16.png"))); controlStop.setToolTipText(formatToolTip("Reset", "Resets the simulation run time to zero.")); controlStop.setPressedIcon(new ImageIcon(GUIFrame.class.getResource("/resources/images/reset-pressed-16.png"))); controlStop.setRolloverEnabled( true ); controlStop.setRolloverIcon(new ImageIcon(GUIFrame.class.getResource("/resources/images/reset-rollover-16.png"))); controlStop.setMargin( noMargin ); controlStop.setEnabled( false ); controlStop.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { if( getSimState() == SIM_STATE_RUNNING ) { GUIFrame.this.pauseSimulation(); } boolean confirmed = GUIFrame.showConfirmStopDialog(); if (!confirmed) return; GUIFrame.this.stopSimulation(); lastSimTime = 0.0d; lastSystemTime = System.currentTimeMillis(); setSpeedUp(0.0d); } } ); int hght = controlStop.getPreferredSize().height; // Separators have 5 pixels before and after and the preferred height of controlStartResume button Dimension separatorDim = new Dimension(11, controlStartResume.getPreferredSize().height); // dimension for 5 pixels gaps Dimension gapDim = new Dimension(5, separatorDim.height); mainToolBar.add(Box.createRigidArea(gapDim)); mainToolBar.add( controlStop ); // 3) Real time button controlRealTime = new JToggleButton( " Real Time " ); controlRealTime.setToolTipText(formatToolTip("Real Time Mode", "When selected, the simulation runs at a fixed multiple of wall clock time.")); controlRealTime.setMargin( smallMargin ); controlRealTime.addActionListener(new RealTimeActionListener()); mainToolBar.addSeparator(separatorDim); mainToolBar.add( controlRealTime ); // 4) Speed Up spinner SpinnerNumberModel numberModel = new SpinnerModel(Simulation.DEFAULT_REAL_TIME_FACTOR, Simulation.MIN_REAL_TIME_FACTOR, Simulation.MAX_REAL_TIME_FACTOR, 1); spinner = new JSpinner(numberModel); // show up to 6 decimal places JSpinner.NumberEditor numberEditor = new JSpinner.NumberEditor(spinner,"0.######"); spinner.setEditor(numberEditor); // make sure spinner TextField is no wider than 9 digits int diff = ((JSpinner.DefaultEditor)spinner.getEditor()).getTextField().getPreferredSize().width - getPixelWidthOfString_ForFont("9", spinner.getFont()) * 9; Dimension dim = spinner.getPreferredSize(); dim.width -= diff; dim.height = hght; spinner.setPreferredSize(dim); spinner.addChangeListener(new SpeedFactorListener()); spinner.setToolTipText(formatToolTip("Speed Multiplier (up/down key)", "Target ratio of simulation time to wall clock time when Real Time mode is selected.")); spinner.setEnabled(false); mainToolBar.add(Box.createRigidArea(gapDim)); mainToolBar.add( spinner ); // 5) Pause time label JLabel pauseAt = new JLabel( "Pause Time:" ); mainToolBar.addSeparator(separatorDim); mainToolBar.add(pauseAt); // 6) Pause time value box pauseTime = new JTextField("0000-00-00T00:00:00") { @Override protected void processFocusEvent(FocusEvent fe) { if (fe.getID() == FocusEvent.FOCUS_LOST) { GUIFrame.this.setPauseTime(this.getText()); } else if (fe.getID() == FocusEvent.FOCUS_GAINED) { pauseTime.selectAll(); } super.processFocusEvent( fe ); } }; // avoid height increase for pauseTime pauseTime.setMaximumSize(pauseTime.getPreferredSize()); // avoid stretching for pauseTime when focusing in and out pauseTime.setPreferredSize(new Dimension(pauseTime.getPreferredSize().width, hght)); pauseTime.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { GUIFrame.this.setPauseTime(pauseTime.getText()); controlStartResume.grabFocus(); } }); pauseTime.setText(""); pauseTime.setHorizontalAlignment(JTextField.RIGHT); pauseTime.setToolTipText(formatToolTip("Pause Time", "Time at which to pause the run, e.g. 3 h, 10 s, etc.")); mainToolBar.add(Box.createRigidArea(gapDim)); mainToolBar.add(pauseTime); // 7) 2D button lockViewXYPlane = new JToggleButton( "2D" ); lockViewXYPlane.setMargin( smallMargin ); lockViewXYPlane.setToolTipText(formatToolTip("2D View", "Sets the camera position to show a bird's eye view of the 3D scene.")); lockViewXYPlane.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { boolean bLock2D = (((JToggleButton)event.getSource()).isSelected()); if (RenderManager.isGood()) { View currentView = RenderManager.inst().getActiveView(); if (currentView != null) { currentView.setLock2D(bLock2D); } } } } ); mainToolBar.addSeparator(separatorDim); mainToolBar.add( lockViewXYPlane ); // 8) Show links button showLinks = new JToggleButton(new ImageIcon(GUIFrame.class.getResource("/resources/images/ShowLinks-16.png"))); showLinks.setToolTipText(formatToolTip("Show Entity Flow", "When selected, arrows are shown between objects to indicate the flow of entities.")); showLinks.setMargin( noMargin ); showLinks.addActionListener(new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { boolean bShow = (((JToggleButton)event.getSource()).isSelected()); if (RenderManager.isGood()) { RenderManager.inst().setShowLinks(bShow); RenderManager.redraw(); } } }); mainToolBar.add(Box.createRigidArea(gapDim)); mainToolBar.add( showLinks ); // 8.5) Show links button createLinks = new JToggleButton(new ImageIcon(GUIFrame.class.getResource("/resources/images/MakeLinks-16.png"))); createLinks.setToolTipText(formatToolTip("Create Entity Links", "When this is enabled, entities are linked when selection is changed.")); createLinks.setMargin( noMargin ); createLinks.addActionListener(new ActionListener() { @Override public void actionPerformed( ActionEvent event ) { boolean bCreate = (((JToggleButton)event.getSource()).isSelected()); if (RenderManager.isGood()) { if (bCreate) { FrameBox.setSelectedEntity(null, false); } RenderManager.inst().setCreateLinks(bCreate); RenderManager.redraw(); } } }); mainToolBar.add(Box.createRigidArea(gapDim)); mainToolBar.add( createLinks ); // 9) Create the display clock and label clockDisplay = new JLabel( "", JLabel.CENTER ); clockDisplay.setPreferredSize( new Dimension( 90, 16 ) ); clockDisplay.setForeground( new Color( 1.0f, 0.0f, 0.0f ) ); clockDisplay.setToolTipText(formatToolTip("Simulation Time", "The present simulation time")); mainToolBar.addSeparator(separatorDim); mainToolBar.add( clockDisplay ); // 10) Create the progress bar progressBar = new JProgressBar( 0, 100 ); progressBar.setPreferredSize( new Dimension( 120, hght ) ); progressBar.setValue( 0 ); progressBar.setStringPainted( true ); progressBar.setToolTipText(formatToolTip("Run Progress", "Percent of the present simulation run that has been completed.")); mainToolBar.add(Box.createRigidArea(gapDim)); mainToolBar.add( progressBar ); // 11) Create a remaining run time display remainingDisplay = new JLabel( "", JLabel.CENTER ); remainingDisplay.setPreferredSize( new Dimension( 90, 16 ) ); remainingDisplay.setForeground( new Color( 1.0f, 0.0f, 0.0f ) ); remainingDisplay.setToolTipText(formatToolTip("Remaining Time", "The remaining time required to complete the present simulation run.")); mainToolBar.add(Box.createRigidArea(gapDim)); mainToolBar.add( remainingDisplay ); // 12) Create a speed-up factor display JLabel speedUpLabel = new JLabel( "Speed:" ); speedUpDisplay = new JLabel( "", JLabel.CENTER ); speedUpDisplay.setPreferredSize( new Dimension( 90, 16 ) ); speedUpDisplay.setForeground( new Color( 1.0f, 0.0f, 0.0f ) ); speedUpDisplay.setToolTipText(formatToolTip("Achieved Speed Multiplier", "The ratio of elapsed simulation time to elasped wall clock time that was achieved.")); mainToolBar.addSeparator(separatorDim); mainToolBar.add( speedUpLabel ); mainToolBar.add( speedUpDisplay ); // 13) Create a cursor position display locatorLabel = new JLabel( "Position:" ); locatorPos = new JLabel( "", JLabel.CENTER ); locatorPos.setPreferredSize( new Dimension( 140, 16 ) ); locatorPos.setForeground( new Color( 1.0f, 0.0f, 0.0f ) ); locatorPos.setToolTipText(formatToolTip("Cursor Position", "The coordinates of the cursor on the x-y plane.")); mainToolBar.addSeparator(separatorDim); mainToolBar.add( locatorLabel ); mainToolBar.add( locatorPos ); // Add the main tool bar to the display getContentPane().add( mainToolBar, BorderLayout.NORTH ); } // ****************************************************************************************************** // VIEW WINDOWS // ****************************************************************************************************** private static class WindowMenu extends JMenu implements MenuListener { WindowMenu(String text) { super(text); this.addMenuListener(this); } @Override public void menuCanceled(MenuEvent arg0) {} @Override public void menuDeselected(MenuEvent arg0) { this.removeAll(); } @Override public void menuSelected(MenuEvent arg0) { if (!RenderManager.isGood()) { return; } ArrayList<Integer> windowIDs = RenderManager.inst().getOpenWindowIDs(); for (int id : windowIDs) { String windowName = RenderManager.inst().getWindowName(id); this.add(new WindowSelector(id, windowName)); } } } private static class WindowSelector extends JMenuItem implements ActionListener { private final int windowID; WindowSelector(int windowID, String windowName) { this.windowID = windowID; this.setText(windowName); this.addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { if (!RenderManager.isGood()) { return; } RenderManager.inst().focusWindow(windowID); } } private static class NewRenderWindowMenu extends JMenu implements MenuListener { NewRenderWindowMenu(String text) { super(text); this.addMenuListener(this); } @Override public void menuSelected(MenuEvent e) { for (View view : View.getAll()) { this.add(new NewRenderWindowLauncher(view)); } this.addSeparator(); this.add(new ViewDefiner()); } @Override public void menuCanceled(MenuEvent arg0) { } @Override public void menuDeselected(MenuEvent arg0) { this.removeAll(); } } private static class NewRenderWindowLauncher extends JMenuItem implements ActionListener { private final View view; NewRenderWindowLauncher(View v) { view = v; this.setText(view.getName()); this.addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { if (!RenderManager.isGood()) { if (RenderManager.canInitialize()) { RenderManager.initialize(SAFE_GRAPHICS); } else { // A fatal error has occurred, don't try to initialize again return; } } InputAgent.applyArgs(view, "ShowWindow", "TRUE"); RenderManager.inst().createWindow(view); FrameBox.setSelectedEntity(view, false); } } private static class ViewDefiner extends JMenuItem implements ActionListener { ViewDefiner() {} { this.setText("Define New View"); this.addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { if (!RenderManager.isGood()) { if (RenderManager.canInitialize()) { RenderManager.initialize(SAFE_GRAPHICS); } else { // A fatal error has occurred, don't try to initialize again return; } } View lastView = null; ArrayList<View> viewList = View.getAll(); if (!viewList.isEmpty()) lastView = viewList.get(viewList.size()-1); View tmp = InputAgent.defineEntityWithUniqueName(View.class, "View", "", true); // Position the new view window to the right of the last one ArrayList<String> arg = new ArrayList<>(); if (lastView != null) { int x = lastView.getWindowPos().get(0) + VIEW_OFFSET; int y = lastView.getWindowPos().get(1); arg.add(String.format((Locale)null, "%d", x)); arg.add(String.format((Locale)null, "%d", y)); InputAgent.apply(tmp, new KeywordIndex("WindowPosition", arg, null)); } RenderManager.inst().createWindow(tmp); FrameBox.setSelectedEntity(tmp, false); InputAgent.applyArgs(tmp, "ShowWindow", "TRUE"); // Use the same view position as the last view window if (lastView == null) return; arg.clear(); lastView.getInput("ViewCenter").getValueTokens(arg); InputAgent.apply(tmp, new KeywordIndex("ViewCenter", arg, null)); arg.clear(); lastView.getInput("ViewPosition").getValueTokens(arg); InputAgent.apply(tmp, new KeywordIndex("ViewPosition", arg, null)); } } public static void setActiveView(View activeView) { boolean lock2D = activeView.is2DLocked(); instance.lockViewXYPlane.setSelected(lock2D); } // ****************************************************************************************************** // RUN STATUS UPDATES // ****************************************************************************************************** private long lastSystemTime = System.currentTimeMillis(); private double lastSimTime = 0.0d; private double speedUp = 0.0d; /** * Sets the values for the simulation time, run progress, speedup factor, * and remaining run time in the Control Panel's status bar. * * @param simTime - the present simulation time in seconds. */ void setClock(double simTime) { // Set the simulation time display String unit = Unit.getDisplayedUnit(TimeUnit.class); double factor = Unit.getDisplayedUnitFactor(TimeUnit.class); clockDisplay.setText(String.format("%,.2f %s", simTime/factor, unit)); // Still update progress if paused at end of run to show 100% if (getSimState() < SIM_STATE_RUNNING) return; // Set the run progress bar display long cTime = System.currentTimeMillis(); double duration = Simulation.getRunDuration() + Simulation.getInitializationTime(); double timeElapsed = simTime - Simulation.getStartTime(); int progress = (int)(timeElapsed * 100.0d / duration); this.setProgress(progress); // Set the speedup factor display if (cTime - lastSystemTime > 5000) { long elapsedMillis = cTime - lastSystemTime; double elapsedSimTime = timeElapsed - lastSimTime; // Determine the speed-up factor speedUp = (elapsedSimTime * 1000.0d) / elapsedMillis; setSpeedUp(speedUp); lastSystemTime = cTime; lastSimTime = timeElapsed; } // Set the remaining time display if (speedUp > 0.0) setRemaining( (duration - timeElapsed)/speedUp ); } /** * Displays the given value on the Control Panel's progress bar. * * @param val - the percent of the run that has completed. */ public void setProgress( int val ) { if (lastValue == val) return; progressBar.setValue( val ); progressBar.repaint(25); lastValue = val; if (getSimState() >= SIM_STATE_CONFIGURED) { String title = String.format("%d%% %s - %s", val, Simulation.getModelName(), InputAgent.getRunName()); setTitle(title); } } /** * Write the given value on the Control Panel's speed up factor box. * * @param val - the speed up factor to write. */ public void setSpeedUp( double val ) { if (val == 0.0) { speedUpDisplay.setText("-"); } else if (val >= 0.99) { speedUpDisplay.setText(String.format("%,.0f", val)); } else { speedUpDisplay.setText(String.format("%,.6f", val)); } } /** * Write the given value on the Control Panel's remaining run time box. * * @param val - the remaining run time in seconds. */ public void setRemaining( double val ) { if (val == 0.0) remainingDisplay.setText("-"); else if (val < 60.0) remainingDisplay.setText(String.format("%.0f seconds left", val)); else if (val < 3600.0) remainingDisplay.setText(String.format("%.1f minutes left", val/60.0)); else if (val < 3600.0*24.0) remainingDisplay.setText(String.format("%.1f hours left", val/3600.0)); else if (val < 3600.0*8760.0) remainingDisplay.setText(String.format("%.1f days left", val/(3600.0*24.0))); else remainingDisplay.setText(String.format("%.1f years left", val/(3600.0*8760.0))); } // ****************************************************************************************************** // SIMULATION CONTROLS // ****************************************************************************************************** /** * Starts or resumes the simulation run. */ public void startSimulation() { if( getSimState() <= SIM_STATE_CONFIGURED ) { if (InputAgent.isSessionEdited()) { this.saveAs(); } Simulation.start(currentEvt); } else if( getSimState() == SIM_STATE_PAUSED ) { currentEvt.resume(currentEvt.secondsToNearestTick(Simulation.getPauseTime())); } else throw new ErrorException( "Invalid Simulation State for Start/Resume" ); } /** * Pauses the simulation run. */ private void pauseSimulation() { if( getSimState() == SIM_STATE_RUNNING ) currentEvt.pause(); else throw new ErrorException( "Invalid Simulation State for pause" ); } /** * Stops the simulation run. */ public void stopSimulation() { if( getSimState() == SIM_STATE_RUNNING || getSimState() == SIM_STATE_PAUSED ) { Simulation.stop(currentEvt); this.updateForSimulationState(GUIFrame.SIM_STATE_CONFIGURED); } else throw new ErrorException( "Invalid Simulation State for stop" ); } /** model was executed, but no configuration performed */ public static final int SIM_STATE_LOADED = 0; /** essential model elements created, no configuration performed */ public static final int SIM_STATE_UNCONFIGURED = 1; /** model has been configured, not started */ public static final int SIM_STATE_CONFIGURED = 2; /** model is presently executing events */ public static final int SIM_STATE_RUNNING = 3; /** model has run, but presently is paused */ public static final int SIM_STATE_PAUSED = 4; private static final String LAST_USED_FOLDER = ""; private int simState; public int getSimState() { return simState; } EventManager currentEvt; public void setEventManager(EventManager e) { currentEvt = e; } public static void updateForSimState(int state) { GUIFrame inst = GUIFrame.getInstance(); if (inst == null) return; inst.updateForSimulationState(state); } /** * Sets the state of the simulation run to the given state value. * * @param state - an index that designates the state of the simulation run. */ void updateForSimulationState(int state) { simState = state; switch( getSimState() ) { case SIM_STATE_LOADED: for( int i = 0; i < fileMenu.getItemCount() - 1; i++ ) { fileMenu.getItem(i).setEnabled(true); } for( int i = 0; i < viewMenu.getItemCount(); i++ ) { viewMenu.getItem(i).setEnabled(true); } windowList.setEnabled( true ); speedUpDisplay.setEnabled( false ); remainingDisplay.setEnabled( false ); setSpeedUp(0); setRemaining(0); setProgress(0); controlStartResume.setEnabled( true ); controlStartResume.setSelected( false ); controlStartResume.setToolTipText(RUN_TOOLTIP); controlStop.setEnabled( false ); controlStop.setSelected( false ); lockViewXYPlane.setEnabled( true ); progressBar.setEnabled( false ); break; case SIM_STATE_UNCONFIGURED: for( int i = 0; i < fileMenu.getItemCount() - 1; i++ ) { fileMenu.getItem(i).setEnabled(true); } for( int i = 0; i < viewMenu.getItemCount(); i++ ) { viewMenu.getItem(i).setEnabled(true); } windowList.setEnabled( true ); speedUpDisplay.setEnabled( false ); remainingDisplay.setEnabled( false ); setSpeedUp(0); setRemaining(0); setProgress(0); controlStartResume.setEnabled( false ); controlStartResume.setSelected( false ); controlStop.setSelected( false ); controlStop.setEnabled( false ); lockViewXYPlane.setEnabled( true ); progressBar.setEnabled( false ); break; case SIM_STATE_CONFIGURED: for( int i = 0; i < fileMenu.getItemCount() - 1; i++ ) { fileMenu.getItem(i).setEnabled(true); } for( int i = 0; i < viewMenu.getItemCount(); i++ ) { viewMenu.getItem(i).setEnabled(true); } windowList.setEnabled( true ); speedUpDisplay.setEnabled( false ); remainingDisplay.setEnabled( false ); setSpeedUp(0); setRemaining(0); setProgress(0); controlStartResume.setEnabled( true ); controlStartResume.setSelected( false ); controlStartResume.setToolTipText(RUN_TOOLTIP); controlStop.setSelected( false ); controlStop.setEnabled( false ); lockViewXYPlane.setEnabled( true ); progressBar.setEnabled( true ); break; case SIM_STATE_RUNNING: speedUpDisplay.setEnabled( true ); remainingDisplay.setEnabled( true ); controlStartResume.setEnabled( true ); controlStartResume.setSelected( true ); controlStartResume.setToolTipText(PAUSE_TOOLTIP); controlStop.setEnabled( true ); controlStop.setSelected( false ); break; case SIM_STATE_PAUSED: controlStartResume.setEnabled( true ); controlStartResume.setSelected( false ); controlStartResume.setToolTipText(RUN_TOOLTIP); controlStop.setEnabled( true ); controlStop.setSelected( false ); break; default: throw new ErrorException( "Unrecognized Graphics State" ); } fileMenu.setEnabled( true ); } public static synchronized void updateForRealTime(boolean executeRT, double factorRT) { GUIFrame inst = GUIFrame.getInstance(); if (inst == null) return; inst.updateForRT(executeRT, factorRT); } /** * updates RealTime button and Spinner */ private void updateForRT(boolean executeRT, double factorRT) { currentEvt.setExecuteRealTime(executeRT, factorRT); controlRealTime.setSelected(executeRT); spinner.setValue(factorRT); if (executeRT) spinner.setEnabled(true); else spinner.setEnabled(false); } public static void updateForPauseTime(String str) { GUIFrame inst = GUIFrame.getInstance(); if (inst == null) return; inst.updateForPT(str); } /** * updates PauseTime entry */ private void updateForPT(String str) { pauseTime.setText(str); } /** * Sets the PauseTime keyword for Simulation. * @param str - value to assign. */ private void setPauseTime(String str) { Input<?> pause = Simulation.getInstance().getInput("PauseTime"); String prevVal = pause.getValueString(); if (prevVal.equals(str)) return; // If the time is in RFC8601 format, enclose in single quotes if (str.contains("-") || str.contains(":")) if (Parser.needsQuoting(str) && !Parser.isQuoted(str)) str = Parser.addQuotes(str); ArrayList<String> tokens = new ArrayList<>(); Parser.tokenize(tokens, str); // if we only got one token, and it isn't RFC8601 - add a unit if (tokens.size() == 1 && !tokens.get(0).contains("-") && !tokens.get(0).contains(":")) tokens.add(Unit.getDisplayedUnit(TimeUnit.class)); try { // Parse the keyword inputs KeywordIndex kw = new KeywordIndex("PauseTime", tokens, null); InputAgent.apply(Simulation.getInstance(), kw); } catch (InputErrorException e) { pauseTime.setText(prevVal); GUIFrame.showErrorDialog("Input Error", e.getMessage()); } } public static Image getWindowIcon() { return iconImage; } public void copyLocationToClipBoard(Vec3d pos) { String data = String.format("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z); StringSelection stringSelection = new StringSelection(data); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents( stringSelection, null ); } public static void showLocatorPosition(Vec3d pos) { GUIFrame inst = GUIFrame.getInstance(); if (inst == null) return; inst.showLocator(pos); } private void showLocator(Vec3d pos) { if( pos == null ) { locatorPos.setText( "-" ); return; } String unit = Unit.getDisplayedUnit(DistanceUnit.class); double factor = Unit.getDisplayedUnitFactor(DistanceUnit.class); locatorPos.setText(String.format((Locale)null, "%.3f %.3f %.3f %s", pos.x/factor, pos.y/factor, pos.z/factor, unit)); } public void enableSave(boolean bool) { saveConfigurationMenuItem.setEnabled(bool); } /** * Sets variables used to determine the position and size of various * windows based on the size of the computer display being used. */ private void calcWindowDefaults() { Dimension guiSize = this.getSize(); Rectangle winSize = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds(); COL1_WIDTH = 220; COL2_WIDTH = Math.min(520, (winSize.width - COL1_WIDTH) / 2); COL3_WIDTH = Math.min(420, winSize.width - COL1_WIDTH - COL2_WIDTH); VIEW_WIDTH = COL2_WIDTH + COL3_WIDTH; COL4_WIDTH = 520; COL1_START = this.getX(); COL2_START = COL1_START + COL1_WIDTH; COL3_START = COL2_START + COL2_WIDTH; COL4_START = Math.min(COL3_START + COL3_WIDTH, winSize.width - COL4_WIDTH); HALF_TOP = (winSize.height - guiSize.height) / 2; HALF_BOTTOM = (winSize.height - guiSize.height - HALF_TOP); LOWER_HEIGHT = (winSize.height - guiSize.height) / 3; VIEW_HEIGHT = winSize.height - guiSize.height - LOWER_HEIGHT; TOP_START = this.getY() + guiSize.height; BOTTOM_START = TOP_START + HALF_TOP; LOWER_START = TOP_START + VIEW_HEIGHT; } /** * Displays the view windows and tools on startup. */ public static void displayWindows() { // Show the view windows specified in the configuration file for (View v : View.getAll()) { if (v.showWindow()) RenderManager.inst().createWindow(v); } // Set the initial state for the "Snap to Grid" check box snapToGrid.setSelected(Simulation.isSnapToGrid()); // Set the initial state for the "Show Axes" check box DisplayEntity ent = (DisplayEntity) Entity.getNamedEntity("XYZ-Axis"); if (ent == null) { xyzAxis.setEnabled(false); xyzAxis.setSelected(false); } else { xyzAxis.setEnabled(true); xyzAxis.setSelected(ent.getShow()); } // Set the initial state for the "Show Grid" check box ent = (DisplayEntity) Entity.getNamedEntity("XY-Grid"); if (ent == null) { grid.setEnabled(false); grid.setSelected(false); } else { grid.setEnabled(true); grid.setSelected(ent.getShow()); } } // ****************************************************************************************************** // MAIN // ****************************************************************************************************** public static void main( String args[] ) { // Process the input arguments and filter out directives ArrayList<String> configFiles = new ArrayList<>(args.length); boolean batch = false; boolean minimize = false; boolean quiet = false; boolean scriptMode = false; boolean headless = false; for (String each : args) { // Batch mode if (each.equalsIgnoreCase("-b") || each.equalsIgnoreCase("-batch")) { batch = true; continue; } // Script mode (command line I/O) if (each.equalsIgnoreCase("-s") || each.equalsIgnoreCase("-script")) { scriptMode = true; continue; } // z-buffer offset if (each.equalsIgnoreCase("-z") || each.equalsIgnoreCase("-zbuffer")) { // Parse the option, but do nothing continue; } // Minimize model window if (each.equalsIgnoreCase("-m") || each.equalsIgnoreCase("-minimize")) { minimize = true; continue; } // Minimize model window if (each.equalsIgnoreCase("-h") || each.equalsIgnoreCase("-headless")) { headless = true; batch = true; continue; } // Do not open default windows if (each.equalsIgnoreCase("-q") || each.equalsIgnoreCase("-quiet")) { quiet = true; continue; } if (each.equalsIgnoreCase("-sg") || each.equalsIgnoreCase("-safe_graphics")) { SAFE_GRAPHICS = true; continue; } // Not a program directive, add to list of config files configFiles.add(each); } InputAgent.setScriptMode(scriptMode); // If not running in batch mode, create the splash screen JWindow splashScreen = null; if (!batch) { URL splashImage = GUIFrame.class.getResource("/resources/images/splashscreen.png"); ImageIcon imageIcon = new ImageIcon(splashImage); Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); int splashX = (screen.width - imageIcon.getIconWidth()) / 2; int splashY = (screen.height - imageIcon.getIconHeight()) / 2; // Set the window's bounds, centering the window splashScreen = new JWindow(); splashScreen.setAlwaysOnTop(true); splashScreen.setBounds(splashX, splashY, imageIcon.getIconWidth(), imageIcon.getIconHeight()); // Build the splash screen splashScreen.getContentPane().add(new JLabel(imageIcon)); // Display it splashScreen.setVisible(true); } // create a graphic simulation LogBox.logLine("Loading Simulation Environment ... "); EventManager evt = new EventManager("DefaultEventManager"); GUIFrame gui = null; if (!headless) { gui = GUIFrame.createInstance(); gui.setEventManager(evt); gui.updateForSimulationState(SIM_STATE_LOADED); evt.setTimeListener(gui); evt.setErrorListener(gui); if (minimize) gui.setExtendedState(JFrame.ICONIFIED); } if (!batch && !headless) { // Begin initializing the rendering system RenderManager.initialize(SAFE_GRAPHICS); } LogBox.logLine("Simulation Environment Loaded"); if (batch) InputAgent.setBatch(true); // Load the autoload file InputAgent.setRecordEdits(false); InputAgent.readResource("<res>/inputs/autoload.cfg"); InputAgent.setPreDefinedEntityCount( Entity.getAll().get( Entity.getAll().size() - 1 ).getEntityNumber()); // Show the Control Panel if (gui != null) { gui.setTitle(Simulation.getModelName()); gui.setVisible(true); gui.calcWindowDefaults(); } // Resolve all input arguments against the current working directory File user = new File(System.getProperty("user.dir")); // Process any configuration files passed on command line // (Multiple configuration files are not supported at present) for (int i = 0; i < configFiles.size(); i++) { //InputAgent.configure(gui, new File(configFiles.get(i))); File abs = new File((File)null, configFiles.get(i)); File loadFile; if (abs.exists()) loadFile = abs.getAbsoluteFile(); else loadFile = new File(user, configFiles.get(i)); Throwable t = GUIFrame.configure(loadFile); if (t != null) { // Hide the splash screen if (splashScreen != null) { splashScreen.dispose(); splashScreen = null; } handleConfigError(t, loadFile); } } // If in script mode, load a configuration file from standard in if (scriptMode) { BufferedReader buf = new BufferedReader(new InputStreamReader(System.in)); InputAgent.readBufferedStream(buf, null, ""); } // If no configuration files were specified on the command line, then load the default configuration file if (configFiles.size() == 0 && !scriptMode) { InputAgent.setRecordEdits(true); InputAgent.loadDefault(); GUIFrame.updateForSimState(GUIFrame.SIM_STATE_CONFIGURED); } // Show the view windows if(!quiet && !batch) { displayWindows(); } // If in batch or quiet mode, close the any tools that were opened if (quiet || batch) Simulation.closeAllTools(); // Set RecordEdits mode (if it has not already been set in the configuration file) InputAgent.setRecordEdits(true); // Start the model if in batch mode if (batch) { if (InputAgent.numErrors() > 0) GUIFrame.shutdown(0); Simulation.start(evt); return; } // Hide the splash screen if (splashScreen != null) { splashScreen.dispose(); splashScreen = null; } // Bring the Control Panel to the front (along with any open Tools) if (gui != null) gui.toFront(); // Set the selected entity to the Simulation object FrameBox.setSelectedEntity(Simulation.getInstance(), false); } public static class SpeedFactorListener implements ChangeListener { @Override public void stateChanged( ChangeEvent e ) { Double val = (Double)((JSpinner)e.getSource()).getValue(); String str; if (val.doubleValue() >= 1.0) { str = String.format("%.0f", val); } else { str = String.format("%.6f", val); } InputAgent.applyArgs(Simulation.getInstance(), "RealTimeFactor", str); } } /* * this class is created so the next value will be value * 2 and the * previous value will be value / 2 */ public static class SpinnerModel extends SpinnerNumberModel { private double value; public SpinnerModel( double val, double min, double max, double stepSize) { super(val, min, max, stepSize); } @Override public Object getPreviousValue() { value = this.getNumber().doubleValue() / 2.0; if (value >= 1.0) value = Math.floor(value); // Avoid going beyond limit Double min = (Double)this.getMinimum(); if (min.doubleValue() > value) { return min; } return value; } @Override public Object getNextValue() { value = this.getNumber().doubleValue() * 2.0; if (value >= 1.0) value = Math.floor(value); // Avoid going beyond limit Double max = (Double)this.getMaximum(); if (max.doubleValue() < value) { return max; } return value; } } public static class RealTimeActionListener implements ActionListener { @Override public void actionPerformed( ActionEvent event ) { if (((JToggleButton)event.getSource()).isSelected()) InputAgent.applyArgs(Simulation.getInstance(), "RealTime", "TRUE"); else InputAgent.applyArgs(Simulation.getInstance(), "RealTime", "FALSE"); } } public static boolean getShuttingDownFlag() { return shuttingDown.get(); } public static void shutdown(int errorCode) { shuttingDown.set(true); if (RenderManager.isGood()) { RenderManager.inst().shutdown(); } System.exit(errorCode); } volatile long simTicks; private static class UIUpdater implements Runnable { private final GUIFrame frame; UIUpdater(GUIFrame gui) { frame = gui; } @Override public void run() { double callBackTime = EventManager.ticksToSecs(frame.simTicks); frame.setClock(callBackTime); FrameBox.updateEntityValues(callBackTime); } } @Override public void tickUpdate(long tick) { if (tick == simTicks) return; simTicks = tick; RenderManager.updateTime(tick); GUIFrame.updateUI(); } @Override public void timeRunning(boolean running) { if (running) { updateForSimulationState(SIM_STATE_RUNNING); } else { updateForSimulationState(SIM_STATE_PAUSED); } } @Override public void handleError(EventManager evt, Throwable t, long currentTick) { if (t instanceof OutOfMemoryError) { OutOfMemoryError e = (OutOfMemoryError)t; InputAgent.logMessage("Out of Memory use the -Xmx flag during execution for more memory"); InputAgent.logMessage("Further debug information:"); InputAgent.logMessage("%s", e.getMessage()); InputAgent.logStackTrace(t); GUIFrame.shutdown(1); return; } else { double curSec = evt.ticksToSeconds(currentTick); InputAgent.logMessage("EXCEPTION AT TIME: %f s", curSec); InputAgent.logMessage("%s", t.getMessage()); InputAgent.logStackTrace(t); } GUIFrame.showErrorDialog("Runtime Error", "JaamSim has detected the following runtime error condition:", t, "Programmers can find more information by opening the Log Viewer.\n" + "The simulation run must be reset to zero simulation time before it " + "can be restarted."); } void load() { LogBox.logLine("Loading..."); Preferences prefs = Preferences.userRoot().node(getClass().getName()); // Create a file chooser final JFileChooser chooser = new JFileChooser(prefs.get(LAST_USED_FOLDER, new File(".").getAbsolutePath())); // Set the file extension filters chooser.setAcceptAllFileFilterUsed(true); FileNameExtensionFilter cfgFilter = new FileNameExtensionFilter("JaamSim Configuration File (*.cfg)", "CFG"); chooser.addChoosableFileFilter(cfgFilter); chooser.setFileFilter(cfgFilter); // Show the file chooser and wait for selection int returnVal = chooser.showOpenDialog(this); // Load the selected file if (returnVal == JFileChooser.APPROVE_OPTION) { File temp = chooser.getSelectedFile(); final GUIFrame gui1 = this; final File chosenfile = temp; new Thread(new Runnable() { @Override public void run() { InputAgent.setRecordEdits(false); gui1.clear(); Throwable ret = GUIFrame.configure(chosenfile); if (ret != null) handleConfigError(ret, chosenfile); InputAgent.setRecordEdits(true); GUIFrame.displayWindows(); FrameBox.setSelectedEntity(Simulation.getInstance(), false); } }).start(); prefs.put(LAST_USED_FOLDER, chosenfile.getParent()); } } static Throwable configure(File file) { InputAgent.setConfigFile(file); GUIFrame.updateForSimState(GUIFrame.SIM_STATE_UNCONFIGURED); Throwable ret = null; try { InputAgent.loadConfigurationFile(file); } catch (Throwable t) { ret = t; } if (ret == null) LogBox.logLine("Configuration File Loaded"); else LogBox.logLine("Configuration File Loaded - errors found"); // show the present state in the user interface GUIFrame gui = GUIFrame.getInstance(); if (gui != null) { gui.setProgress(0); gui.setTitle( Simulation.getModelName() + " - " + InputAgent.getRunName() ); gui.updateForSimulationState(GUIFrame.SIM_STATE_CONFIGURED); gui.enableSave(InputAgent.getRecordEditsFound()); } return ret; } static void handleConfigError(Throwable t, File file) { if (t instanceof InputErrorException) { InputAgent.logMessage("Input Error: %s", t.getMessage()); GUIFrame.showErrorOptionDialog("Input Error", "Input errors were detected while loading file: '%s'\n\n%s\n\n" + "Open '%s' with Log Viewer?", file.getName(), t.getMessage(), InputAgent.getRunName() + ".log"); return; } InputAgent.logMessage("Fatal Error while loading file '%s': %s\n", file.getName(), t.getMessage()); GUIFrame.showErrorDialog("Fatal Error", String.format("A fatal error has occured while loading the file '%s':", file.getName()), t.getMessage(), ""); } /** * Saves the configuration file. * @param gui = Control Panel window for JaamSim * @param fileName = absolute file path and file name for the file to be saved */ private void setSaveFile(String fileName) { // Set root directory File temp = new File(fileName); // Save the configuration file InputAgent.printNewConfigurationFileWithName( fileName ); InputAgent.setConfigFile(temp); // Set the title bar to match the new run name this.setTitle( Simulation.getModelName() + " - " + InputAgent.getRunName() ); } boolean save() { LogBox.logLine("Saving..."); if( InputAgent.getConfigFile() != null ) { setSaveFile(InputAgent.getConfigFile().getPath()); return true; } boolean confirmed = saveAs(); return confirmed; } boolean saveAs() { LogBox.logLine("Save As..."); Preferences prefs = Preferences.userRoot().node(getClass().getName()); // Create a file chooser final JFileChooser chooser = new JFileChooser(prefs.get(LAST_USED_FOLDER, new File(".").getAbsolutePath())); // Set the file extension filters chooser.setAcceptAllFileFilterUsed(true); FileNameExtensionFilter cfgFilter = new FileNameExtensionFilter("JaamSim Configuration File (*.cfg)", "CFG"); chooser.addChoosableFileFilter(cfgFilter); chooser.setFileFilter(cfgFilter); chooser.setSelectedFile(InputAgent.getConfigFile()); // Show the file chooser and wait for selection int returnVal = chooser.showSaveDialog(this); if (returnVal != JFileChooser.APPROVE_OPTION) return false; File file = chooser.getSelectedFile(); String filePath = file.getPath(); // Add the file extension ".cfg" if needed filePath = filePath.trim(); if (file.getName().trim().indexOf('.') == -1) { filePath = filePath.concat(".cfg"); } // Confirm overwrite if file already exists File temp = new File(filePath); if (temp.exists()) { boolean confirmed = GUIFrame.showSaveAsDialog(file.getName()); if (!confirmed) { return false; } } // Save the configuration file setSaveFile(filePath); prefs.put(LAST_USED_FOLDER, file.getParent()); return true; } // ****************************************************************************************************** // DIALOG BOXES // ****************************************************************************************************** /** * Shows the "Confirm Save As" dialog box * @param fileName - name of the file to be saved * @return true if the file is to be overwritten. */ public static boolean showSaveAsDialog(String fileName) { int userOption = JOptionPane.showConfirmDialog(null, String.format("The file '%s' already exists.\n" + "Do you want to replace it?", fileName), "Confirm Save As", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); return (userOption == JOptionPane.YES_OPTION); } /** * Shows the "Confirm Stop" dialog box. * @return true if the run is to be stopped. */ public static boolean showConfirmStopDialog() { int userOption = JOptionPane.showConfirmDialog( null, "WARNING: Are you sure you want to reset the simulation time to 0?", "Confirm Reset", JOptionPane.YES_OPTION, JOptionPane.WARNING_MESSAGE ); return (userOption == JOptionPane.YES_OPTION); } /** * Shows the "Save Changes" dialog box * @return true for any response other than Cancel or Close. */ public static boolean showSaveChangesDialog(GUIFrame gui) { String message; if (InputAgent.getConfigFile() == null) message = "Do you want to save the changes you made?"; else message = String.format("Do you want to save the changes you made to '%s'?", InputAgent.getConfigFile().getName()); Object[] options = {"Save", "Don't Save", "Cancel"}; int userOption = JOptionPane.showOptionDialog( null, message, "Save Changes", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]); if (userOption == JOptionPane.YES_OPTION) { boolean confirmed = gui.save(); return confirmed; } else if (userOption == JOptionPane.NO_OPTION) return true; return false; } private static String getErrorMessage(String source, int position, String pre, String message, String post) { StringBuilder sb = new StringBuilder(); sb.append("<html>"); // Initial text prior to the message if (!pre.isEmpty()) { sb.append(html_replace(pre)).append("<br><br>"); } // Error message sb.append(html_replace(message)).append("<br>"); // Append the source expression and an 'arrow' pointing at the error if (!source.isEmpty()) { sb.append("<pre><font color=\"red\">"); sb.append(html_replace(source)).append("<br>"); for (int i = 0; i < position; ++i) { sb.append(" "); } sb.append("<b>^</b></font></pre>"); } // Final text after the message if (!post.isEmpty()) { if (source.isEmpty()) { sb.append("<br>"); } sb.append(html_replace(post)); } sb.append("</html>"); return sb.toString(); } /** * Displays the Error Message dialog box * @param title - text for the dialog box name * @param source - expression that cause the error (if applicable) * @param position - location of the error in the expression (if applicable) * @param pre - text to appear prior to the error message * @param message - error message * @param post - text to appear after the error message */ public static void showErrorDialog(String title, String source, int position, String pre, String message, String post) { if (InputAgent.getBatch()) GUIFrame.shutdown(1); String msg = GUIFrame.getErrorMessage(source, position, pre, message, post); JOptionPane.showMessageDialog(null, msg, title, JOptionPane.ERROR_MESSAGE); } public static void showErrorDialog(String title, String pre, String message, String post) { GUIFrame.showErrorDialog(title, "", -1, pre, message, post); } public static void showErrorDialog(String title, String message) { GUIFrame.showErrorDialog(title, "", -1, "", message, ""); } public static void showErrorDialog(String title, String pre, Throwable t, String post) { if (t instanceof InputErrorException) { InputErrorException e = (InputErrorException)t; GUIFrame.showErrorDialog(title, e.source, e.position, pre, e.getMessage(), post); return; } if (t instanceof ErrorException) { ErrorException e = (ErrorException)t; GUIFrame.showErrorDialog(title, e.source, e.position, pre, e.getMessage(), post); return; } GUIFrame.showErrorDialog(title, "", -1, pre, t.getMessage(), post); } /** * Shows the Error Message dialog box from a non-Swing thread * @param title - text for the dialog box name * @param fmt - format string for the error message * @param args - inputs to the error message */ public static void invokeErrorDialog(String title, String fmt, Object... args) { final String msg = String.format(fmt, args); SwingUtilities.invokeLater(new RunnableError(title, msg)); } private static class RunnableError implements Runnable { private final String title; private final String message; public RunnableError(String t, String m) { title = t; message = m; } @Override public void run() { GUIFrame.showErrorDialog(title, message); } } /** * Shows the Error Message dialog box with option to open the Log Viewer * @param title - text for the dialog box name * @param fmt - format string for the error message * @param args - inputs to the error message */ public static void showErrorOptionDialog(String title, String fmt, Object... args) { if (InputAgent.getBatch()) GUIFrame.shutdown(1); final String msg = String.format(fmt, args); Object[] options = {"Yes", "No"}; int userOption = JOptionPane.showOptionDialog(null, msg, title, JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, null, options, options[0]); if (userOption == JOptionPane.YES_OPTION) { InputAgent.applyArgs(Simulation.getInstance(), "ShowLogViewer", "TRUE"); } } /** * Shows the Error Message dialog box for the Input Editor * @param title - text for the dialog box name * @param pre - text to appear before the error message * @param e - input error object * @param post - text to appear after the error message * @return true if the input is to be re-edited */ public static boolean showErrorEditDialog(String title, String pre, InputErrorException e, String post) { String msg = GUIFrame.getErrorMessage(e.source, e.position, "Input error:", e.getMessage(), "Do you want to continue editing, or reset the input?"); String[] options = { "Edit", "Reset" }; int reply = JOptionPane.showOptionDialog(null, msg, "Input Error", JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, null, options, options[0]); return (reply == JOptionPane.OK_OPTION); } // ****************************************************************************************************** // TOOL TIPS // ****************************************************************************************************** private static final Pattern amp = Pattern.compile("&"); private static final Pattern lt = Pattern.compile("<"); private static final Pattern gt = Pattern.compile(">"); private static final Pattern br = Pattern.compile("\n"); private static final String html_replace(String str) { String desc = str; desc = amp.matcher(desc).replaceAll("&"); desc = lt.matcher(desc).replaceAll("<"); desc = gt.matcher(desc).replaceAll(">"); desc = br.matcher(desc).replaceAll("<BR>"); return desc; } /** * Returns the HTML code for pop-up tooltips in the Model Builder and Control Panel. * @param name - name of the item whose tooltip is to be generated * @param description - text describing the item's function * @return HTML for the tooltip */ public static String formatToolTip(String name, String desc) { return String.format("<html><p width=\"200px\"><b>%s</b><br>%s</p></html>", name, desc); } /** * Returns the HTML code for a keyword's pop-up tooltip in the Input Editor. * @param className - object whose keyword tooltip is to be displayed * @param keyword - name of the keyword * @param description - description of the keyword * @param example - an example showing how the keyword is used * @param exampleList - a list of examples that show how the keyword can be used * @return HTML for the tooltip */ public static String formatKeywordToolTip(String className, String keyword, String description, String example, String[] exampleList) { String desc = html_replace(description); // Single example String examp; if (!example.isEmpty()) { examp = html_replace(example); } // List of examples else { StringBuilder sb = new StringBuilder(); for (int i=0; i<exampleList.length; i++) { String item = html_replace(exampleList[i]); if (i > 0) sb.append("<BR>"); sb.append(className).append("1 ").append(keyword).append(" { "); sb.append(item).append(" }"); } examp = sb.toString(); } return String.format("<html><p width=\"350px\"><b>%s</b><br>%s<br><br><u>Examples:</u><br>%s</p></html>", keyword, desc, examp); } /** * Returns the HTML code for an output's pop-up tooltip in the Output Viewer. * @param name - name of the output * @param description - description of the output * @return HTML for the tooltip */ public static String formatOutputToolTip(String name, String description) { String desc = html_replace(description); return String.format("<html><p width=\"250px\"><b>%s</b><br>%s</p></html>", name, desc); } }