/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ package org.opensourcephysics.tools; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.Locale; import java.util.Set; import java.util.Vector; import java.util.jar.JarEntry; import java.util.jar.JarFile; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JEditorPane; 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.JPanel; import javax.swing.JPopupMenu; import javax.swing.JRadioButtonMenuItem; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextPane; import javax.swing.JToolBar; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.WindowConstants; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.border.Border; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.filechooser.FileFilter; import javax.swing.text.html.HTMLEditorKit; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreePath; import javax.swing.undo.UndoableEdit; import javax.swing.undo.UndoableEditSupport; import org.opensourcephysics.controls.Cryptic; import org.opensourcephysics.controls.MessageFrame; import org.opensourcephysics.controls.OSPLog; import org.opensourcephysics.controls.Password; import org.opensourcephysics.controls.XML; import org.opensourcephysics.controls.XMLControl; import org.opensourcephysics.controls.XMLControlElement; import org.opensourcephysics.controls.XMLTable; import org.opensourcephysics.controls.XMLTableInspector; import org.opensourcephysics.controls.XMLTreePanel; import org.opensourcephysics.desktop.OSPDesktop; import org.opensourcephysics.display.AppFrame; import org.opensourcephysics.display.DisplayRes; import org.opensourcephysics.display.OSPRuntime; import org.opensourcephysics.display.PrintUtils; import org.opensourcephysics.tools.LaunchNode.DisplayTab; /** * This provides a GUI for launching osp applications and xml files. * * @author Douglas Brown */ public class Launcher { // static constants protected static final Icon defaultIcon = new DefaultIcon(); // static fields protected static Launcher mainLauncher; protected static String defaultFileName = "launcher_default"; //$NON-NLS-1$ protected static String resourcesPath = "/org/opensourcephysics/resources/tools/"; //$NON-NLS-1$ protected static String classPath; // list of jar names for classes, resources protected static String tabSetBasePath = ""; // absolute base path of tabset //$NON-NLS-1$ protected static String version = "1.4"; //$NON-NLS-1$ protected static String releaseDate = "July 2007"; //$NON-NLS-1$ protected static JFileChooser chooser; protected static FileFilter xmlFileFilter; protected static FileFilter xsetFileFilter; protected static FileFilter launcherFileFilter; protected static int wInit = 480, hInit = 400; protected static JDialog splashDialog; protected static JLabel creditsLabel; protected static JLabel splashTitleLabel; protected static JLabel splashPathLabel; protected static javax.swing.Timer splashTimer; protected static float baseMenuFontSize; // some of these icons are created in LaunchBuilder protected static Icon launchIcon, launchedIcon, singletonIcon, whiteFolderIcon; protected static Icon redFileIcon, greenFileIcon, magentaFileIcon, yellowFileIcon; protected static Icon whiteFileIcon, noFileIcon, ghostFileIcon; protected static Icon redFolderIcon, greenFolderIcon, yellowFolderIcon; protected static Icon linkIcon, htmlIcon, launchEmptyIcon, ejsIcon; protected static Icon navOpenIcon, navClosedIcon; protected static Icon backIcon, forwardIcon; @SuppressWarnings("javadoc") public static boolean singleAppMode = false; @SuppressWarnings("javadoc") public static LaunchNode activeNode; private static boolean newVMAllowed = false; protected static javax.swing.Timer frameFinder; protected static ArrayList<Frame> existingFrames = new ArrayList<Frame>(); protected static String[] extractExtensions = {"pdf", "txt", "doc"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ protected static Set<String> passwords = new HashSet<String>(); // instance fields private boolean canExit = true; protected JDialog xmlInspector; protected JDialog tableInspector; protected int divider = 160; @SuppressWarnings("javadoc") public LauncherFrame frame; protected JPanel contentPane; protected JTabbedPane tabbedPane; protected boolean postEdits = true; protected boolean navigationVisible = true; protected JToolBar navbar; protected JButton navButton; protected JButton backButton; protected JButton forwardButton; protected Component navSpacer = Box.createHorizontalGlue(); protected Component[] navbarAddOns; protected JMenuItem singleAppItem; protected LaunchNode selectedNode; // currently selected node protected LaunchNode previousNode; // previously selected node protected String tabSetName; // name relative to tabSetBasePath protected JTextPane textPane; // for button view protected JScrollPane textScroller; // for button view protected boolean showText = true; // for button view protected ArrayList<HTMLPane> htmlTabList = new ArrayList<HTMLPane>(); // HTML textPanes and scrollers protected JMenu fileMenu; protected JMenu displayMenu; protected JMenu helpMenu; protected JMenuItem openItem; protected JMenu openFromJarMenu; protected JMenuItem passwordItem; protected JMenuItem closeTabItem; protected JMenuItem closeAllItem; protected JMenuItem editItem; protected JMenuItem exitItem; protected JMenuItem inspectItem; protected JMenuItem hideItem; protected JMenuItem backItem; protected JMenu languageMenu; protected JMenuItem sizeUpItem; protected JMenuItem sizeDownItem; protected JMenu lookFeelMenu; protected ButtonGroup specificLFGroup, genericLFGroup; protected JMenuItem javaLFItem; protected JMenuItem systemLFItem; protected JMenuItem defaultLFItem; protected JMenuItem lookFeelItem; protected JMenuItem logItem; protected JMenuItem aboutItem; protected JMenuItem authorInfoItem; protected JMenu diagnosticMenu; protected JMenuItem[] languageItems; protected LaunchClassChooser classChooser; protected JPopupMenu popup = new JPopupMenu(); protected Set<String> openPaths = new HashSet<String>(); // relative paths protected Launcher spawner; protected boolean previewing = false; protected boolean editorEnabled = true; // enables editing current set protected Set<String> changedFiles = new HashSet<String>(); // relative paths protected MouseListener tabListener; protected boolean newNodeSelected = false; protected boolean selfContained = false; protected String jarBasePath = null; // non-null only if current xset is internal to jar protected String title; protected ArrayList<Component> tabs = new ArrayList<Component>(); protected LauncherUndo undoManager; protected UndoableEditSupport undoSupport; protected String password; protected boolean pwRequiredToLoad; protected HyperlinkListener linkListener; protected boolean saveState = true; protected String lookAndFeel; protected Collection<?>[] expansions; protected String selectedPath; protected JButton memoryButton; protected int xsetMemorySize; static { OSPRuntime.setAuthorMode(false); OSPRuntime.setLauncherMode(true); for(String ext : extractExtensions) { ResourceLoader.addExtractExtension(ext); } // load icons--this will also set jarpath, if any String imageFile = "/org/opensourcephysics/resources/tools/images/launch.gif"; //$NON-NLS-1$ launchIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/launch.gif"; //$NON-NLS-1$ launchIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/launched.gif"; //$NON-NLS-1$ launchedIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/singleton.gif"; //$NON-NLS-1$ singletonIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/nofile.gif"; //$NON-NLS-1$ noFileIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/greenfile.gif"; //$NON-NLS-1$ greenFileIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/magentafile.gif"; //$NON-NLS-1$ magentaFileIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/link.gif"; //$NON-NLS-1$ linkIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/html.gif"; //$NON-NLS-1$ htmlIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/launchempty.gif"; //$NON-NLS-1$ launchEmptyIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/launchEJS.gif"; //$NON-NLS-1$ ejsIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/nav_open.gif"; //$NON-NLS-1$ navOpenIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/nav_closed.gif"; //$NON-NLS-1$ navClosedIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/undo.gif"; //$NON-NLS-1$ backIcon = loadIcon(imageFile); imageFile = "/org/opensourcephysics/resources/tools/images/redo.gif"; //$NON-NLS-1$ forwardIcon = loadIcon(imageFile); } /** * Constructs a bare Launcher with a splash screen. */ public Launcher() { this(true); } /** * Constructs a bare Launcher with or without a splash screen. * * @param splash true to show the splash screen */ public Launcher(boolean splash) { createGUI(splash); XML.setLoader(LaunchSet.class, new LaunchSet()); // if statement added by W. Christian if(OSPRuntime.applet==null) { // never allow new VM if Launcher was instantiated by an applet // determine whether launching in new VM is possible; may not be possible in Java Web Start // create a test launch thread Runnable launchRunner = new Runnable() { public void run() { // prevent single VM in Vista and Linux if(OSPRuntime.isVista()||OSPRuntime.isLinux()) { return; } try { Process proc = Runtime.getRuntime().exec("java"); //$NON-NLS-1$ // if it made it this far, new VM is allowed (?) newVMAllowed = true; proc.destroy(); } catch(Exception ex) { /** empty block */ } } }; try { Thread thread = new Thread(launchRunner); thread.start(); thread.join(5000); // wait for join but don't wait over 5 seconds } catch(InterruptedException ex) { /** empty block */ } } if(FontSizer.getLevel()!=0) { setFontLevel(FontSizer.getLevel()); } else { refreshStringResources(); refreshGUI(); } // center frame on the screen Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); int x = (dim.width-frame.getBounds().width)/2; int y = (dim.height-frame.getBounds().height)/2; frame.setLocation(x, y); } /** * Constructs a Launcher and opens the specified xml file. * * @param fileName the name of the xml file */ public Launcher(String fileName) { this(fileName, (fileName==null)||!fileName.startsWith("<?xml")); //$NON-NLS-1$ } /** * Constructs a Launcher and opens the specified xml file. * * @param fileName the name of the xml file * @param splash true to show the splash screen */ public Launcher(String fileName, boolean splash) { createGUI(splash); XML.setLoader(LaunchSet.class, new LaunchSet()); String path = null; if(fileName==null) { // look for default file with launchJarName or defaultFileName if(OSPRuntime.getLaunchJarName()!=null) { fileName = XML.stripExtension(OSPRuntime.getLaunchJarName())+".xset"; //$NON-NLS-1$ path = open(fileName); } if(path==null) { fileName = defaultFileName+".xset"; //$NON-NLS-1$ path = open(fileName); } if(path==null) { fileName = defaultFileName+".xml"; //$NON-NLS-1$ path = open(fileName); } } else { path = open(fileName); } if(FontSizer.getLevel()!=0) { setFontLevel(FontSizer.getLevel()); } else { refreshStringResources(); refreshGUI(); } // center frame on the screen Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); int x = (dim.width-frame.getBounds().width)/2; int y = (dim.height-frame.getBounds().height)/2; frame.setLocation(x, y); } /** * Whether exiting invokes System.exit() * @param _can */ public void setCanExit(boolean _can) { this.canExit = _can; } /** * Gets the content pane. * * @return the content pane */ public Container getContentPane() { return contentPane; } /** * Gets the size. * * @return the size Dimension object */ public Dimension getSize() { return contentPane.getSize(); } /** * Sets the size. * * @param dim the size Dimension */ public void setSize(Dimension dim) { contentPane.setPreferredSize(dim); frame.pack(); } /** * Gets the divider location. * * @return the divider location */ public int getDivider() { return divider; } /** * Sets the divider location. * * @param loc the divider location */ public void setDivider(int loc) { divider = loc; refreshGUI(); } /** * Reports visibility of the Launcher frame. * * @return true if visible */ public boolean isVisible() { return frame.isVisible(); } /** * Sets the visibility of the Launcher frame. * * @param visible true to show the frame */ public void setVisible(boolean visible) { frame.setVisible(visible); } /** * Sets navigation button visibility * * @param vis true to show navigation buttons */ public void setNavigationVisible(boolean vis) { navigationVisible = vis; LaunchNode node = getSelectedNode(); if((node!=null)&&node.isButtonView()) { showButtonView(node); } else { showTabbedPaneView(); } } /** * Clears the navigation history. */ public void clearHistory() { undoManager.discardAllEdits(); backButton.setEnabled(false); forwardButton.setEnabled(false); } /** * Sets the editorEnabled property. * * @param enabled true to enable editing from within Launcher */ public void setEditorEnabled(boolean enabled) { editorEnabled = enabled; } /** * Sets the hyperlinksEnabled property for all nodes. * * @param enabled true to enable hyperlinks in html pages */ public void setHyperlinksEnabled(boolean enabled) { for(int i = 0; i<getTabCount(); i++) { LaunchPanel tab = getTab(i); Enumeration<?> e = tab.getRootNode().breadthFirstEnumeration(); while(e.hasMoreElements()) { LaunchNode next = (LaunchNode) e.nextElement(); setHyperlinksEnabled(next, enabled); } } } /** * Sets the hyperlinksEnabled property for a specified node. * @param node * @param enabled true to enable hyperlinks in node's html pages */ public void setHyperlinksEnabled(LaunchNode node, boolean enabled) { for(int i = 0; i<node.getDisplayTabCount(); i++) { LaunchNode.DisplayTab html = node.getDisplayTab(i); html.hyperlinksEnabled = enabled; } } /** * Gets the LaunchPanel in the selected tab. May return null. * * @return the LaunchPanel */ public LaunchPanel getSelectedTab() { return(LaunchPanel) tabbedPane.getSelectedComponent(); } /** * Sets the selected tab by name and returns its LaunchPanel, * or null if tab not found. The path passed to this method * is one or more node names separated by /. * * @param path a path starting with the name of the tab's root node * @return the LaunchPanel containing the root */ public LaunchPanel setSelectedTab(String path) { if(path==null) { return null; } // extract root name from path String rootName = path; int n = path.indexOf("/"); //$NON-NLS-1$ if (n>-1) { rootName = path.substring(0, n); } for(int i = 0; i<tabbedPane.getTabCount(); i++) { LaunchPanel tab = getTab(i); // if(path.startsWith(tab.getRootNode().name)) { if(rootName.equals(tab.getRootNode().name)) { tabbedPane.setSelectedComponent(tab); return tab; } } return null; } /** * Sets the selected tab and returns its LaunchPanel, * or null if tab not found. * * @param tab the tab to select * @return the LaunchPanel containing the root */ public LaunchPanel setSelectedTab(LaunchPanel tab) { tabbedPane.setSelectedComponent(tab); return(tab==tabbedPane.getSelectedComponent()) ? tab : null; } /** * Gets the selected launch node. May return null. * * @return the selected launch node */ public LaunchNode getSelectedNode() { if(getSelectedTab()==null) { selectedNode = null; } else { selectedNode = getSelectedTab().getSelectedNode(); } return selectedNode; } /** * Sets the selected node by path and returns the node, * or null if node not found. Path is node names separated by / or \ * * @param path the path of the node * @return the LaunchNode */ public LaunchNode setSelectedNode(String path) { return setSelectedNode(path, 0, null); } /** * Sets the selected node by path and returns the node, * or null if node not found. Path is node names separated by / or \ * * @param path the path of the node * @param tabNumber the display tab number * @return the LaunchNode */ public LaunchNode setSelectedNode(String path, int tabNumber) { return setSelectedNode(path, tabNumber, null); } /** * Sets the selected node by path and returns the node, * or null if node not found. Path is node names separated by / or \ * * @param path the path of the node * @param tabNumber the display tab number * @param url the URL to display * @return the LaunchNode */ public LaunchNode setSelectedNode(String path, int tabNumber, URL url) { if(path==null) { return null; } path = XML.forwardSlash(path); setSelectedTab(path); LaunchPanel tab = getSelectedTab(); if(tab==null) { return null; } Enumeration<?> e = tab.getRootNode().breadthFirstEnumeration(); while(e.hasMoreElements()) { LaunchNode node = (LaunchNode) e.nextElement(); if(path.equals(node.getPathString())) { // found node tab.setSelectedNode(node, tabNumber, url); return node; } } return null; } /** * Sets the selected node by path and returns the node, * or null if node not found. Path is node names separated by / or \ * * @param keywords the keywords of the node * @param tabNumber the tab to display * @return the LaunchNode */ public LaunchNode setSelectedNodeByKey(String keywords, int tabNumber) { if((keywords==null)||keywords.equals("")) { //$NON-NLS-1$ return null; } // strip anchor, if any, from keywords String anchor = null; int n = keywords.indexOf("#"); //$NON-NLS-1$ if (n>-1) { anchor = keywords.substring(n); keywords = keywords.substring(0, n); } for(int i = 0; i<getTabCount(); i++) { LaunchPanel tab = getTab(i); Enumeration<?> e = tab.getRootNode().breadthFirstEnumeration(); while(e.hasMoreElements()) { LaunchNode node = (LaunchNode) e.nextElement(); if (keywords.equals(node.keywords)) { // found node setSelectedTab(tab); URL url = node.getDisplayTab(tabNumber).url; if (url!=null && anchor!=null) { try { url = new URL(url, anchor); tab.setSelectedNode(node, tabNumber, url); return node; } catch (MalformedURLException e1) { } } tab.setSelectedNode(node, tabNumber); return node; } } } return null; } /** * Gets the root node of the selected launch tree. May return null. * * @return the root node */ public LaunchNode getRootNode() { if(getSelectedTab()==null) { return null; } return getSelectedTab().getRootNode(); } /** * Gets the current number of tabs (LaunchPanels) * * @return the tab count */ public int getTabCount() { return tabbedPane.getTabCount(); } /** * Gets the launch panel at the specified tab index. May return null. * * @param i the tab index * @return the launch panel */ public LaunchPanel getTab(int i) { if(i>=tabbedPane.getTabCount()) { return null; } return(LaunchPanel) tabbedPane.getComponentAt(i); } /** * Gets the html tab at the specified index. * * @param i the tab index * @return the html tab */ public HTMLPane getHTMLTab(int i) { while(i>=htmlTabList.size()) { htmlTabList.add(new HTMLPane()); } return htmlTabList.get(i); } /** * Gets the html tab count. * * @return the html tab count */ public int getHTMLTabCount() { return htmlTabList.size(); } /** * Opens an xml document and selects a tab and/or node specified by name. * args[0] may be a relative path, absolute path, or self-contained xml string. * args[1] may be a tab name and/or node path. * * @param args the arguments * @return the absolute path, or null if failed or currently open */ public String open(String[] args) { if((args==null)||(args.length==0)) { return null; } // arg[0] is file name or xml string to open String path = open(args[0]); // arg[1] is tab name or tab/node names if((args.length>1)&&(args[1]!=null)) { setSelectedNode(args[1]); } return path; } /** * Opens an xml document specified by name and displays it in a new tab * (or selects the tab if already open). * Name may be a relative path, absolute path, or self-contained xml string. * * @param name the name * @return the absolute path, or null if failed or currently open */ public String open(String name) { if((name==null)||name.equals("")) { //$NON-NLS-1$ return null; } // add search paths to resource loader in last-to-first order ResourceLoader.addSearchPath(resourcesPath); ResourceLoader.addSearchPath(tabSetBasePath); String path = name; String absolutePath = ""; //$NON-NLS-1$ XMLControlElement control = new XMLControlElement(); // look for self-contained xml string (commonly used for previews) if(name.startsWith("<?xml")) { //$NON-NLS-1$ control.readXML(name); if(control.failedToRead()) { return null; } } if(control.getObjectClassName().equals(Object.class.getName())) { // did not read xml string // read the named file and get its absolute path // look first in launch jar directory (added by D Brown 2007-11-05) String jarBase = OSPRuntime.getLaunchJarDirectory(); absolutePath = control.read(XML.getResolvedPath(path, jarBase)); if(control.failedToRead()) { // look using relative path absolutePath = control.read(path); } } if(control.failedToRead()) { String jar = XML.stripExtension(OSPRuntime.getLaunchJarName()); if(!name.startsWith(defaultFileName)&&(jar!=null)&&!name.startsWith(jar)) { OSPLog.info(LaunchRes.getString("Log.Message.InvalidXML")+" "+name); //$NON-NLS-1$//$NON-NLS-2$ JOptionPane.showMessageDialog(null, LaunchRes.getString("Dialog.InvalidXML.Message")+" \""+name+"\"", //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ LaunchRes.getString("Dialog.InvalidXML.Title"), JOptionPane.WARNING_MESSAGE); //$NON-NLS-1$ } return null; } OSPLog.fine(name); // absolute and return paths are empty if name is xml string String returnPath = XML.forwardSlash(absolutePath); // set non-null jar base path if opening from a jar or zip file String jarName = LaunchRes.getString("Splash.Label.Internal"); //$NON-NLS-1$ int zip = Math.max(returnPath.indexOf("jar!"), returnPath.indexOf("zip!")); //$NON-NLS-1$//$NON-NLS-2$ zip = Math.max(zip, returnPath.indexOf("trz!")); //$NON-NLS-1$ if(zip>-1) { jarName = XML.getName(returnPath.substring(0, zip+3)); String s = XML.getDirectoryPath(returnPath.substring(0, zip+3)); jarBasePath = s.equals("") //$NON-NLS-1$ ? XML.forwardSlash(System.getProperty("user.dir", "")) //$NON-NLS-1$//$NON-NLS-2$ : s; returnPath = returnPath.substring(zip+5); } else { jarBasePath = null; } Class<?> type = control.getObjectClass(); if(LaunchSet.class.equals(type)) { // show name and icon in splash label if splashing if(!returnPath.equals("")&&(splashDialog!=null)&&splashDialog.isVisible()) { //$NON-NLS-1$ Resource res = ResourceLoader.getResource(path); String loading = LaunchRes.getString("Log.Message.Loading")+": "; //$NON-NLS-1$//$NON-NLS-2$ if(res.getFile()!=null) { // external file OSPLog.info(loading+res.getAbsolutePath()); // pale blue background and magenta icon if external file splashDialog.getContentPane().setBackground(new Color(242, 242, 255)); splashPathLabel.setIcon(magentaFileIcon); splashPathLabel.setText(loading+name); } else { loading = LaunchRes.getString("Log.Message.LoadingFrom")+" "+ //$NON-NLS-1$//$NON-NLS-2$ jarName+": "+name; //$NON-NLS-1$ boolean internal = jarName.equals(LaunchRes.getString("Splash.Label.Internal"))|| //$NON-NLS-1$ jarName.equals(OSPRuntime.getLaunchJarName()); if(internal) { // pale yellow background and green icon if internal resource splashDialog.getContentPane().setBackground(new Color(255, 255, 228)); splashPathLabel.setIcon(greenFileIcon); } else { // pale blue background and magenta icon if external resource splashDialog.getContentPane().setBackground(new Color(242, 242, 255)); splashPathLabel.setIcon(magentaFileIcon); } splashPathLabel.setText(loading); loading = LaunchRes.getString("Log.Message.Loading")+": "; //$NON-NLS-1$//$NON-NLS-2$ OSPLog.info(loading+res.getAbsolutePath()); } } // close all open tabs if(!removeAllTabs()) { // password = prevPassword; return null; } // set paths tabSetName = XML.getName(returnPath); // set static tabset base path unless reading xml string (return path "") if(!returnPath.equals("")) { //$NON-NLS-1$ if(jarBasePath!=null) { // xset loaded from internal jar entry tabSetBasePath = ""; //$NON-NLS-1$ } else { tabSetBasePath = XML.getDirectoryPath(returnPath); } } // load the xset data OSPLog.finest(LaunchRes.getString("Log.Message.Loading")+": "+ //$NON-NLS-1$//$NON-NLS-2$ returnPath); // don't post edits during loading boolean post = postEdits; postEdits = false; LaunchSet tabset = new LaunchSet(this, tabSetName); control.loadObject(tabset); if(tabset.failedToLoad) { if(tabSetName.equals(XML.getName(returnPath))) { tabSetName = null; } returnPath = null; } else if((splashDialog!=null)&&splashDialog.isVisible()) { LaunchNode root = getRootNode(); if((root!=null)&&!root.getAuthor().trim().equals("")) { //$NON-NLS-1$ String by = LaunchRes.getString("Label.Author")+": "; //$NON-NLS-1$//$NON-NLS-2$ creditsLabel.setText(by+root.getAuthor()); } } // check for password // String prevPassword = password; // if(control.getPassword()!=null) { // boolean pwRequired = control.getBoolean("pw_required_by_launcher"); //$NON-NLS-1$ // if((Launcher.this instanceof LaunchBuilder)||pwRequired) { // if(Password.verify(control.getPassword(), returnPath)) { // password = control.getPassword(); // } // else { // LaunchNode root = getRootNode(); // if((root!=null)&& root.buttonView) { // System.out.println("buttons"); // root.enabled = false; // showButtonView(root); // return returnPath; // } // } // } // } changedFiles.clear(); OSPLog.fine("returning "+returnPath); //$NON-NLS-1$ postEdits = post; return returnPath; } else if(LaunchNode.class.equals(type)) { // load the xml file data OSPLog.finest(LaunchRes.getString("Log.Message.Loading")+": "+path); //$NON-NLS-1$//$NON-NLS-2$ LaunchNode node = new LaunchNode(LaunchRes.getString("NewNode.Name")); //$NON-NLS-1$ // assign file name BEFORE loading node node.setFileName(XML.getPathRelativeTo(returnPath, tabSetBasePath)); control.loadObject(node); String tabName = getDisplayName(returnPath); // if node is already open, select the tab and return null for(int i = 0; i<tabbedPane.getComponentCount(); i++) { if(tabbedPane.getTitleAt(i).equals(tabName)) { LaunchNode root = ((LaunchPanel) tabbedPane.getComponent(i)).getRootNode(); if(root.matches(node)) { tabbedPane.setSelectedIndex(i); return null; } } } // don't post edits during loading postEdits = false; // if no tabset is open, then create new one if(tabSetName==null) { tabSetName = LaunchRes.getString("Tabset.Name.New"); //$NON-NLS-1$ title = null; tabSetBasePath = XML.getDirectoryPath(returnPath); editorEnabled = true; } addTab(node); Enumeration<?> e = node.breadthFirstEnumeration(); while(e.hasMoreElements()) { LaunchNode next = (LaunchNode) e.nextElement(); next.setLaunchClass(next.launchClassName); } postEdits = true; return returnPath; // successfully opened and added to tab } else { OSPLog.info(LaunchRes.getString("Log.Message.NotLauncherFile")); //$NON-NLS-1$ if(name.length()>20) { name = name.substring(0, 20)+"..."; //$NON-NLS-1$ } JOptionPane.showMessageDialog(null, LaunchRes.getString("Dialog.NotLauncherFile.Message")+ //$NON-NLS-1$ " \""+name+"\"", //$NON-NLS-1$//$NON-NLS-2$ LaunchRes.getString("Dialog.NotLauncherFile.Title"), //$NON-NLS-1$ JOptionPane.WARNING_MESSAGE); } return null; } /** * Sets the components to be displayed at the right end of the navigation bar. * * @param comps the components */ public void setNavbarRightEndComponents(Component[] comps) { // remove old right end components from navbar if((navbar.getComponentCount()>1)&&(navbarAddOns!=null)) { navbar.remove(navSpacer); for(Component c : navbarAddOns) { if(c!=null) { navbar.remove(c); } } } // set new right end components navbarAddOns = comps; // add new right end components if not null if((navbar.getComponentCount()>1)&&(navbarAddOns!=null)) { navbar.add(navSpacer); for(Component c : navbarAddOns) { if(c!=null) { navbar.add(c); } } } navbar.revalidate(); } // ______________________________ protected methods _____________________________ /** * Creates a LaunchPanel with the specified root and adds it to a new tab. * * @param root the root node * @return true if tab was added */ protected boolean addTab(LaunchNode root) { final LaunchPanel tab = new LaunchPanel(root, this); tabs.add(tab); if(root.isHiddenInLauncher()&&!(this instanceof LaunchBuilder)) { return false; } tab.tree.setCellRenderer(new LaunchRenderer()); tab.tree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { newNodeSelected = true; refreshGUI(); } }); tab.tree.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if(splashDialog!=null) { splashDialog.dispose(); } tab.tree.removeMouseListener(this); } }); tab.tree.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { popup.removeAll(); handleMousePressed(e, tab); } }); tabbedPane.addTab(root.toString(), tab); tabbedPane.setSelectedComponent(tab); tab.setSelectedNode(root); if(!root.tooltip.equals("")) { //$NON-NLS-1$ tabbedPane.setToolTipTextAt(tabbedPane.getSelectedIndex(), root.tooltip); } tab.dataPanel.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { divider = tab.splitPane.getDividerLocation(); } }); return true; } /** * Displays a button view of the specified node. * * @param node the node */ protected void showButtonView(final LaunchNode node) { LaunchNode.DisplayTab html = node.getDisplayTab(0); if((html!=null)&&(html.url!=null)) { setLinksEnabled(textPane, node.enabled && html.hyperlinksEnabled); try { if(html.url.getContent()!=null) { final java.net.URL url = html.url; Runnable runner = new Runnable() { public void run() { try { textPane.setPage(url); } catch(IOException ex) { OSPLog.fine(LaunchRes.getString("Log.Message.BadURL")+" "+url); //$NON-NLS-1$//$NON-NLS-2$ } } }; SwingUtilities.invokeLater(runner); } } catch(IOException ex) { OSPLog.finest(LaunchRes.getString("Log.Message.BadURL")+" "+html.url); //$NON-NLS-1$//$NON-NLS-2$ if(showText) { textPane.setContentType(LaunchPanel.TEXT_TYPE); textPane.setText(node.description); } } } else if(showText) { textPane.setContentType(LaunchPanel.TEXT_TYPE); textPane.setText(node.description); } contentPane.removeAll(); if(navigationVisible&&!(this instanceof LaunchBuilder)) { contentPane.add(navbar, BorderLayout.NORTH); } contentPane.add(textScroller, BorderLayout.CENTER); // add buttons (child nodes) to SOUTH JPanel box = new JPanel(); contentPane.add(box, BorderLayout.SOUTH); ActionListener openAction = new AbstractAction() { public void actionPerformed(ActionEvent e) { // open linked file and node int i = Integer.parseInt(e.getActionCommand()); LaunchNode child = (LaunchNode) node.getChildAt(i); if(child.getLaunchClass()==null) { String[] prevArgs = undoManager.getLauncherState(); if(open(child.args)!=null) { if(prevArgs!=null) { UndoableEdit edit = undoManager.new LoadEdit(child.args, prevArgs); undoSupport.postEdit(edit); } refreshGUI(); } } else { child.launch(); } } }; for(int i = 0; i<node.getChildCount(); i++) { LaunchNode child = (LaunchNode) node.getChildAt(i); JButton button = new JButton(child.name); button.addActionListener(openAction); button.setActionCommand(String.valueOf(i)); button.setToolTipText(child.tooltip); button.setEnabled(node.enabled); box.add(button); } frame.validate(); refreshGUI(); frame.repaint(); } /** * Displays the standard tabbed pane view. */ protected void showTabbedPaneView() { contentPane.removeAll(); if(navigationVisible&&!(this instanceof LaunchBuilder)) { contentPane.add(navbar, BorderLayout.NORTH); } contentPane.add(tabbedPane, BorderLayout.CENTER); frame.validate(); refreshGUI(); frame.repaint(); } /** * Opens an xml file selected with a chooser. * * @return the name of the opened file */ protected String open() { getXMLChooser().setFileFilter(launcherFileFilter); int result = getXMLChooser().showOpenDialog(null); if(result==JFileChooser.APPROVE_OPTION) { File file = getXMLChooser().getSelectedFile(); String fileName = XML.forwardSlash(file.getAbsolutePath()); OSPRuntime.chooserDir = XML.getDirectoryPath(fileName); return open(fileName); } return null; } /** * Removes the selected tab. * * @return true if the tab was removed */ protected boolean removeSelectedTab() { int i = tabbedPane.getSelectedIndex(); if(i<0) { return false; } String[] prevArgs = undoManager.getLauncherState(); tabs.remove(getTab(i)); tabbedPane.removeTabAt(i); previousNode = selectedNode; newNodeSelected = true; if(tabbedPane.getTabCount()==0) { tabSetName = null; title = null; if(prevArgs!=null) { // null new args indicate not redoable LauncherUndo.LoadEdit edit = undoManager.new LoadEdit(null, prevArgs); undoSupport.postEdit(edit); } } refreshGUI(); return true; } /** * Removes all tabs. * * @return true if all tabs were removed */ protected boolean removeAllTabs() { int n = tabbedPane.getTabCount(); if(n==0) { return true; } boolean post = postEdits; postEdits = false; for(int i = n-1; i>=0; i--) { tabbedPane.removeTabAt(i); } if(tabbedPane.getTabCount()==0) { tabSetName = null; title = null; password = null; } else { previousNode = selectedNode; newNodeSelected = true; } refreshGUI(); tabs.clear(); for(int i = 0; i<tabbedPane.getTabCount(); i++) { tabs.add(tabbedPane.getComponentAt(i)); } postEdits = post; return tabbedPane.getTabCount()==0; } /** * Refreshes string resources. */ protected void refreshStringResources() { fileMenu.setText(LaunchRes.getString("Menu.File")); //$NON-NLS-1$ displayMenu.setText(LaunchRes.getString("Menu.Display")); //$NON-NLS-1$ openItem.setText(LaunchRes.getString("Menu.File.Open")); //$NON-NLS-1$ passwordItem.setText(LaunchRes.getString("Launcher.MenuItem.EnterPassword")); //$NON-NLS-1$ closeTabItem.setText(LaunchRes.getString("Menu.File.CloseTab")); //$NON-NLS-1$ closeAllItem.setText(LaunchRes.getString("Menu.File.CloseAll")); //$NON-NLS-1$ editItem.setText(LaunchRes.getString("Menu.File.Edit")); //$NON-NLS-1$ backItem.setText(LaunchRes.getString("Menu.File.Back")); //$NON-NLS-1$ helpMenu.setText(LaunchRes.getString("Menu.Help")); //$NON-NLS-1$ logItem.setText(LaunchRes.getString("Menu.Help.MessageLog")); //$NON-NLS-1$ inspectItem.setText(LaunchRes.getString("Menu.Help.Inspect")); //$NON-NLS-1$ String s = XML.getSimpleClassName(this.getClass()); String about = LaunchRes.getString("Menu.Help.About") //$NON-NLS-1$ +" "+s+"..."; //$NON-NLS-1$//$NON-NLS-2$ aboutItem.setText(about); String authorInfo = LaunchRes.getString("Menu.Help.AuthorInfo")+"..."; //$NON-NLS-1$//$NON-NLS-2$ LaunchNode node = getSelectedNode(); if(node!=null) { authorInfo = LaunchRes.getString("Help.About.Title")+" \"" //$NON-NLS-1$//$NON-NLS-2$ +node.getName()+"\"..."; //$NON-NLS-1$ } authorInfoItem.setText(authorInfo); diagnosticMenu.setText(LaunchRes.getString("Menu.Help.Diagnostics")); //$NON-NLS-1$ if(exitItem!=null) { exitItem.setText(LaunchRes.getString("Menu.File.Exit")); //$NON-NLS-1$ } languageMenu.setText(LaunchRes.getString("Menu.Display.Language")); //$NON-NLS-1$ sizeUpItem.setText(LaunchRes.getString("Menu.Display.IncreaseFontSize")); //$NON-NLS-1$ sizeDownItem.setText(LaunchRes.getString("Menu.Display.DecreaseFontSize")); //$NON-NLS-1$ // lookFeelItem.setText(LaunchRes.getString("Menu.Display.LookAndFeel")); //$NON-NLS-1$ lookFeelMenu.setText(LaunchRes.getString("Menu.Display.LookFeel")); //$NON-NLS-1$ javaLFItem.setText(LaunchRes.getString("MenuItem.JavaLookFeel")); //$NON-NLS-1$ systemLFItem.setText(LaunchRes.getString("MenuItem.SystemLookFeel")); //$NON-NLS-1$ defaultLFItem.setText(LaunchRes.getString("MenuItem.DefaultLookFeel")); //$NON-NLS-1$ if(openFromJarMenu!=null) { openFromJarMenu.setText(LaunchRes.getString("Menu.File.OpenFromJar")); //$NON-NLS-1$ } Locale[] locales = OSPRuntime.getInstalledLocales(); for(int i = 0; i<locales.length; i++) { if(locales[i].getLanguage().equals(LaunchRes.resourceLocale.getLanguage())) { languageItems[i].setSelected(true); } } } /** * Refreshes the GUI. */ protected void refreshGUI() { refreshMemoryButton(); // set tab properties LaunchPanel tab = getSelectedTab(); boolean rootDisabled = tab!=null && !tab.getRootNode().enabled; // set frame title String name = (title==null) ? tabSetName : title; if(name==null) { name = LaunchRes.getString("Frame.Title"); //$NON-NLS-1$ } else { name = LaunchRes.getString("Frame.Title")+": "+name; //$NON-NLS-1$//$NON-NLS-2$ } if (rootDisabled) { name += " "+LaunchRes.getString("Launcher.Title.NeedsPassword"); //$NON-NLS-1$//$NON-NLS-2$ } frame.setTitle(name); if(tab!=null) { tab.tree.setEnabled(tab.getRootNode().enabled); tabbedPane.setEnabled(tab.getRootNode().enabled); tab.splitPane.setDividerLocation(divider); } // update undo/redo buttons backButton.setEnabled(undoManager.canUndo()); forwardButton.setEnabled(undoManager.canRedo()); // rebuild file menu fileMenu.removeAll(); if(undoManager.canReload()) { fileMenu.add(backItem); } if((OSPRuntime.applet==null)) { if(fileMenu.getItemCount()>0) { fileMenu.addSeparator(); } fileMenu.add(openItem); } if(openFromJarMenu!=null) { fileMenu.add(openFromJarMenu); } if (rootDisabled) { fileMenu.add(passwordItem); } if(OSPRuntime.applet!=null) { // added by W. Christian fileMenu.add(hideItem); return; } if(tab!=null) { if(fileMenu.getItemCount()>0) { fileMenu.addSeparator(); } boolean showCloseTab = true; if(getClass()==Launcher.class) { if(tab.getRootNode().isButtonView()) { showCloseTab = false; name = LaunchRes.getString("Frame.Title")+": "+ //$NON-NLS-1$//$NON-NLS-2$ tab.getRootNode().name; } else if(tabbedPane.getTabCount()==1) { showCloseTab = false; } } if(showCloseTab) { fileMenu.add(closeTabItem); closeAllItem.setText(LaunchRes.getString("Menu.File.CloseAll")); //$NON-NLS-1$ } else { closeAllItem.setText(LaunchRes.getString("MenuItem.Close")); //$NON-NLS-1$ } fileMenu.add(closeAllItem); } if(editorEnabled && !OSPRuntime.isWebStart() && !rootDisabled) { fileMenu.addSeparator(); fileMenu.add(editItem); } // added by W. Christian to capture screen JMenu printMenu = new JMenu(DisplayRes.getString("DrawingFrame.Print_menu_title")); //$NON-NLS-1$ JMenuItem printFrameItem = new JMenuItem(DisplayRes.getString("DrawingFrame.PrintFrame_menu_item")); //$NON-NLS-1$ JMenuItem saveFrameAsEPSItem = new JMenuItem(DisplayRes.getString("DrawingFrame.SaveFrameAsEPS_menu_item")); //$NON-NLS-1$ printMenu.add(printFrameItem); printMenu.add(saveFrameAsEPSItem); fileMenu.add(printMenu); // print action printFrameItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { PrintUtils.printComponent(frame); } }); // save as EPS action saveFrameAsEPSItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { PrintUtils.saveComponentAsEPS(frame); } catch(IOException ex) { /** empty block */ } } }); // end W. Christian code fileMenu.addSeparator(); if(exitItem!=null) { fileMenu.add(exitItem); } boolean rootEnabled = getRootNode()!=null && getRootNode().enabled; inspectItem.setEnabled(rootEnabled && (password==null || (this instanceof LaunchBuilder))); sizeDownItem.setEnabled(fileMenu.getFont().getSize()>baseMenuFontSize); } /** * Creates applet GUI items. */ private void appletGUI() { // added by W. Christian hideItem = new JMenuItem(LaunchRes.getString("Menu.File.Hide")); //$NON-NLS-1$ hideItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { exit(); } }); } /** * Creates the GUI. * * @param splash true to show the splash screen */ protected void createGUI(boolean splash) { // create applet GUI items appletGUI(); // added by W. Christian // get existing frames Frame[] frames = Frame.getFrames(); for(int i = 0, n = frames.length; i<n; i++) { existingFrames.add(frames[i]); } // instantiate the OSPLog and Translator OSPLog.getOSPLog(); OSPRuntime.getTranslator(); // set up the undo system undoManager = new LauncherUndo(this); undoSupport = new UndoableEditSupport(); undoSupport.addUndoableEditListener(undoManager); // create the frame frame = new LauncherFrame(); existingFrames.add(frame); if(splash&&(OSPRuntime.applet==null)) { splash(); } // create xml inspector xmlInspector = new JDialog(frame, false); xmlInspector.setSize(new java.awt.Dimension(600, 300)); tableInspector = new XMLTableInspector(true, false); // center inspectors on screen Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); int x = (dim.width-xmlInspector.getBounds().width)/2; int y = (dim.height-xmlInspector.getBounds().height)/2; xmlInspector.setLocation(x, y); x = (dim.width-tableInspector.getBounds().width)/2; y = (dim.height-tableInspector.getBounds().height)/2; tableInspector.setLocation(x, y); contentPane = new JPanel(new BorderLayout()); contentPane.setPreferredSize(new Dimension(wInit, hInit)); frame.setContentPane(contentPane); frame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); // create navigation bar navbar = new JToolBar(); navbar.setFloatable(false); navbar.setBorder(BorderFactory.createEtchedBorder()); navButton = new JButton(navOpenIcon); navButton.setBorder(BorderFactory.createEmptyBorder()); navButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(navbar.getComponentCount()>1) { navbar.remove(backButton); navbar.remove(forwardButton); navbar.remove(navSpacer); if(navbarAddOns!=null) { for(Component c : navbarAddOns) { if(c!=null) { navbar.remove(c); } } } navButton.setIcon(navClosedIcon); } else { navbar.add(backButton); navbar.add(forwardButton); if(navbarAddOns!=null) { navbar.add(navSpacer); for(Component c : navbarAddOns) { if(c!=null) { navbar.add(c); } } } navButton.setIcon(navOpenIcon); } navbar.revalidate(); } }); navbar.add(navButton); backButton = new JButton(backIcon); backButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { undoManager.undo(); refreshGUI(); } }); navbar.add(backButton); forwardButton = new JButton(forwardIcon); forwardButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { undoManager.redo(); refreshGUI(); } }); navbar.add(forwardButton); backButton.setEnabled(false); forwardButton.setEnabled(false); // create memory button memoryButton = new JButton(); memoryButton.setOpaque(false); memoryButton.setBorderPainted(false); Border space = BorderFactory.createEmptyBorder(2, 4, 2, 3); Border line = BorderFactory.createLineBorder(Color.GRAY); memoryButton.setBorder(BorderFactory.createCompoundBorder(line, space)); Font font = memoryButton.getFont(); memoryButton.setFont(font.deriveFont(Font.PLAIN, font.getSize()-1)); memoryButton.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent e) { refreshMemoryButton(); memoryButton.setBorderPainted(true); } public void mouseExited(MouseEvent e) { memoryButton.setBorderPainted(false); } public void mousePressed(MouseEvent e) { JPopupMenu popup = new JPopupMenu(); JMenu menu = new JMenu(LaunchRes.getString("Launcher.Button.Memory.Popup.Relaunch")); //$NON-NLS-1$ ActionListener relauncher = new ActionListener() { public void actionPerformed(ActionEvent e) { JMenuItem item = (JMenuItem)e.getSource(); String text = item.getText(); text = text.substring(0, text.length()-2); long memorySize = Long.parseLong(text); relaunch(null, memorySize, frame); } }; java.lang.management.MemoryMXBean memory = java.lang.management.ManagementFactory.getMemoryMXBean(); long memSize = memory.getHeapMemoryUsage().getMax()/(1024*1024); int[] sizes = new int[] {64, 125, 250, 500, 1000}; for (int next: sizes) { if (xsetMemorySize<9*next/10 && (memSize<9*next/10 || memSize>11*next/10)) { JMenuItem item = new JMenuItem(next+"MB"); //$NON-NLS-1$ item.addActionListener(relauncher); menu.add(item); } } if (menu.getItemCount()>0) { popup.add(menu); popup.show(memoryButton, 0, memoryButton.getHeight()); } } }); Component[] comps = new Component[] { Box.createHorizontalGlue(), memoryButton, Box.createHorizontalStrut(2)}; setNavbarRightEndComponents(comps); // create text pane and scroller for button view textPane = new JTextPane(); textPane.setEditable(false); textScroller = new JScrollPane(textPane); // create tabbed pane tabbedPane = new JTabbedPane(SwingConstants.BOTTOM); contentPane.add(tabbedPane, BorderLayout.CENTER); tabbedPane.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { previousNode = selectedNode; selectedNode = getSelectedNode(); newNodeSelected = true; refreshSelectedTab(); if((previousNode!=null)&&(selectedNode!=null)) { // set html properties of newly selected node if((selectedNode.htmlURL==null)&&!selectedNode.tabData.isEmpty()) { int page = Math.max(0, selectedNode.tabNumber); LaunchNode.DisplayTab htmlData = selectedNode.tabData.get(page); selectedNode.htmlURL = htmlData.url; selectedNode.tabNumber = page; // OSPLog.info("set selected node "+selectedNode+" to "+selectedNode.htmlURL //$NON-NLS-1$ // +"and tabnumber "+selectedNode.tabNumber); //$NON-NLS-1$ } // post undoable NavEdit if(postEdits) { UndoableEdit edit = undoManager.new NavEdit(previousNode, selectedNode); undoSupport.postEdit(edit); } } refreshGUI(); } }); tabListener = new MouseAdapter() { public void mousePressed(MouseEvent e) { if(contentPane.getTopLevelAncestor()!=frame) { return; } if(OSPRuntime.isPopupTrigger(e)) { // make popup and add items JPopupMenu popup = new JPopupMenu(); JMenuItem item = new JMenuItem(LaunchRes.getString("MenuItem.Close")); //$NON-NLS-1$ item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { removeSelectedTab(); } }); popup.add(item); popup.show(tabbedPane, e.getX(), e.getY()+8); } } }; tabbedPane.addMouseListener(tabListener); // create the menu bar final JMenuBar menubar = new JMenuBar(); fileMenu = new JMenu(); fileMenu.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if(splashDialog!=null) { splashDialog.dispose(); } fileMenu.removeMouseListener(this); } }); menubar.add(fileMenu); openItem = new JMenuItem(); int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); openItem.setAccelerator(KeyStroke.getKeyStroke('O', mask)); openItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String[] prevArgs = undoManager.getLauncherState(); if(prevArgs!=null) { String fileName = open(); if(fileName!=null) { String[] args = new String[] {fileName}; LauncherUndo.LoadEdit edit = undoManager.new LoadEdit(args, prevArgs); undoSupport.postEdit(edit); refreshGUI(); } } else { open(); refreshGUI(); } } }); passwordItem = new JMenuItem(); passwordItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(Password.verify(password, tabSetName)) { passwords.add(password); getRootNode().enabled = true; if(expansions!=null) { for(int i = 0; i<expansions.length; i++) { getTab(i).setExpandedNodes(expansions[i]); } } if (selectedPath!=null) setSelectedNode(selectedPath); changedFiles.clear(); refreshSelectedTab(); frame.validate(); } } }); closeTabItem = new JMenuItem(); closeTabItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { removeSelectedTab(); } }); closeAllItem = new JMenuItem(); closeAllItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String[] prevArgs = undoManager.getLauncherState(); if(removeAllTabs()&&(prevArgs!=null)) { // null new args indicate not redoable LauncherUndo.LoadEdit edit = undoManager.new LoadEdit(null, prevArgs); undoSupport.postEdit(edit); } refreshGUI(); } }); editItem = new JMenuItem(); editItem.setAccelerator(KeyStroke.getKeyStroke('E', mask)); if(OSPRuntime.isWebStart()) { editItem.setEnabled(false); } editItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if((password!=null)&&!pwRequiredToLoad&&!Password.verify(password, tabSetName)) { return; } if(previewing) { previewing = false; if(Launcher.this.spawner!=null) { LaunchNode node = Launcher.this.getSelectedNode(); if(node!=null) { Launcher.this.spawner.setSelectedNode(node.getPathString()); } } exit(); // edit previews by exiting to builder } else { LaunchBuilder builder; if(tabSetName==null) { builder = new LaunchBuilder(false); builder.newItem.doClick(); } else { LaunchSet tabset = new LaunchSet(Launcher.this, tabSetName); XMLControlElement control = new XMLControlElement(tabset); // set null password so LaunchBuilder opens without verification control.setPassword(null); builder = new LaunchBuilder(control.toXML()); LaunchNode node = Launcher.this.getSelectedNode(); if(node!=null) { builder.setSelectedNode(node.getPathString()); } builder.tabSetName = tabSetName; builder.password = password; } builder.spawner = Launcher.this; builder.jarBasePath = jarBasePath; Point p = Launcher.this.frame.getLocation(); builder.frame.setLocation(p.x+24, p.y+24); builder.frame.setVisible(true); builder.frame.pack(); builder.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); } } }); backItem = new JMenuItem(); backItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, mask)); backItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { undoManager.undo(); refreshGUI(); } }); displayMenu = new JMenu(); menubar.add(displayMenu); // language menu LaunchRes.addPropertyChangeListener("locale", new PropertyChangeListener() { //$NON-NLS-1$ public void propertyChange(PropertyChangeEvent e) { refreshStringResources(); refreshGUI(); } }); languageMenu = new JMenu(); final Locale[] locales = OSPRuntime.getInstalledLocales(); Action languageAction = new AbstractAction() { public void actionPerformed(ActionEvent e) { String language = e.getActionCommand(); OSPLog.finest("setting language to "+language); //$NON-NLS-1$ for(int i = 0; i<locales.length; i++) { if(language.equals(locales[i].getDisplayName())) { LaunchRes.setLocale(locales[i]); return; } } } }; ButtonGroup languageGroup = new ButtonGroup(); languageItems = new JMenuItem[locales.length]; for(int i = 0; i<locales.length; i++) { languageItems[i] = new JRadioButtonMenuItem(OSPRuntime.getDisplayLanguage(locales[i])); languageItems[i].setActionCommand(locales[i].getDisplayName()); languageItems[i].addActionListener(languageAction); languageMenu.add(languageItems[i]); languageGroup.add(languageItems[i]); } displayMenu.add(languageMenu); displayMenu.addSeparator(); // font size listener FontSizer.addPropertyChangeListener("level", new PropertyChangeListener() { //$NON-NLS-1$ public void propertyChange(PropertyChangeEvent e) { int level = ((Integer) e.getNewValue()).intValue(); setFontLevel(level); } }); sizeUpItem = new JMenuItem(); sizeUpItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { FontSizer.levelUp(); } }); displayMenu.add(sizeUpItem); sizeDownItem = new JMenuItem(); sizeDownItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { FontSizer.levelDown(); } }); displayMenu.add(sizeDownItem); displayMenu.addSeparator(); // create look and feel menu lookFeelMenu = new JMenu(); displayMenu.add(lookFeelMenu); Action lfAction = new AbstractAction() { public void actionPerformed(ActionEvent e) { setLookAndFeel(e.getActionCommand(), false); } }; String currentLF = UIManager.getLookAndFeel().getClass().getName(); // create specific LF menu items and select current LF specificLFGroup = new ButtonGroup(); LookAndFeelInfo[] lfInfo = UIManager.getInstalledLookAndFeels(); for(int i = 0; i<lfInfo.length; i++) { String next = lfInfo[i].getClassName(); String command = (next.indexOf("Nimbus")>-1) //$NON-NLS-1$ ? OSPRuntime.NIMBUS_LF : (next.indexOf("GTK")>-1) //$NON-NLS-1$ ? OSPRuntime.GTK_LF : (next.indexOf("Motif")>-1) //$NON-NLS-1$ ? OSPRuntime.MOTIF_LF : (next.indexOf("WindowsClassic")>-1) //$NON-NLS-1$ ? null : (next.indexOf("Windows")>-1) //$NON-NLS-1$ ? OSPRuntime.WINDOWS_LF : (next.indexOf("Metal")>-1) //$NON-NLS-1$ ? OSPRuntime.METAL_LF : null; if(command==null) { continue; } String name = XML.getName(lfInfo[i].getName()); JRadioButtonMenuItem item = new JRadioButtonMenuItem(name); specificLFGroup.add(item); item.setActionCommand(command); item.addActionListener(lfAction); lookFeelMenu.add(item); if(currentLF.equals(next)) { item.setSelected(true); } } // create generic LF items and select current LF genericLFGroup = new ButtonGroup(); defaultLFItem = new JRadioButtonMenuItem(); defaultLFItem.setSelected(true); defaultLFItem.setActionCommand(OSPRuntime.DEFAULT_LF); defaultLFItem.addActionListener(lfAction); genericLFGroup.add(defaultLFItem); javaLFItem = new JRadioButtonMenuItem(); javaLFItem.setActionCommand(OSPRuntime.CROSS_PLATFORM_LF); javaLFItem.addActionListener(lfAction); genericLFGroup.add(javaLFItem); systemLFItem = new JRadioButtonMenuItem(); systemLFItem.setActionCommand(OSPRuntime.SYSTEM_LF); systemLFItem.addActionListener(lfAction); genericLFGroup.add(systemLFItem); lookFeelMenu.addSeparator(); lookFeelMenu.add(javaLFItem); lookFeelMenu.add(systemLFItem); lookFeelMenu.add(defaultLFItem); helpMenu = new JMenu(); helpMenu.addMouseListener(new MouseAdapter() { // hide splash dialog if visible public void mousePressed(MouseEvent e) { if(splashDialog!=null) { splashDialog.dispose(); } helpMenu.removeMouseListener(this); } }); helpMenu.addMouseListener(new MouseAdapter() { // refresh authorInfo item public void mouseEntered(MouseEvent e) { mousePressed(e); } public void mousePressed(MouseEvent e) { LaunchNode node = getSelectedNode(); if(node!=null) { helpMenu.add(authorInfoItem); } else { helpMenu.remove(authorInfoItem); } } }); menubar.add(helpMenu); logItem = new JMenuItem(); logItem.setAccelerator(KeyStroke.getKeyStroke('L', mask)); logItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(OSPRuntime.applet==null) { // not running as applet so create and position the log. Point p0 = new Frame().getLocation(); OSPLog log = OSPLog.getOSPLog(); if((log.getLocation().x==p0.x)&&(log.getLocation().y==p0.y)) { Point p = frame.getLocation(); log.setLocation(p.x+28, p.y+28); } } OSPLog.showLog(); } }); helpMenu.add(logItem); inspectItem = new JMenuItem(); helpMenu.add(inspectItem); inspectItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { LaunchSet tabSet = new LaunchSet(Launcher.this, tabSetName); tabSet.showHiddenNodes = Launcher.this instanceof LaunchBuilder; XMLControl xml = new XMLControlElement(tabSet); XMLTreePanel treePanel = new XMLTreePanel(xml, false); xmlInspector.setContentPane(treePanel); xmlInspector.setTitle(LaunchRes.getString("Inspector.Title.TabSet")+" \""+getDisplayName(tabSetName)+"\""); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ xmlInspector.setVisible(true); } }); // diagnostics menu diagnosticMenu = new JMenu(); helpMenu.add(diagnosticMenu); JMenuItem jarItem = new JMenuItem("Jar"); //$NON-NLS-1$ jarItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Diagnostics.aboutLaunchJar(); } }); diagnosticMenu.add(jarItem); JMenuItem vmItem = new JMenuItem("Java VM"); //$NON-NLS-1$ vmItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Diagnostics.aboutJava(); } }); diagnosticMenu.add(vmItem); JMenuItem OSItem = new JMenuItem("OS"); //$NON-NLS-1$ OSItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Diagnostics.aboutOS(); } }); diagnosticMenu.add(OSItem); JMenuItem qtItem = new JMenuItem("QuickTime"); //$NON-NLS-1$ qtItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Diagnostics.aboutQTJava(); } }); diagnosticMenu.add(qtItem); JMenuItem j3dItem = new JMenuItem("Java 3D"); //$NON-NLS-1$ j3dItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Diagnostics.aboutJava3D(); } }); diagnosticMenu.add(j3dItem); JMenuItem joglItem = new JMenuItem("JOGL"); //$NON-NLS-1$ joglItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Diagnostics.aboutJOGL(); } }); diagnosticMenu.add(joglItem); helpMenu.addSeparator(); aboutItem = new JMenuItem(); aboutItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showAboutDialog(); } }); helpMenu.add(aboutItem); authorInfoItem = new JMenuItem(); authorInfoItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showAuthorInformation(); } }); helpMenu.add(authorInfoItem); if(OSPRuntime.applet==null) { // add window listener to exit frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { exit(); } // Added by W. Christian to disable the Language menu public void windowGainedFocus(WindowEvent e) { OSPRuntime.setAuthorMode(false); } public void windowActivated(WindowEvent e) { OSPRuntime.setAuthorMode(false); } }); // add exit menu item fileMenu.addSeparator(); exitItem = new JMenuItem(); exitItem.setAccelerator(KeyStroke.getKeyStroke('Q', mask)); exitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { exit(); } }); } // if launch jar not yet found, load class file as last attempt if(OSPRuntime.getLaunchJarPath()==null) { ResourceLoader.getResource("/org/opensourcephysics/tools/Launcher.class"); //$NON-NLS-1$ } // create and populate the internal xset menu if(OSPRuntime.getLaunchJarPath()!=null) { JarFile jar = OSPRuntime.getLaunchJar(); if(jar!=null) { Action action = new AbstractAction() { public void actionPerformed(ActionEvent e) { // get name of file to open String fileName = ((JMenuItem) e.getSource()).getText(); fileName = OSPRuntime.getLaunchJarName()+"!/"+fileName; //$NON-NLS-1$ String[] prevArgs = undoManager.getLauncherState(); if((prevArgs!=null)&&(open(fileName)!=null)) { String[] args = new String[] {fileName}; LauncherUndo.LoadEdit edit = undoManager.new LoadEdit(args, prevArgs); undoSupport.postEdit(edit); refreshGUI(); } else { open(fileName); refreshGUI(); } } }; // iterate thru JarFile entries and add menu items for(Enumeration<?> e = jar.entries(); e.hasMoreElements(); ) { JarEntry entry = (JarEntry) e.nextElement(); String name = entry.getName(); if(name.endsWith(".xset")&&!name.startsWith(resourcesPath.substring(1))) { //$NON-NLS-1$ if(name.startsWith(defaultFileName)) { continue; } if(openFromJarMenu==null) { openFromJarMenu = new JMenu(); } JMenuItem item = new JMenuItem(name); item.addActionListener(action); openFromJarMenu.add(item); } } } } baseMenuFontSize = fileMenu.getFont().getSize(); frame.setJMenuBar(menubar); frame.pack(); } // end createGUI /** * Sets the font level. * * @param level the level */ protected void setFontLevel(int level) { // set font levels of menubar and content pane FontSizer.setFonts(frame.getJMenuBar(), level); FontSizer.setFonts(frame.getContentPane(), level); // refresh string resources to adjust label sizes refreshStringResources(); // refresh the tree to adjust node sizes LaunchPanel tab = getSelectedTab(); if(tab!=null) { Enumeration<?> en = tab.getRootNode().breadthFirstEnumeration(); while(en.hasMoreElements()) { LaunchNode node = (LaunchNode) en.nextElement(); tab.treeModel.nodeChanged(node); } } // refresh GUI to update font size menu refreshGUI(); } /** * Gets the paths of currently open set and tabs. * * @return the open paths */ protected Set<String> getOpenPaths() { openPaths.clear(); openPaths.add(tabSetName); for(int i = 0; i<tabbedPane.getTabCount(); i++) { LaunchPanel panel = (LaunchPanel) tabbedPane.getComponentAt(i); LaunchNode[] nodes = panel.getRootNode().getAllOwnedNodes(); for(int j = 0; j<nodes.length; j++) { openPaths.add(nodes[j].getFileName()); } openPaths.add(panel.getRootNode().getFileName()); } return openPaths; } /** * Sets the look and feel. * * @param lf OSPRuntime name of look and feel * @param always true to set LnF even if already set * @return a new Launcher */ protected Launcher setLookAndFeel(final String lf, final boolean always) { if(lf==null) { return null; } lookAndFeel = lf; boolean newDecorations = true; boolean currentDecorations = JFrame.isDefaultLookAndFeelDecorated(); if(lf.equals(OSPRuntime.SYSTEM_LF)) { systemLFItem.setSelected(true); } else if(lf.equals(OSPRuntime.CROSS_PLATFORM_LF)) { javaLFItem.setSelected(true); } else if(lf.equals(OSPRuntime.DEFAULT_LF)) { newDecorations = OSPRuntime.DEFAULT_LOOK_AND_FEEL_DECORATIONS; defaultLFItem.setSelected(true); } else if(genericLFGroup.getSelection()!=null) { genericLFGroup.remove(systemLFItem); genericLFGroup.remove(javaLFItem); genericLFGroup.remove(defaultLFItem); systemLFItem.setSelected(false); javaLFItem.setSelected(false); defaultLFItem.setSelected(false); genericLFGroup.add(systemLFItem); genericLFGroup.add(javaLFItem); genericLFGroup.add(defaultLFItem); } Object lfType = OSPRuntime.LOOK_AND_FEEL_TYPES.get(lf); LookAndFeel currentLF = UIManager.getLookAndFeel(); if(!always&&(newDecorations==currentDecorations)&¤tLF.getClass().getName().equals(lfType)) { return null; } if(!isVisible()) { frame.addWindowListener(new WindowAdapter() { public void windowOpened(WindowEvent e) { setLookAndFeel(lf, always); frame.removeWindowListener(this); } }); return null; } OSPRuntime.setLookAndFeel(newDecorations, lf); if(spawner!=null) { spawner = spawner.setLookAndFeel(lf, true); } exitCurrentApps(); LauncherUndo undoManager = Launcher.this.undoManager; UndoableEditSupport undoSupport = Launcher.this.undoSupport; LaunchNode node = Launcher.this.getSelectedNode(); Point loc = Launcher.this.frame.getLocation(); Launcher.this.frame.dispose(); Launcher launcher; LaunchSet tabset = new LaunchSet(Launcher.this, tabSetName); XMLControlElement control = new XMLControlElement(tabset); // set null password so LaunchBuilder opens without verification control.setPassword(null); if(Launcher.this instanceof LaunchBuilder) { launcher = new LaunchBuilder(control.toXML()); } else { launcher = new Launcher(control.toXML()); } launcher.setSelectedNode(node.getPathString()); undoManager.setLauncher(launcher); launcher.undoManager = undoManager; launcher.undoSupport = undoSupport; launcher.tabSetName = tabSetName; launcher.password = password; launcher.spawner = spawner; launcher.jarBasePath = jarBasePath; launcher.frame.setDefaultCloseOperation(Launcher.this.frame.getDefaultCloseOperation()); launcher.refreshGUI(); launcher.frame.setLocation(loc); launcher.setVisible(true); launcher.frame.pack(); SwingUtilities.updateComponentTreeUI(OSPLog.getOSPLog()); return launcher; } /** * Shows the about dialog. */ protected void showAboutDialog() { String newline = XML.NEW_LINE; String vers = version; String date = OSPRuntime.getLaunchJarBuildDate(); if (date!=null) vers = vers+" "+date; //$NON-NLS-1$ String name = getClass().getSimpleName(); String aboutString = name+" "+vers+newline //$NON-NLS-1$ +"Copyright (c) 2010 Wolfgang Christian"+newline //$NON-NLS-1$ +"Open Source Physics Project"+newline //$NON-NLS-1$ +"www.opensourcephysics.org"+newline+newline //$NON-NLS-1$ +LaunchRes.getString("Label.CodeAuthor")+": Douglas Brown"; //$NON-NLS-1$ //$NON-NLS-2$ String translator = LaunchRes.getString("Launcher.About.Translator"); //$NON-NLS-1$ if (!translator.equals("")) { //$NON-NLS-1$ Locale loc = LaunchRes.resourceLocale; String language = OSPRuntime.getDisplayLanguage(loc); aboutString += newline + newline + LaunchRes.getString("Launcher.About.Language")+ ": " //$NON-NLS-1$ //$NON-NLS-2$ + language + newline; aboutString += LaunchRes.getString("Launcher.About.TranslationBy") //$NON-NLS-1$ +": "+ translator + newline; //$NON-NLS-1$ } JOptionPane.showMessageDialog(frame, aboutString, LaunchRes.getString("Help.About.Title")+" "+name, //$NON-NLS-1$//$NON-NLS-2$ JOptionPane.INFORMATION_MESSAGE); } /** * Shows the metadata dialog. */ protected void showAuthorInformation() { LaunchNode node = getSelectedNode(); if(node!=null) { String line = XML.NEW_LINE; String info = ""; //$NON-NLS-1$ if(!node.getAuthor().trim().equals("")) { //$NON-NLS-1$ info += LaunchRes.getString("Label.Author")+": "; //$NON-NLS-1$//$NON-NLS-2$ info += node.getAuthor()+line; } if(!node.getKeywords().trim().equals("")) { //$NON-NLS-1$ info += LaunchRes.getString("Label.Keywords")+": "; //$NON-NLS-1$//$NON-NLS-2$ info += node.getKeywords()+line; } if(!node.getCourseLevel().trim().equals("")) { //$NON-NLS-1$ info += LaunchRes.getString("Label.Level")+": "; //$NON-NLS-1$//$NON-NLS-2$ info += node.getCourseLevel()+line; } if(!node.getLanguages().trim().equals("")) { //$NON-NLS-1$ info += LaunchRes.getString("Label.Languages")+": "; //$NON-NLS-1$//$NON-NLS-2$ info += node.getLanguages()+line; } if(!node.getComment().trim().equals("")) { //$NON-NLS-1$ info += LaunchRes.getString("Label.Comments")+": "; //$NON-NLS-1$//$NON-NLS-2$ info += node.getComment()+line; } if(info.equals("")) { //$NON-NLS-1$ info = LaunchRes.getString("Dialog.AuthorInfo.NoInfo"); //$NON-NLS-1$ } JOptionPane.showMessageDialog(frame, info, LaunchRes.getString("Help.About.Title")+" \"" //$NON-NLS-1$//$NON-NLS-2$ +node.getName()+"\"", //$NON-NLS-1$ JOptionPane.INFORMATION_MESSAGE); } } /** * Determines whether the specified node is a link to another xset, tab or node. * To be a link, there must be no launch class but one or more arguments. * * @param node the launch node to verify * @return <code>true</code> if the node is a link */ protected boolean isLink(LaunchNode node) { if((node==null)||!node.isLeaf()||((node.launchClassName!=null)&&!node.launchClassName.equals(""))) { //$NON-NLS-1$ return false; } return(!node.args[0].equals("")|| //$NON-NLS-1$ ((node.args.length>1)&&!node.args[1].equals(""))|| //$NON-NLS-1$ ((node.args.length>2)&&!node.args[2].equals(""))); //$NON-NLS-1$ } /** * Determines whether the specified node has an associated EJS model (xml file). * * @param node the launch node to check * @return <code>true</code> if the node has an EJS model */ protected boolean hasEJSModel(LaunchNode node) { if((node==null)||!node.isLeaf()) { return false; } return EjsTool.hasEjsModel(node.getLaunchClass()); } /** * Determines whether the specified node is launchable. * * @param node the launch node to verify * @return <code>true</code> if the node is launchable */ protected boolean isLaunchable(LaunchNode node) { if((node==null)||!node.isLeaf()) { return false; } return isLaunchable(node.getLaunchClass()); } /** * Enables hyperlinks for the specified JEditorPane. * * @param textPane the editor pane * @param enabled true to enable hyperlinks */ protected void setLinksEnabled(JEditorPane textPane, boolean enabled) { if((linkListener==null)&&enabled) { linkListener = new HyperlinkListener() { HyperlinkEvent event; public void hyperlinkUpdate(HyperlinkEvent e) { if(e.getEventType()==HyperlinkEvent.EventType.ACTIVATED) { // workaround so each event is handled only once if(event==e) { return; } event = e; URL url = e.getURL(); String path = url.toString(); // browse web-hosted links and extracted files externally boolean extracted = false; for(String ext : extractExtensions) { extracted = extracted||path.endsWith(ext); } boolean browseExternally = !url.getHost().equals("")||extracted; //$NON-NLS-1$ if(browseExternally) { if(extracted&&(path.indexOf("jar!")>-1)) { //$NON-NLS-1$ // look to see if file already exists outside jar int j = path.indexOf("jar!/"); //$NON-NLS-1$ String fileName = path.substring(j+5); File target = new File(fileName); if(target.exists()) { path = target.toURI().toString(); } else { // get resource to extract file from jar Resource res = ResourceLoader.getResource(path); if(res!=null) { path = res.getURL().toString(); // add shutdown hook to dispose of extracted file final File tempFile = res.getFile(); Thread shutdownHook = new Thread() { public void run() { tempFile.deleteOnExit(); } }; Runtime.getRuntime().addShutdownHook(shutdownHook); } } } // open link in external browser if(!org.opensourcephysics.desktop.OSPDesktop.displayURL(path)) { OSPLog.warning("unable to open in browser: "+path); //$NON-NLS-1$ } } else { // browse internally and post undoable edit URL prev = selectedNode.htmlURL; if(prev!=url) { String undoPath = selectedNode.getPathString(); Integer undoPage = new Integer(selectedNode.tabNumber); Object[] undoData = new Object[] {null, undoPath, undoPage, prev}; Object[] redoData = null; // check to see if link is an anchor in same page if((prev!=null)&&prev.getPath().equals(url.getPath())) { redoData = new Object[] {null, undoPath, undoPage, url}; } else { Object[] nodeData = getNodeAndPage(url); if(nodeData!=null) { redoData = new Object[] {null, nodeData[0], nodeData[1], url}; } // no redo node found, so redo = undo node else { redoData = new Object[] {null, undoPath, undoPage, url}; } } // post undoable edit if(postEdits) { UndoableEdit edit = undoManager.new NavEdit(undoData, redoData); undoSupport.postEdit(edit); } // select new node // prevent duplicate NavEdit while selecting node postEdits = false; String nodePath = (String) redoData[1]; int page = ((Integer) redoData[2]).intValue(); setSelectedNode(nodePath, page, url); postEdits = true; } } } } }; } if(enabled) { textPane.addHyperlinkListener(linkListener); } else { textPane.removeHyperlinkListener(linkListener); } } /** * Gets the node path and page number associated with an html URL. * May return null. * * @param html the URL * @return [0] String node path, [1] Integer pageNumber */ protected Object[] getNodeAndPage(URL html) { String urlPath = html.getFile(); for(int i = 0; i<getTabCount(); i++) { LaunchPanel tab = getTab(i); Enumeration<?> e = tab.getRootNode().breadthFirstEnumeration(); while(e.hasMoreElements()) { LaunchNode node = (LaunchNode) e.nextElement(); for(int j = 0; j<node.getDisplayTabCount(); j++) { URL next = node.getDisplayTab(j).getURL(); if(next==null) { continue; } String nextPath = next.getFile(); if(nextPath.equals(urlPath)) { // found match String nodePath = node.getPathString(); return new Object[] {nodePath, new Integer(j)}; } } } } return null; } /** * Determines whether the specified class is launchable. * * @param type the launch class to verify * @return <code>true</code> if the class is launchable */ protected static boolean isLaunchable(Class<?> type) { if(type==null) { return false; } try { //throw exception if main method does not exist; return value not used type.getMethod("main", new Class[] {String[].class}); //$NON-NLS-1$ return true; } catch(NoSuchMethodException ex) { return false; } } /** * Determines whether the specified class is a model. * A model class must define a static getModelPane(String[], Frame) method. * * @param type the class to verify * @return <code>true</code> if the class is a model */ protected static boolean isModel(Class<?> type) { if(type==null) { return false; } try { type.getMethod("getModelPane", //$NON-NLS-1$ new Class[] {String[].class, JFrame.class}); return true; } catch(NoSuchMethodException ex) { return false; } } /** * Gets the modelPane for a specified class. May return null. * @param type * @param args * @return the model pane */ protected static JComponent getModelPane(Class<?> type, String[] args) { if(type==null) { return null; } // get launcher frame to pass to static class method JFrame frame = null; for(Frame next : Frame.getFrames()) { if(next instanceof Launcher.LauncherFrame) { frame = (Launcher.LauncherFrame) next; break; } } try { Method m = type.getMethod("getModelPane", //$NON-NLS-1$ new Class[] {String[].class, JFrame.class}); return(JComponent) m.invoke(type, new Object[] {args, frame}); } catch(Exception ex) {} return null; } /** * Handles a mouse pressed event. * * @param e the mouse event * @param tab the launch panel receiving the event */ protected void handleMousePressed(MouseEvent e, final LaunchPanel tab) { LaunchNode selectedNode = getSelectedNode(); if(OSPRuntime.isPopupTrigger(e)) { // make sure node is selected for right-clicks TreePath path = tab.tree.getPathForLocation(e.getX(), e.getY()); if(path==null) { return; } tab.tree.setSelectionPath(path); final LaunchNode node = getSelectedNode(); if(node==null) { return; } // add items to popup menu JMenuItem inspectItem = new JMenuItem(LaunchRes.getString("MenuItem.Inspect")); //$NON-NLS-1$ inspectItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { inspect(node); } }); inspectItem.setEnabled((password==null)||(this instanceof LaunchBuilder)); popup.add(inspectItem); if(node.getLaunchClass()!=null) { if(node.launchCount==0) { popup.addSeparator(); JMenuItem launchItem = new JMenuItem(LaunchRes.getString("MenuItem.Launch")); //$NON-NLS-1$ popup.add(launchItem); launchItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { node.launch(tab); } }); } else { popup.addSeparator(); JMenuItem terminateItem = new JMenuItem(LaunchRes.getString("MenuItem.Terminate")); //$NON-NLS-1$ popup.add(terminateItem); terminateItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { node.terminateAll(); } }); if(node.launchCount>1) { terminateItem.setText(LaunchRes.getString("MenuItem.TerminateAll")); //$NON-NLS-1$ } if(!node.isSingleton()) { JMenuItem launchItem = new JMenuItem(LaunchRes.getString("MenuItem.Relaunch")); //$NON-NLS-1$ popup.add(launchItem); launchItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { node.launch(tab); } }); } } if(hasEJSModel(node)) { popup.addSeparator(); JMenuItem ejsItem = new JMenuItem(LaunchRes.getString("Popup.MenuItem.EjsModel")); //$NON-NLS-1$ popup.add(ejsItem); ejsItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { boolean quit = EjsTool.runEjs(node.getLaunchClass(), new Cryptic(password).getCryptic()); if(quit) { node.terminateAll(); } } }); } } // show popup when running Launcher (LaunchBuilder) if(getClass().equals(Launcher.class)) { popup.show(tab, e.getX()+4, e.getY()+12); } } else if(e.getClickCount()==2) { if(isLaunchable(selectedNode)) { if(selectedNode.launchCount==0) { selectedNode.launch(tab); } else if(selectedNode.isSingleton()||(selectedNode.isSingleVM()&&selectedNode.isSingleApp())) { JOptionPane.showMessageDialog(frame, LaunchRes.getString("Dialog.Singleton.Message")+ //$NON-NLS-1$ " \""+selectedNode.toString()+"\"", //$NON-NLS-1$//$NON-NLS-2$ LaunchRes.getString("Dialog.Singleton.Title"), //$NON-NLS-1$ JOptionPane.INFORMATION_MESSAGE); } else { int selected = JOptionPane.showConfirmDialog(frame, LaunchRes.getString("Dialog.Relaunch.Message"), //$NON-NLS-1$ LaunchRes.getString("Dialog.Relaunch.Title"), //$NON-NLS-1$ JOptionPane.YES_NO_OPTION); if(selected==JOptionPane.YES_OPTION) { selectedNode.launch(tab); } } if(selectedNode.launchPanel!=null) { selectedNode.launchPanel.repaint(); } } else if(isLink(selectedNode)) { String[] prevArgs = undoManager.getLauncherState(); if((open(selectedNode.args)!=null)&&(prevArgs!=null)) { UndoableEdit edit = undoManager.new LoadEdit(selectedNode.args, prevArgs); undoSupport.postEdit(edit); } refreshGUI(); } else if (!selectedNode.getPDFPaths().isEmpty()) { for (String path: selectedNode.getPDFPaths()) { String base = tabSetBasePath.equals("")? jarBasePath: tabSetBasePath; //$NON-NLS-1$ String target = XML.getResolvedPath(path, base); target = ResourceLoader.getURIPath(target); OSPLog.finer("opening PDF target: "+target); //$NON-NLS-1$ OSPDesktop.displayURL(target); } } else if(selectedNode.getLaunchClass()==null && selectedNode.launchClassName!=null && !selectedNode.launchClassName.equals("")) { //$NON-NLS-1$ // create jar list String[] jars = LaunchClassChooser.parsePath(selectedNode.getClassPath()); String jarList = ""; //$NON-NLS-1$ for(int i = 0; i<jars.length; i++) { if(!jarList.equals("")) { //$NON-NLS-1$ if(jars[i].equals(OSPRuntime.getLaunchJarName())) { continue; } // separate jars with commas jarList += ", "; //$NON-NLS-1$ } jarList += jars[i]; } JOptionPane.showMessageDialog(frame, LaunchRes.getString("Dialog.ClassNotFound.Message1")+ //$NON-NLS-1$ selectedNode.launchClassName+XML.NEW_LINE+LaunchRes.getString("Dialog.ClassNotFound.Message2")+ //$NON-NLS-1$ XML.NEW_LINE+jarList, LaunchRes.getString("Dialog.ClassNotFound.Title"), //$NON-NLS-1$ JOptionPane.WARNING_MESSAGE); } } } private void exitCurrentApps() { final Frame[] prevFrames = Frame.getFrames(); for(int i = 0, n = prevFrames.length; i<n; i++) { if(existingFrames.contains(prevFrames[i])) { continue; // don't mess with pre-existing frames such as the applet plugin } if(!(prevFrames[i] instanceof LauncherFrame)) { WindowListener[] listeners = prevFrames[i].getWindowListeners(); for(int j = 0; j<listeners.length; j++) { listeners[j].windowClosing(null); } if(prevFrames[i].isVisible()) { prevFrames[i].dispose(); } } } } /** * Displays a splash screen while loading. */ private void splash() { int w = 360; int h = 120; if(splashDialog==null) { splashDialog = new JDialog(frame, false); splashDialog.setUndecorated(true); Color darkred = new Color(128, 0, 0); JPanel splash = new JPanel(new BorderLayout()); // white background to start splash.setBackground(Color.white); splash.setPreferredSize(new Dimension(w, h)); splash.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { splashDialog.dispose(); } }); frame.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { splashDialog.dispose(); frame.removeMouseListener(this); } }); Border etch = BorderFactory.createEtchedBorder(); splash.setBorder(etch); splashDialog.setContentPane(splash); Box labels = Box.createVerticalBox(); JLabel titleLabel = new JLabel("OSP Launcher"); //$NON-NLS-1$ if(this instanceof LaunchBuilder) { titleLabel.setText("OSP Launch Builder"); //$NON-NLS-1$ } Font font = titleLabel.getFont().deriveFont(Font.BOLD); titleLabel.setFont(font.deriveFont(24f)); titleLabel.setForeground(darkred); titleLabel.setAlignmentX(0.5f); titleLabel.setHorizontalAlignment(SwingConstants.CENTER); creditsLabel = new JLabel(" "); //$NON-NLS-1$ font = font.deriveFont(Font.PLAIN); font = font.deriveFont(12f); creditsLabel.setFont(font); creditsLabel.setBorder(BorderFactory.createEmptyBorder(2, 2, 10, 2)); creditsLabel.setHorizontalAlignment(SwingConstants.CENTER); creditsLabel.setAlignmentX(0.5f); splashPathLabel = new JLabel(" ") { //$NON-NLS-1$ public void setText(String s) { int max = 80; if((s!=null)&&(s.length()>max)) { s = s.substring(0, max-4)+"..."; //$NON-NLS-1$ } super.setText(s); } }; splashPathLabel.setFont(font); splashPathLabel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); splashPathLabel.setHorizontalAlignment(SwingConstants.CENTER); splashPathLabel.setAlignmentX(0.5f); labels.add(Box.createGlue()); labels.add(titleLabel); labels.add(Box.createGlue()); labels.add(splashPathLabel); labels.add(creditsLabel); splash.add(labels, BorderLayout.CENTER); splashDialog.pack(); splashTimer = new javax.swing.Timer(4000, new ActionListener() { public void actionPerformed(ActionEvent e) { if(frame.isShowing()) { splashDialog.dispose(); splashTimer.stop(); } } }); } Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); int x = dim.width/2; int y = dim.height/2; splashDialog.setLocation(x-w/2, y-h/2); splashDialog.setVisible(true); splashTimer.start(); } /** * Exits this application. */ protected void exit() { if(!terminateApps()) { // change default close operation to prevent window closing final int op = frame.getDefaultCloseOperation(); frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); // restore default close operation later Runnable runner = new Runnable() { public void run() { frame.setDefaultCloseOperation(op); } }; SwingUtilities.invokeLater(runner); return; } // HIDE_ON_CLOSE apps should exit if((OSPRuntime.applet==null)&&(frame.getDefaultCloseOperation()==WindowConstants.HIDE_ON_CLOSE)) { if (canExit) System.exit(0); else { exitCurrentApps(); // close existing programs frame.setVisible(false); } } // all applets and non-HIDE_ON_CLOSE apps should just hide themselves else { exitCurrentApps(); // close existing programs frame.setVisible(false); } } /** * Terminates running apps. * * @return false if process is cancelled by the user */ protected boolean terminateApps() { if(frame.getDefaultCloseOperation()==WindowConstants.HIDE_ON_CLOSE) { // ask to terminate apps running in this process boolean approved = false; Frame[] frames = Frame.getFrames(); for(int i = 0, n = frames.length; i<n; i++) { if(!approved&&frames[i].isVisible()&&!(frames[i] instanceof LauncherFrame)&&!(frames[i] instanceof OSPLog)&&!(frames[i] instanceof EncryptionTool)) { if(existingFrames.contains(frames[i])) { continue; // don't mess with pre-existing frames such as the applet plugin } int selected = JOptionPane.showConfirmDialog(frame, LaunchRes.getString("Dialog.Terminate.Message")+XML.NEW_LINE //$NON-NLS-1$ +LaunchRes.getString("Dialog.Terminate.Question"), LaunchRes.getString("Dialog.Terminate.Title"), //$NON-NLS-1$//$NON-NLS-2$ JOptionPane.YES_NO_OPTION); if(selected==JOptionPane.YES_OPTION) { approved = true; } else { return false; } } } // ask to terminate apps running in separate processes approved = false; boolean declined = false; // look for nodes with running processes Component[] comps = tabbedPane.getComponents(); for(int i = 0; i<comps.length; i++) { LaunchPanel tab = (LaunchPanel) comps[i]; Enumeration<?> e = tab.getRootNode().breadthFirstEnumeration(); while(e.hasMoreElements()) { LaunchNode node = (LaunchNode) e.nextElement(); if(!node.processes.isEmpty()) { if(!approved&&!declined) { // ask for approval int selected = JOptionPane.showConfirmDialog(frame, LaunchRes.getString("Dialog.TerminateSeparateVM.Message") //$NON-NLS-1$ +XML.NEW_LINE+LaunchRes.getString("Dialog.TerminateSeparateVM.Question"), LaunchRes.getString( //$NON-NLS-1$ "Dialog.TerminateSeparateVM.Title"), //$NON-NLS-1$ JOptionPane.YES_NO_OPTION); approved = (selected==JOptionPane.YES_OPTION); declined = !approved; } if(approved) { // terminate processes for(Iterator<Process> it = node.processes.iterator(); it.hasNext(); ) { Process proc = it.next(); it.remove(); proc.destroy(); } } else { return false; } } } } } return true; } /** * A unique frame class for Launcher. This is used to differentiate Launcher * from other apps when disposing of frames in singleApp mode. */ public class LauncherFrame extends JFrame { /** * Constructor LauncherFrame */ private LauncherFrame() { setName("LauncherTool"); //$NON-NLS-1$ try { // gets the image directly without using the resource loader java.net.URL url = Launcher.class.getResource(OSPRuntime.OSP_ICON_FILE); ImageIcon icon = new ImageIcon(url); setIconImage(icon.getImage()); } catch(Exception ex) { // image not found } } } /** * A cell renderer class to show launchable nodes. */ private class LaunchRenderer extends DefaultTreeCellRenderer { public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); LaunchNode node = (LaunchNode) value; setToolTipText(node.tooltip.equals("") //$NON-NLS-1$ ? null : node.tooltip); if((node.getFileName()!=null)&&(Launcher.this instanceof LaunchBuilder)) { setToolTipText(LaunchRes.getString("ToolTip.FileName")+" \""+node.getFileName()+"\""); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ setIcon(getFileIcon(node)); } else if(node.launchCount>0) { if(node.isSingleton()) { setIcon(singletonIcon); } else if(node.isSingleVM()&&node.isSingleApp()) { setIcon(singletonIcon); } else { setIcon(launchedIcon); } } else if(hasEJSModel(node)) { setIcon(ejsIcon); } else if(isLaunchable(node)) { setIcon(launchIcon); } else if(isLink(node)) { setIcon(linkIcon); } else if(node.isLeaf()) { if((node.getLaunchClass()==null)&&(node.launchClassName!=null)&&!node.launchClassName.equals("")) { //$NON-NLS-1$ setIcon(launchEmptyIcon); } else if(node.getDisplayTabCount()>0) { int count = 0; for(Iterator<?> it = node.tabData.iterator(); it.hasNext(); ) { DisplayTab html = (DisplayTab) it.next(); if((html.url!=null)||(html.getModelClass()!=null)) { count++; } } if(count>0) { setIcon(htmlIcon); } else { setIcon(noFileIcon); } } else { setIcon(noFileIcon); } } return this; } } /** * Gets an appropriate File icon for a node (must have non-null filename) * * @param node the launch node * @return the icon */ protected Icon getFileIcon(LaunchNode node) { if(node.getFileName().length()==0) { return null; } File file = node.getFile(); Resource res = node.getResource(); boolean changed = changedFiles.contains(node.getFileName()); // yellow file icon if changed if(changed) { return yellowFileIcon; // ghost file icon if resource is null or parent self contained } else if((res==null)||node.isParentSelfContained()) { return ghostFileIcon; // white file icon if has writable file } else if((file!=null)&&file.canWrite()) { return whiteFileIcon; // magenta icon if opened from jar or zip file } else if(file==null) { return magentaFileIcon; } // red icon if opened from read-only file return redFileIcon; } /** * A class to save and load a set of launch tabs and Launcher static fields. */ public class LaunchSet implements XML.ObjectLoader { private Launcher launcher; private String name; boolean failedToLoad = false; @SuppressWarnings("javadoc") public boolean showHiddenNodes = true; /** * Constructor LaunchSet */ public LaunchSet() { launcher = Launcher.this; } protected LaunchSet(Launcher launcher, String path) { this.launcher = launcher; name = XML.getName(XML.forwardSlash(path)); } public void saveObject(XMLControl control, Object obj) { LaunchSet tabset = (LaunchSet) obj; Launcher launcher = tabset.launcher; control.setValue("classpath", classPath); //$NON-NLS-1$ control.setValue("title", launcher.title); //$NON-NLS-1$ // save dimensions and split pane divider location Dimension dim = launcher.getSize(); control.setValue("width", dim.width); //$NON-NLS-1$ control.setValue("height", dim.height); //$NON-NLS-1$ control.setValue("divider", launcher.divider); //$NON-NLS-1$ control.setValue("look_and_feel", launcher.lookAndFeel); //$NON-NLS-1$ control.setValue("editor_enabled", launcher.editorEnabled); //$NON-NLS-1$ if(saveState) { // save currently selected node LaunchNode node = launcher.getSelectedNode(); control.setValue("selected_node", node.getPathString()); //$NON-NLS-1$ // save array of expanded node collections (one for each tab) int n = launcher.tabbedPane.getTabCount(); Collection<?>[] expansions = new Collection[n]; for(int i = 0; i<n; i++) { LaunchPanel tab = launcher.getTab(i); expansions[i] = tab.getExpandedNodes(); } control.setValue("expanded", expansions); //$NON-NLS-1$ } // save collection of tab root nodes Collection<Object> nodes = new ArrayList<Object>(); for(int i = 0; i<launcher.tabs.size(); i++) { LaunchNode root = ((LaunchPanel) launcher.tabs.get(i)).getRootNode(); if(root.isHiddenInLauncher()&&!tabset.showHiddenNodes) { continue; } // initialize selfContained, previewing and saveHiddenNodes properties root.parentSelfContained = false; root.previewing = false; root.saveHiddenNodes = tabset.showHiddenNodes; if(launcher.selfContained) { // save tab root root.setSelfContained(false); root.parentSelfContained = true; nodes.add(root); } else if(launcher.previewing) { // preview tab root root.previewing = true; nodes.add(root); } else if((root.getFileName()==null)||root.getFileName().equals("")) { //$NON-NLS-1$ // save tab root nodes.add(root); } else { // save tab root filename nodes.add(root.getFileName()); } } control.setValue("launch_nodes", nodes); //$NON-NLS-1$ boolean hasPW = (launcher.password!=null)&&!launcher.password.equals(""); //$NON-NLS-1$ if(hasPW&&pwRequiredToLoad) { control.setValue("pw_required_by_launcher", true); //$NON-NLS-1$ } control.setValue("xml_password", launcher.password); //$NON-NLS-1$ } public Object createObject(XMLControl control) { return null; } public Object loadObject(XMLControl control, Object obj) { LaunchSet tabset = (LaunchSet) obj; final Launcher launcher = tabset.launcher; // load a different launch set if(control.getPropertyNames().contains("launchset")) { //$NON-NLS-1$ String path = launcher.open(control.getString("launchset")); //$NON-NLS-1$ tabset.failedToLoad = path==null; return obj; } // load static properties if(control.getPropertyNames().contains("classpath")) { //$NON-NLS-1$ classPath = control.getString("classpath"); //$NON-NLS-1$ } final String lookAndFeel = control.getString("look_and_feel"); //$NON-NLS-1$ if(lookAndFeel!=null) { Runnable runner = new Runnable() { public void run() { launcher.setLookAndFeel(lookAndFeel, false); } }; SwingUtilities.invokeLater(runner); } // read memory size if (control.getPropertyNames().contains("memory_size")) //$NON-NLS-1$ launcher.xsetMemorySize = control.getInt("memory_size"); //$NON-NLS-1$ else launcher.xsetMemorySize = 0; // read selected node and set saveState flag String selectedPath = control.getString("selected_node"); //$NON-NLS-1$ launcher.saveState = selectedPath!=null; boolean rootEnabled = true; // load launch nodes Collection<?> nodes = Collection.class.cast(control.getObject("launch_nodes")); //$NON-NLS-1$ if((nodes!=null)&&!nodes.isEmpty()) { int i = launcher.tabbedPane.getSelectedIndex(); Iterator<?> it = nodes.iterator(); boolean tabAdded = false; LaunchNode buttonNode = null; while(it.hasNext()) { Object next = it.next(); // prevent circular references if((tabset.name!=null)&&tabset.name.equals(next)) { continue; } if(next instanceof String) { String path = XML.getResolvedPath((String) next, tabSetBasePath); if(launcher.open(path)!=null) { tabAdded = true; } } else if(next instanceof LaunchNode) { LaunchNode node = (LaunchNode) next; if (!tabAdded) { if(splashDialog!=null && splashDialog.isVisible()) { if(!node.getAuthor().trim().equals("")) { //$NON-NLS-1$ String by = LaunchRes.getString("Label.Author")+": "; //$NON-NLS-1$//$NON-NLS-2$ creditsLabel.setText(by+node.getAuthor()); } } if (control instanceof XMLControlElement) { XMLControlElement element = (XMLControlElement)control; String pw = element.getPassword(); if(pw!=null && !passwords.contains(pw)) { boolean pwRequired = control.getBoolean("pw_required_by_launcher"); //$NON-NLS-1$ if((Launcher.this instanceof LaunchBuilder)||pwRequired) { node.enabled = false; rootEnabled = false; } } } } if((launcher.getClass()==Launcher.class)&&node.isButtonView()&&(buttonNode==null)) { buttonNode = node; } tabAdded = launcher.addTab(node)||tabAdded; } } if(!launcher.saveState || !rootEnabled) { // select the first button node or first added tab if(buttonNode!=null) { for(int j = 0; j<launcher.tabbedPane.getTabCount(); j++) { if(launcher.getTab(j).getRootNode()==buttonNode) { launcher.tabbedPane.setSelectedIndex(j); } } } else if(tabAdded) { launcher.tabbedPane.setSelectedIndex(i+1); } } } if(launcher.saveState) { Collection<?>[] expansions = Collection[].class.cast(control.getObject("expanded")); //$NON-NLS-1$ if(expansions!=null) { if (rootEnabled) { for(int i = 0; i<expansions.length; i++) { LaunchPanel tab = launcher.getTab(i); tab.setExpandedNodes(expansions[i]); } } else launcher.expansions = expansions; } if (rootEnabled) launcher.setSelectedNode(selectedPath); else launcher.selectedPath = selectedPath; } launcher.title = control.getString("title"); //$NON-NLS-1$ // load security items if(control.getPropertyNames().contains("editor_enabled")) { //$NON-NLS-1$ launcher.editorEnabled = control.getBoolean("editor_enabled"); //$NON-NLS-1$ } launcher.password = control.getString("xml_password"); //$NON-NLS-1$ launcher.pwRequiredToLoad = control.getBoolean("pw_required_by_launcher"); //$NON-NLS-1$ // load dimensions if(control.getPropertyNames().contains("width")&&control.getPropertyNames().contains("height")) { //$NON-NLS-1$//$NON-NLS-2$ Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); dim.width = Math.min((8*dim.width)/10, control.getInt("width")); //$NON-NLS-1$ dim.height = Math.min((8*dim.height)/10, control.getInt("height")); //$NON-NLS-1$ launcher.setSize(dim); } // load divider position if(control.getPropertyNames().contains("divider")) { //$NON-NLS-1$ launcher.divider = control.getInt("divider"); //$NON-NLS-1$ launcher.refreshGUI(); } if (launcher.getRootNode()!=null && !launcher.getRootNode().enabled) { final Launcher launchr = launcher; Runnable runner = new Runnable() { public void run() { launchr.passwordItem.doClick(0); } }; SwingUtilities.invokeLater(runner); } return obj; } } // ________________________________ static methods _____________________________ /** * Launches an application with no arguments. * * @param type the class to be launched */ public static void launch(Class<?> type) { launch(type, null, null); } /** * Launches an application with an array of string arguments. * * @param type the class to be launched * @param args the String array of arguments */ public static void launch(Class<?> type, String[] args) { launch(type, args, null); } /** * Launches an application associated with a launch node. * * @param type the class to be launched * @param args the argument array (may be null) * @param node the launch node (may be null) */ public static void launch(final Class<?> type, String[] args, final LaunchNode node) { if(type==null) { OSPLog.info(LaunchRes.getString("Log.Message.NoClass")); //$NON-NLS-1$ JOptionPane.showMessageDialog(null, LaunchRes.getString("Dialog.NoLaunchClass.Message"), //$NON-NLS-1$ LaunchRes.getString("Dialog.NoLaunchClass.Title"), JOptionPane.WARNING_MESSAGE); //$NON-NLS-1$ return; } String desc = LaunchRes.getString("Log.Message.Launching")+" "+type+", args "; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ if(args==null) { desc += args; } else { desc += "{"; //$NON-NLS-1$ for(int i = 0; i<args.length; i++) { desc += args[i]; if(i<args.length-1) { desc += ", "; //$NON-NLS-1$ } } desc += "}"; //$NON-NLS-1$ } OSPLog.fine(desc); // launch the app in single vm mode if(OSPRuntime.launchingInSingleVM||!newVMAllowed) { OSPRuntime.launchingInSingleVM = true; OSPLog.finer(LaunchRes.getString("Log.Message.LaunchCurrentVM")); //$NON-NLS-1$ // get array of frames before launching final Frame[] prevFrames = Frame.getFrames(); // dispose of previous frames if single app mode if(singleAppMode) { OSPLog.finer(LaunchRes.getString("Log.Message.LaunchSingleApp")); //$NON-NLS-1$ boolean vis = OSPLog.isLogVisible(); for(int i = 0, n = prevFrames.length; i<n; i++) { if(existingFrames.contains(prevFrames[i])) { continue; // don't mess with pre-exisitng frames such as the applet plugin } if(!(prevFrames[i] instanceof LauncherFrame)) { // inform window listeners so they can close running apps WindowListener[] listeners = prevFrames[i].getWindowListeners(); for(int j = 0; j<listeners.length; j++) { listeners[j].windowClosing(null); } prevFrames[i].dispose(); } } if(vis) { OSPLog.showLog(); } // terminate processes running in other VMs using node.terminateAll()? } // set xml class loader if(node!=null) { String classPath = node.getClassPath(); XML.setClassLoader(LaunchClassChooser.getClassLoader(classPath)); } if (node!=null && node.launchPanel!=null && node.launchPanel.launcher!=null) { String pw = node.launchPanel.launcher.password; String encrypted = pw==null? null: new Cryptic(pw).getCryptic(); try { // try block in case property setting not allowed System.setProperty("launcher.password", encrypted); //$NON-NLS-1$ } catch (Exception ex) {} } // launch in singleVM by invoking main method from separate daemon thread final String[] launchArgs = args; // if (args.length>=1 && "".equals(args[0])) //$NON-NLS-1$ // launchArgs = null; // else launchArgs = args; final Runnable launchRunner = new Runnable() { public void run() { activeNode = node; try { Method m = type.getMethod("main", new Class[] {String[].class}); //$NON-NLS-1$ m.invoke(type, new Object[] {launchArgs}); // may not return! } catch(Exception ex) { ex.printStackTrace(); } // main method returned, so remove launchThread from node map if (node!=null) node.threads.remove(this); activeNode = null; // find frames associated with launched app and store in node if(frameFinder!=null) { findFramesFor(node, prevFrames, this); if(frameFinder!=null) { frameFinder.stop(); frameFinder = null; } } } }; // create timer to look for new frames in case main method doesn't return if(frameFinder!=null) { frameFinder.stop(); } frameFinder = new javax.swing.Timer(1000, new ActionListener() { public void actionPerformed(ActionEvent e) { findFramesFor(node, prevFrames, launchRunner); // stops timer if finds frames } }); Thread launchThread = new Thread(launchRunner); launchThread.setDaemon(true); if (node!=null) node.threads.put(launchRunner, launchThread); launchThread.start(); frameFinder.start(); return; } // launch the app in a separate java process OSPLog.finer(LaunchRes.getString("Log.Message.LaunchSeparateVM")); //$NON-NLS-1$ // construct the command to execute final Vector<String> cmd = new Vector<String>(); cmd.add("java"); //$NON-NLS-1$ cmd.add("-Dorg.osp.launcher=true"); //$NON-NLS-1$ if (node!=null && node.launchPanel!=null && node.launchPanel.launcher!=null && node.launchPanel.launcher.password!=null) { String encrypted = new Cryptic(node.launchPanel.launcher.password).getCryptic(); cmd.add("-Dlauncher.password="+encrypted); //$NON-NLS-1$ } if((classPath!=null)&&!classPath.equals("")) { //$NON-NLS-1$ String jar = getDefaultJar(); if((jar!=null)&&(classPath.indexOf(jar)==-1)) { classPath += ";"+jar; //$NON-NLS-1$ } // convert any colons to semicolons int i = classPath.indexOf(":"); //$NON-NLS-1$ while(i!=-1) { classPath = classPath.substring(0, i)+";"+classPath.substring(i+1); //$NON-NLS-1$ i = classPath.indexOf(":"); //$NON-NLS-1$ } // replace semicolons with platform-dependent path separator char pathSeparator = System.getProperty("path.separator").charAt(0); //$NON-NLS-1$ classPath = classPath.replace(';', pathSeparator); cmd.add("-classpath"); //$NON-NLS-1$ cmd.add(classPath); } cmd.add(type.getName()); if(args!=null) { for(int i = 0; i<args.length; i++) { if(args[i]!=null) { cmd.add(args[i]); } } } // if(args!=null && !(args.length==1 && "".equals(args[0]))) { //$NON-NLS-1$ // for(int i = 0; i<args.length && args[i]!=null && !"".equals(args[i]); i++) { //$NON-NLS-1$ // cmd.add(args[i]); // } // } // create a launch thread for separate VM Runnable launchRunner = new Runnable() { public void run() { OSPLog.finer(LaunchRes.getString("Log.Message.Command")+" "+cmd.toString()); //$NON-NLS-1$//$NON-NLS-2$ String[] cmdarray = cmd.toArray(new String[0]); String[] envVars = new String[] {"osp_launcher=true"}; //$NON-NLS-1$ try { Process proc = Runtime.getRuntime().exec(cmdarray, envVars); if(node!=null) { node.processes.add(proc); } BufferedInputStream errStream = new BufferedInputStream(proc.getErrorStream()); StringBuffer buff = new StringBuffer(); while(true) { int datum = errStream.read(); if(datum==-1) { break; } buff.append((char) datum); } String msg = buff.toString().trim(); if(msg.length()>0) { OSPLog.warning("error when launching node "+node+": "+buff.toString()); //$NON-NLS-1$ //$NON-NLS-2$ } errStream.close(); proc.waitFor(); if(node!=null) { node.threadRunning(false); node.processes.remove(proc); } } catch(Exception ex) { OSPLog.info(ex.toString()); if(node!=null) { node.threadRunning(false); } } } }; if(node!=null) { node.threadRunning(true); } new Thread(launchRunner).start(); } /** * Sets the static jarsOnly property. * * @param onlyJars true to restrict class paths to jar files */ public static void setJarsOnly(boolean onlyJars) { LaunchClassChooser.jarsOnly = onlyJars; } /** * Starts the main launcher with specified arguments and memory size. * * @param args the main method arguments * @param desiredMemorySize the desired memory size in MB */ private static void start(String[] args, long desiredMemorySize) { // get current memory size java.lang.management.MemoryMXBean memory = java.lang.management.ManagementFactory.getMemoryMXBean(); long memorySize = memory.getHeapMemoryUsage().getMax()/(1024*1024); // if memory size is not at least 90% of desired, then relaunch if (memorySize<9*desiredMemorySize/10) { relaunch(args, desiredMemorySize, null); return; } // else create the main Launcher mainLauncher = new Launcher(); if((args!=null)&&(args.length>0)) { mainLauncher.open(args); } else { String path = null; // look for default file with launchJarName or defaultFileName if(OSPRuntime.getLaunchJarName()!=null) { path = mainLauncher.open(XML.stripExtension(OSPRuntime.getLaunchJarName())+".xset"); //$NON-NLS-1$ } if(path==null) { path = mainLauncher.open(defaultFileName+".xset"); //$NON-NLS-1$ } if(path==null) { path = mainLauncher.open(defaultFileName+".xml"); //$NON-NLS-1$ } } mainLauncher.refreshGUI(); // center frame on the screen Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); int x = (dim.width-mainLauncher.frame.getBounds().width)/2; int y = (dim.height-mainLauncher.frame.getBounds().height)/2; mainLauncher.frame.setLocation(x, y); mainLauncher.frame.setVisible(true); } /** * Attempts to relaunch the current jar in a separate VM with specified arguments * and memory size. If successful, the current VM exits. * * @param args the main method arguments * @param memorySize the desired memory size in MB * @param comp a component used by JOptionPane (may be null) */ protected static void relaunch(final String[] args, final long memorySize, final Component comp) { String jarPath = OSPRuntime.getLaunchJarPath(); final Vector<String> cmd = new Vector<String>(); cmd.add("java"); //$NON-NLS-1$ cmd.add("-Xms32m"); //$NON-NLS-1$ cmd.add("-Xmx"+memorySize+"m"); //$NON-NLS-1$ //$NON-NLS-2$ cmd.add("-jar"); //$NON-NLS-1$ cmd.add(jarPath); if (args!=null) { for (String next: args) { cmd.add(next); } } // create a timer to exit the system after 500 ms final Timer timer = new Timer(500, new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); timer.setRepeats(false); timer.start(); // create a thread to launch in separate VM Runnable launchRunner = new Runnable() { public void run() { String[] cmdarray = cmd.toArray(new String[0]); try { Process proc = Runtime.getRuntime().exec(cmdarray); BufferedInputStream errStream=new BufferedInputStream(proc.getErrorStream()); // next line will block if no errors (ie if relaunch is successful) // this results in the timer exiting the current VM errStream.read(); // if relaunch fails, then stop the timer, show a warning dialog, // and, if needed, start with default memory size timer.stop(); errStream.close(); JOptionPane.showMessageDialog(comp, LaunchRes.getString("Launcher.Dialog.InsufficientMemory.Message") //$NON-NLS-1$ +" "+memorySize+"MB.", //$NON-NLS-1$ //$NON-NLS-2$ LaunchRes.getString("Launcher.Dialog.InsufficientMemory.Title"), //$NON-NLS-1$ JOptionPane.WARNING_MESSAGE); if (mainLauncher==null) { // if not yet started, start with current memory size java.lang.management.MemoryMXBean memory = java.lang.management.ManagementFactory.getMemoryMXBean(); long memorySize = memory.getHeapMemoryUsage().getMax()/(1024*1024); start(args, memorySize); } } catch(Exception ex) {} } }; new Thread(launchRunner).start(); } /** * Main entry point when used as application. * * @param args args[0] may be an xml file name */ public static void main(String[] args) { // java.util.Locale.setDefault(new java.util.Locale("es")); // OSPLog.setLevel(ConsoleLevel.ALL); // get current memory size java.lang.management.MemoryMXBean memory = java.lang.management.ManagementFactory.getMemoryMXBean(); long memorySize = memory.getHeapMemoryUsage().getMax()/(1024*1024); // open default xset, if any, and look for desired memory size if (OSPRuntime.getLaunchJarName()!=null) { String xset = XML.stripExtension(OSPRuntime.getLaunchJarName())+".xset"; //$NON-NLS-1$ String jarBase = OSPRuntime.getLaunchJarDirectory(); String path = XML.getResolvedPath(xset, jarBase); XMLControl control = new XMLControlElement(path); if (!control.failedToRead() && control.getPropertyNames().contains("memory_size")) { //$NON-NLS-1$ memorySize = control.getInt("memory_size"); //$NON-NLS-1$ } } start(args, memorySize); } /** * Gets a class chooser for selecting launchable classes from jar files. * * @return the jar class chooser */ protected LaunchClassChooser getClassChooser() { if(classChooser==null) { classChooser = new LaunchClassChooser(contentPane); } return classChooser; } /** * Gets a file filter that acepts xml files. * * @return the xml file filter */ protected static FileFilter getXMLFilter() { if (xmlFileFilter==null) { xmlFileFilter = new FileFilter() { // accept all directories and *.xml files. public boolean accept(File f) { if(f==null) { return false; } if(f.isDirectory()) { return true; } String extension = null; String name = f.getName(); int i = name.lastIndexOf('.'); if((i>0)&&(i<name.length()-1)) { extension = name.substring(i+1).toLowerCase(); } if((extension!=null)&&extension.equals("xml")) { //$NON-NLS-1$ return true; } return false; } // the description of this filter public String getDescription() { return LaunchRes.getString("FileChooser.XMLFilter.Description"); //$NON-NLS-1$ } }; } return xmlFileFilter; } /** * Gets a file chooser for selecting xml files. * * @return the xml chooser */ protected static JFileChooser getXMLChooser() { if(chooser!=null) { FontSizer.setFonts(chooser, FontSizer.getLevel()); return chooser; } chooser = new JFileChooser(new File(OSPRuntime.chooserDir)); // add xml file filters launcherFileFilter = new FileFilter() { // accept all directories, *.xml, *.xset and zip files. public boolean accept(File f) { if(f==null) { return false; } if(f.isDirectory()) { return true; } String extension = null; String name = f.getName(); int i = name.lastIndexOf('.'); if((i>0)&&(i<name.length()-1)) { extension = name.substring(i+1).toLowerCase(); } if((extension!=null)&&(extension.equals("xset")||extension.equals("xml") //$NON-NLS-1$ //$NON-NLS-2$ ||extension.equals("zip")||extension.equals("trz"))) { //$NON-NLS-1$//$NON-NLS-2$ return true; } return false; } // the description of this filter public String getDescription() { return LaunchRes.getString("FileChooser.LauncherFilter.Description"); //$NON-NLS-1$ } }; xsetFileFilter = new FileFilter() { // accept all directories and *.xset files. public boolean accept(File f) { if(f==null) { return false; } if(f.isDirectory()) { return true; } String extension = null; String name = f.getName(); int i = name.lastIndexOf('.'); if((i>0)&&(i<name.length()-1)) { extension = name.substring(i+1).toLowerCase(); } if((extension!=null)&&extension.equals("xset")) { //$NON-NLS-1$ return true; } return false; } // the description of this filter public String getDescription() { return LaunchRes.getString("FileChooser.XSETFilter.Description"); //$NON-NLS-1$ } }; chooser.addChoosableFileFilter(getXMLFilter()); chooser.addChoosableFileFilter(xsetFileFilter); chooser.addChoosableFileFilter(launcherFileFilter); FontSizer.setFonts(chooser, FontSizer.getLevel()); return chooser; } /** * Refreshes the selected tab. */ public void refreshSelectedTab() { LaunchNode root = getRootNode(); if((root!=null)&&root.isButtonView()&&(Launcher.this.getClass()==Launcher.class)) { showButtonView(root); } else { showTabbedPaneView(); LaunchPanel tab = getSelectedTab(); if(tab!=null) { if(getSelectedNode()!=null) { tab.displayTabs(selectedNode); } else { tab.displayTabs(root); } } } } /** * Refreshes the memory button. */ protected void refreshMemoryButton() { System.gc(); java.lang.management.MemoryMXBean memory = java.lang.management.ManagementFactory.getMemoryMXBean(); long cur = memory.getHeapMemoryUsage().getUsed()/(1024*1024); long max = memory.getHeapMemoryUsage().getMax()/(1024*1024); // if (outOfMemory) { // outOfMemory = false; // cur = max; // JOptionPane.showMessageDialog(memoryButton, // TrackerRes.getString("Tracker.Dialog.OutOfMemory.Message1")+"\n" //$NON-NLS-1$ //$NON-NLS-2$ // + TrackerRes.getString("Tracker.Dialog.OutOfMemory.Message2"), //$NON-NLS-1$ // TrackerRes.getString("Tracker.Dialog.OutOfMemory.Title"), //$NON-NLS-1$ // JOptionPane.WARNING_MESSAGE); // } String mem = LaunchRes.getString("Launcher.Button.Memory")+": "; //$NON-NLS-1$ //$NON-NLS-2$ String of = LaunchRes.getString("Launcher.Of")+" "; //$NON-NLS-1$ //$NON-NLS-2$ memoryButton.setText(mem+cur+"MB "+of+max+"MB"); //$NON-NLS-1$ //$NON-NLS-2$ double used = ((double)cur)/max; memoryButton.setForeground(used>0.8? Color.red: Color.black); } /** * Gets the display name of the specified file name. * * @param fileName the file name * @return the bare name without path or extension */ protected static String getDisplayName(String fileName) { fileName = XML.getName(fileName); int i = fileName.lastIndexOf("."); //$NON-NLS-1$ if(i!=-1) { return fileName.substring(0, i); } return fileName; } /** * Gets the name of the jar containing the default launcher xml file, if any. * * @return the jar name */ protected static String getDefaultJar() { URL url = ClassLoader.getSystemResource(defaultFileName+".xset"); //$NON-NLS-1$ if(url==null) { url = ClassLoader.getSystemResource(defaultFileName+".xml"); //$NON-NLS-1$ } if(url==null) { return null; } String path = url.getPath(); // trim trailing slash and file name int i = path.indexOf("/"+defaultFileName); //$NON-NLS-1$ if(i==-1) { return null; } path = path.substring(0, i); // jar name is followed by "!" i = path.lastIndexOf("!"); //$NON-NLS-1$ if(i==-1) { return null; } // trim and return jar name return path.substring(path.lastIndexOf("/")+1, i); //$NON-NLS-1$ } /** * Displays the properties of the specified node in an xml table inspector. * * @param node the launch node */ private void inspect(LaunchNode node) { XMLControl xml = new XMLControlElement(node); if(hasEJSModel(node)) { String name = XML.getSimpleClassName(node.getLaunchClass()); xml.setValue("EJS_model", name+".xml"); //$NON-NLS-1$//$NON-NLS-2$ } XMLTable table = new XMLTable(xml); tableInspector.setContentPane(new JScrollPane(table)); tableInspector.setTitle(LaunchRes.getString("Inspector.Title.Node")+" \""+node.name+"\""); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ tableInspector.setVisible(true); } private static void findFramesFor(final LaunchNode node, Frame[] prevFrames, final Runnable runner) { // get current frames Frame[] frames = Frame.getFrames(); // make newFrames collection final Collection<Frame> newFrames = new ArrayList<Frame>(); for(int i = 0; i<frames.length; i++) { // ignore message frames, DO_NOTHING frames and incidental frames if(frames[i] instanceof JFrame) { JFrame frame = (JFrame) frames[i]; if((frame.getDefaultCloseOperation()==WindowConstants.DO_NOTHING_ON_CLOSE)||(frame instanceof MessageFrame)||(frame instanceof LauncherFrame)) { continue; } } if((frames[i].getClass().getName().indexOf("SharedOwnerFrame")>-1)||( //$NON-NLS-1$ frames[i].getClass().getName().indexOf("QTFrame")>-1)) { //$NON-NLS-1$ continue; } newFrames.add(frames[i]); } // throw out previous frames for(int i = 0; i<prevFrames.length; i++) { newFrames.remove(prevFrames[i]); } // return if no new frames found if(newFrames.isEmpty()) { return; } // look thru new frames for "control frame" // "control frame" is JFrame/EXIT_ON_CLOSE or OSPFrame/wishesToExit() frames = newFrames.toArray(new Frame[0]); newFrames.clear(); FrameCloser frameCloser = new FrameCloser(node, newFrames, runner); for(int i = 0; i<frames.length; i++) { if(frames[i] instanceof JFrame) { // found new frame JFrame frame = (JFrame) frames[i]; if(((frame instanceof AppFrame)&&((AppFrame) frame).wishesToExit())||(frame.getDefaultCloseOperation()==JFrame.EXIT_ON_CLOSE)) { // found control frame if(frame.getDefaultCloseOperation()==JFrame.EXIT_ON_CLOSE) { // change default close operation from EXIT_ON_CLOSE to DISPOSE_ON_CLOSE // so exiting a launched app does not close Launcher frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); } // add window listener so control frames can close associated windows frame.addWindowListener(frameCloser); } newFrames.add(frame); } } // add frames to node if(node!=null) { node.frames.addAll(newFrames); node.launchCount++; // turn off and release timer if(frameFinder!=null) { frameFinder.stop(); frameFinder = null; } // repaint if(node.launchPanel!=null) { node.launchPanel.repaint(); } } } // a class to refresh a node when a launched frame closes static class FrameCloser extends WindowAdapter { LaunchNode node; Collection<Frame> frames; Runnable runner; FrameCloser(LaunchNode node, Collection<Frame> newFrames, Runnable runner) { frames = newFrames; this.node = node; this.runner = runner; } public void windowClosing(WindowEvent e) { OSPLog.fine("Closing frames for node "+node); //$NON-NLS-1$ Iterator<Frame> it = frames.iterator(); // dispose of control frame and associated frames while(it.hasNext()) { Frame frame = it.next(); // remove this frame closer so it only operates once frame.removeWindowListener(this); frame.dispose(); } if(node!=null) { Thread thread = node.threads.get(runner); if(thread!=null) { thread.interrupt(); node.threads.put(runner, null); } node.frames.removeAll(frames); node.launchCount = Math.max(0, --node.launchCount); if(node.launchPanel!=null) { node.launchPanel.repaint(); } } } } /** * Loads an icon and substitutes default icon if not found. * * @param path the path to the icon image * @return the icon */ protected static Icon loadIcon(String path) { Icon icon = ResourceLoader.getIcon(path); if(icon==null) { icon = defaultIcon; } return icon; } /** * A class to provide a default icon in case any images are missing */ static class DefaultIcon implements Icon { int w = 16, h = 16; // width and height of icon public void paintIcon(Component c, Graphics g, int x, int y) { Color prev = g.getColor(); g.setColor(Color.BLUE); g.drawOval(x+3, y+3, w-6, h-6); g.setColor(prev); } public int getIconWidth() { return w; } public int getIconHeight() { return h; } } /** * A class for displaying html pages in a scrolled textPane. */ public class HTMLPane { @SuppressWarnings("javadoc") public JTextPane editorPane; JScrollPane scroller; // constructor HTMLPane() { editorPane = new JTextPane() { public void paintComponent(Graphics g) { if(OSPRuntime.antiAliasText) { Graphics2D g2 = (Graphics2D) g; RenderingHints rh = g2.getRenderingHints(); rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); rh.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } super.paintComponent(g); } }; editorPane.setEditable(false); editorPane.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if(!undoManager.canUndo()) { return; } if(OSPRuntime.isPopupTrigger(e)) { // make popup and add back item JPopupMenu popup = new JPopupMenu(); JMenuItem item = new JMenuItem(LaunchRes.getString("Popup.MenuItem.Back")); //$NON-NLS-1$ item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { undoManager.undo(); } }); popup.add(item); popup.show(editorPane, e.getX(), e.getY()+8); } } }); HTMLEditorKit editorKit = new HTMLEditorKit(); editorPane.setEditorKit(editorKit); scroller = new JScrollPane(editorPane); } } } /* * Open Source Physics software is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be released * under the GNU GPL license. * * This software 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 this; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */