/* * 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.display; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferStrategy; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.WindowConstants; import org.opensourcephysics.tools.FontSizer; import org.opensourcephysics.tools.ToolsRes; /** * OSPFrame is the base class for Open Source Physics JFrames such as DrawingFrame and DataTableFrame. * * Copyright: Copyright (c) 2002 * @author Wolfgang Christian * @version 1.1 */ public class OSPFrame extends JFrame implements Hidable, AppFrame { /** Location of OSP icon. */ static final String OSP_ICON_FILE = "/org/opensourcephysics/resources/controls/images/osp_icon.gif"; //$NON-NLS-1$ // value is set in static block protected ArrayList<JButton> customButtons = new ArrayList<JButton>(); // list of custom buttons, custom buttons are enabled when the animation is stopped static int topx = 10; static int topy = 100; /** Set to <I>true</I> if a simulation should automatically render this frame after every animation step. */ protected boolean animated = false; /** Set to <I>true</I> if a simulation should automatically clear the data when it is initialized. */ protected boolean autoclear = false; /** Set <I>true</I> if the Frame's defaultCloseOperation has been changed by Launcher. */ private volatile boolean wishesToExit = false; /** The thread group that created this object. */ public ThreadGroup constructorThreadGroup = Thread.currentThread().getThreadGroup(); protected boolean keepHidden = false; protected BufferStrategy strategy; protected JPanel buttonPanel = new JPanel(); protected Collection<JFrame> childFrames = new ArrayList<JFrame>(); /** * Gets a file chooser that is the same for all OSPFrames. * * @deprecated use <code>OSPRuntime.getChooser()<\code>. * @return the chooser */ public static JFileChooser getChooser() { return OSPRuntime.getChooser(); } /** * OSPFrame constructor with a title. * @param title */ public OSPFrame(String title) { super(TeXParser.parseTeX(title)); if(OSPRuntime.appletMode) { keepHidden = true; } buttonPanel.setVisible(false); setLocation(topx, topy); Dimension d = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); topx = Math.min(topx+20, (int) d.getWidth()-100); topy = Math.min(topy+20, (int) d.getHeight()-100); setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); // Changes font size to current level setFontLevel(FontSizer.getLevel()); FontSizer.addPropertyChangeListener("level", new PropertyChangeListener() { //$NON-NLS-1$ public void propertyChange(PropertyChangeEvent e) { int level = ((Integer) e.getNewValue()).intValue(); setFontLevel(level); } }); ToolsRes.addPropertyChangeListener("locale", new PropertyChangeListener() { //$NON-NLS-1$ public void propertyChange(PropertyChangeEvent e) { refreshGUI(); } }); try { URL url = OSPFrame.class.getResource(OSP_ICON_FILE); ImageIcon icon = new ImageIcon(url); setIconImage(icon.getImage()); //setIconImage(ResourceLoader.getImage(OSPRuntime.OSP_ICON_FILE)); } catch(Exception ex) { // image not found } addWindowListener(new WindowAdapter() { /** * Closes and disposes child windows when this window is about to be closed. */ public void windowClosed(WindowEvent e) { disposeChildWindows(); } }); } void disposeChildWindows() { //if(OSPRuntime.applet!=null) return; // applets do not have a main window so return Iterator<JFrame> it = childFrames.iterator(); while(it.hasNext()) { JFrame f = it.next(); if(!f.isDisplayable()) { continue; // frame has already been disposed } if(f instanceof OSPFrame) { ((OSPFrame) f).setKeepHidden(true); } else { f.setVisible(false); } f.dispose(); } childFrames.clear(); } /** * OSPFrame constructor. */ public OSPFrame() { this("Open Source Physics"); //$NON-NLS-1$ } /** * OSPFrame constructor with a new content pane. * @param contentPane */ public OSPFrame(Container contentPane) { this(); setContentPane(contentPane); } /** * Sets the title for this frame to the specified string after converting TeX math symbols to characters. * @param title the title to be displayed in the frame's border. * A <code>null</code> value * is treated as an empty string, "". * @see #getTitle */ public void setTitle(String title) { super.setTitle(TeXParser.parseTeX(title)); } /** * Adds a child frame that depends on this frame. * Child frames are closed when this frame is closed. * * @param frame JFrame */ public void addChildFrame(JFrame frame) { if((frame==null)||!frame.isDisplayable()) { return; } childFrames.add(frame); } /** * Clears all frames from the child frame list. */ public void clearChildFrames() { childFrames.clear(); } /** * Gets a copy of the ChildFrames collection. * @return Collection */ public Collection<JFrame> getChildFrames() { return new ArrayList<JFrame>(childFrames); } /** * Gets the ICONIFIED flag for this frame. * * @return boolean true if frame is iconified; false otherwise */ public boolean isIconified() { return(getExtendedState()&ICONIFIED)==1; } /** * Invalidates image buffers if a drawing panel is buffered. */ public void invalidateImage() { // default does nothing } /** * Sets the font level. * * @param level the level */ protected void setFontLevel(int level) { FontSizer.setFonts(getJMenuBar(), level); FontSizer.setFonts(getContentPane(), level); } /** * Reads the animated property. * * @return boolean */ public boolean isAnimated() { return animated; } /** * Sets the animated property. * * @param animated */ public void setAnimated(boolean animated) { this.animated = animated; } /** * Reads the animated property. * * @return boolean */ public boolean isAutoclear() { return autoclear; } /** * Sets the autoclear property. * * @param autoclear */ public void setAutoclear(boolean autoclear) { this.autoclear = autoclear; } /** * Adds a Display menu to the menu bar. * * The default method does nothing. * Override this method to create a menu item that is appropriate for the frame. */ protected JMenu loadDisplayMenu() { return null; } /** * Adds a Tools menu to the menu bar. * * The default method does nothing. * Override this method to create a menu item that is appropriate for the frame. */ protected JMenu loadToolsMenu() { return null; } /** * Clears data from drawing objects within this frame. * * The default method does nothing. * Override this method to select the object(s) and the data to be cleared. */ public void clearData() {} /** * Clears data and repaints the drawing panel within this frame. * * The default method does nothing. * Override this method to clear and repaint objects that have data. */ public void clearDataAndRepaint() {} public void setSize(int width, int height) { super.setSize(width, height); validate(); } /** * Shows the frame on the screen if the keep hidden flag is false. * * @deprecated */ public void show() { if(!keepHidden) { super.show(); } } /** * Disposes all resources. */ public void dispose() { keepHidden = true; this.clearData(); disposeChildWindows(); super.dispose(); } /** * Shows or hides this component depending on the value of parameter * <code>b</code> and the <code>keepHidden</code> flag. * * OSP Applets often keep windows hidden. * * @param b */ public void setVisible(boolean b) { if(!keepHidden) { boolean shouldRender = (!isVisible())&&animated; // render animated frames when made visible super.setVisible(b); if(shouldRender) { render(); } } } /** * Sets the keepHidden flag. * * @param _keepHidden */ public void setKeepHidden(boolean _keepHidden) { keepHidden = _keepHidden; if(keepHidden) { super.setVisible(false); } } /** * Reads the keepHidden flag. * */ public boolean isKeepHidden() { return keepHidden; } /** * Gets the ThreadGroup that constructed this frame. * * @return the ThreadGroup */ public ThreadGroup getConstructorThreadGroup() { return constructorThreadGroup; } /** * Creates a BufferStrategy based on the capabilites of the hardware. */ public void createBufferStrategy() { createBufferStrategy(2); strategy = this.getBufferStrategy(); } /** * Shows (repaints) the frame useing the current BufferStrategy. */ public void bufferStrategyShow() { if((strategy)==null) { createBufferStrategy(); } if(isIconified()||!isShowing()) { return; } Graphics g = strategy.getDrawGraphics(); paintComponents(g); g.dispose(); strategy.show(); } /** * Renders the frame. Subclass this method to render the contents of this frame in the calling thread. */ public void render() {} /** * Gets a menu with the given name from the menu bar. Returns null if menu item does not exist. * * @param menuName String * @return JMenu */ public JMenu getMenu(String menuName) { JMenuBar menuBar = getJMenuBar(); if(menuBar==null) { return null; } menuName = menuName.trim(); JMenu menu = null; for(int i = 0; i<menuBar.getMenuCount(); i++) { JMenu next = menuBar.getMenu(i); if(next.getText().trim().equals(menuName)) { menu = next; break; } } return menu; } /** * Removes a menu with the given name from the menu bar and returns the removed item. * Returns null if menu item does not exist. * * @param menuName String * @return JMenu */ public JMenu removeMenu(String menuName) { JMenuBar menuBar = getJMenuBar(); if(menuBar==null) { return null; } menuName = menuName.trim(); JMenu menu = null; for(int i = 0; i<menuBar.getMenuCount(); i++) { JMenu next = menuBar.getMenu(i); if(next.getText().trim().equals(menuName)) { menu = next; menuBar.remove(i); break; } } return menu; } /** * Removes a menu item with the given name from the menu bar and returns the removed item. * Returns null if menu item does not exist. * * @param menuName String * @return JMenu */ public JMenuItem removeMenuItem(String menuName, String itemName) { JMenu menu = getMenu(menuName); if(menu==null) { return null; } itemName = itemName.trim(); JMenuItem item = null; for(int i = 0; i<menu.getItemCount(); i++) { JMenuItem next = menu.getItem(i); if(next.getText().trim().equals(itemName)) { item = next; menu.remove(i); break; } } return item; } /* * Creates a menu in the menu bar from the given XML document. * @param xmlMenu name of the xml file with menu data */ public void parseXMLMenu(String xmlMenu) { parseXMLMenu(xmlMenu, null); } public void parseXMLMenu(String xmlMenu, Class<?> type) { System.out.println("The parseXMLMenu method has been disabled to reduce the size OSP jar files."); //$NON-NLS-1$ } /* * Creates a menu in the menu bar from the given XML document. * @param xmlMenu name of the xml file with menu data * @param type the class to load the menu, may be null * * public void parseXMLMenu(String xmlMenu, Class type) { * XMLControl xml = null; * if(type!=null) { * org.opensourcephysics.tools.Resource res = org.opensourcephysics.tools.ResourceLoader.getResource(xmlMenu, * type); * if(res!=null) { * xml = new XMLControlElement(res.getString()); * } * } * if(xml==null) { * xml = new XMLControlElement(xmlMenu); * } * if(xml.failedToRead()) { * OSPLog.info("Menu not found: "+xmlMenu); * } else { * type = xml.getObjectClass(); * if((type!=null)&&org.opensourcephysics.tools.LaunchNode.class.isAssignableFrom(type)) { * // load the xml data into a launch node and add the menu item * org.opensourcephysics.tools.LaunchNode node = (org.opensourcephysics.tools.LaunchNode) xml.loadObject(null); * JMenuBar menuBar = getJMenuBar(); * if(menuBar==null) { * return; * } * // get the menu name and create the menu if null * String menuName = node.toString(); * JMenu menu = getMenu(menuName); * if(menu==null) { * menu = new JMenu(menuName); * menuBar.add(menu); * menuBar.validate(); * } * // add the node item to the menu * node.addMenuItemsTo(menu); * OSPLog.finest("Menu loaded: "+xmlMenu); * } * } * } */ /** * Refreshes the user interface in response to display changes such as Language. */ protected void refreshGUI() { Iterator<JButton> it = customButtons.iterator(); while(it.hasNext()) { TranslatableButton b = (TranslatableButton) it.next(); b.refreshGUI(); } buttonPanel.validate(); } /** * Adds a custom button to the control's frame. * * @param methodName the name of the method; the method has no parameters * @param text the button's text label * @param toolTipText the button's tool tip text * @param target the target for the method * @return the custom button */ public JButton addButton(String methodName, String text, String toolTipText, final Object target) { TranslatableButton b = new TranslatableButton(text, toolTipText, target); // changed by D Brown 2007-10-17 if(OSPRuntime.getTranslator()!=null) { text = OSPRuntime.getTranslator().getProperty(target.getClass(), "custom_button."+text, text); //$NON-NLS-1$ toolTipText = OSPRuntime.getTranslator().getProperty(target.getClass(), "custom_button."+toolTipText, toolTipText); //$NON-NLS-1$ } b.setText(text); b.setToolTipText(toolTipText); Class<?>[] parameters = {}; try { final java.lang.reflect.Method m = target.getClass().getMethod(methodName, parameters); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Object[] args = {}; try { m.invoke(target, args); } catch(IllegalAccessException iae) { System.err.println(iae); } catch(java.lang.reflect.InvocationTargetException ite) { System.err.println(ite); } } }); buttonPanel.setVisible(true); buttonPanel.add(b); validate(); pack(); } catch(NoSuchMethodException nsme) { System.err.println("Error adding custom button "+text+". The method "+methodName+"() does not exist."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } customButtons.add(b); return b; } class TranslatableButton extends JButton { String text, tip; Object target; /** * Constructor TranslatableButton * @param text * @param tip * @param target */ public TranslatableButton(String text, String tip, Object target) { this.text = text; this.tip = tip; this.target = target; } void refreshGUI() { if(OSPRuntime.getTranslator()!=null) { setText(OSPRuntime.getTranslator().getProperty(target.getClass(), "custom_button."+text, text)); //$NON-NLS-1$ setToolTipText(OSPRuntime.getTranslator().getProperty(target.getClass(), "custom_button."+tip, tip)); //$NON-NLS-1$ } } } /** * Overrides JFrame method. This converts EXIT_ON_CLOSE to DISPOSE_ON_CLOSE * and sets the wishesToExit flag. * * @param operation the operation */ public void setDefaultCloseOperation(int operation) { if((operation==JFrame.EXIT_ON_CLOSE)&&OSPRuntime.launchingInSingleVM) { operation = WindowConstants.DISPOSE_ON_CLOSE; wishesToExit = true; } try { super.setDefaultCloseOperation(operation); } catch(Exception ex) { // cannot set the default close operation for java applets frames in Java 1.6 } } /** * Returns true if this frame wishes to exit. * Launcher uses this to identify control frames. * * @return true if this frame wishes to exit */ public boolean wishesToExit() { return wishesToExit; } } /* * 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 */