X-Git-Url: https://git.gag.com/?a=blobdiff_plain;ds=sidebyside;f=src%2Fnet%2Fsf%2Fopenrocket%2Futil%2FGUIUtil.java;h=831f2381afc44b53f13d84fb536cfc38d87572dd;hb=8320c04afa30e2aa0150adc870d02abeedb01066;hp=b39338907f20bfae2bbfdf858c655829db523e52;hpb=e298a9509613f232227d16d28310611b33c3aa03;p=debian%2Fopenrocket diff --git a/src/net/sf/openrocket/util/GUIUtil.java b/src/net/sf/openrocket/util/GUIUtil.java index b3933890..831f2381 100644 --- a/src/net/sf/openrocket/util/GUIUtil.java +++ b/src/net/sf/openrocket/util/GUIUtil.java @@ -2,6 +2,7 @@ package net.sf.openrocket.util; import java.awt.Component; import java.awt.Container; +import java.awt.Font; import java.awt.Image; import java.awt.KeyboardFocusManager; import java.awt.Point; @@ -21,16 +22,16 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; -import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.Vector; import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.AbstractButton; import javax.swing.Action; +import javax.swing.BoundedRangeModel; +import javax.swing.ComboBoxModel; import javax.swing.DefaultBoundedRangeModel; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListSelectionModel; @@ -44,88 +45,104 @@ import javax.swing.JSpinner; import javax.swing.JTable; import javax.swing.JTree; import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; import javax.swing.LookAndFeel; import javax.swing.RootPaneContainer; +import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; import javax.swing.UIManager; +import javax.swing.border.TitledBorder; import javax.swing.event.ChangeListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; +import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeSelectionModel; -import javax.swing.tree.TreeNode; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeSelectionModel; import net.sf.openrocket.gui.Resettable; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.startup.Application; public class GUIUtil { - + private static final LogHelper log = Application.getLogger(); + private static final KeyStroke ESCAPE = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - private static final String CLOSE_ACTION_KEY = "escape:WINDOW_CLOSING"; + private static final String CLOSE_ACTION_KEY = "escape:WINDOW_CLOSING"; + + private static final List images = new ArrayList(); + static { + loadImage("pix/icon/icon-256.png"); + loadImage("pix/icon/icon-064.png"); + loadImage("pix/icon/icon-048.png"); + loadImage("pix/icon/icon-032.png"); + loadImage("pix/icon/icon-016.png"); + } + + private static void loadImage(String file) { + InputStream is; + + is = ClassLoader.getSystemResourceAsStream(file); + if (is == null) + return; + + try { + Image image = ImageIO.read(is); + images.add(image); + } catch (IOException ignore) { + ignore.printStackTrace(); + } + } + - private static final List images = new ArrayList(); - static { - loadImage("pix/icon/icon-256.png"); - loadImage("pix/icon/icon-064.png"); - loadImage("pix/icon/icon-048.png"); - loadImage("pix/icon/icon-032.png"); - loadImage("pix/icon/icon-016.png"); - } - private static void loadImage(String file) { - InputStream is; - - is = ClassLoader.getSystemResourceAsStream(file); - if (is == null) - return; - - try { - Image image = ImageIO.read(is); - images.add(image); - } catch (IOException ignore) { - ignore.printStackTrace(); - } - } - - - /** - * Set suitable options for a single-use disposable dialog. This includes - * setting ESC to close the dialog and adding the appropriate window icons. - * If defaultButton is provided, it is set to the default button action. - *

- * The default button must be already attached to the dialog. - * - * @param dialog the dialog. - * @param defaultButton the default button of the dialog, or null. - */ - public static void setDisposableDialogOptions(JDialog dialog, JButton defaultButton) { - installEscapeCloseOperation(dialog); - setWindowIcons(dialog); - addModelNullingListener(dialog); - if (defaultButton != null) { - setDefaultButton(defaultButton); - } - } - + /** + * Set suitable options for a single-use disposable dialog. This includes + * setting ESC to close the dialog, adding the appropriate window icons and + * setting the location based on the platform. If defaultButton is provided, + * it is set to the default button action. + *

+ * The default button must be already attached to the dialog. + * + * @param dialog the dialog. + * @param defaultButton the default button of the dialog, or null. + */ + public static void setDisposableDialogOptions(JDialog dialog, JButton defaultButton) { + installEscapeCloseOperation(dialog); + setWindowIcons(dialog); + addModelNullingListener(dialog); + dialog.setLocationByPlatform(true); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.pack(); + if (defaultButton != null) { + setDefaultButton(defaultButton); + } + } + /** * Add the correct action to close a JDialog when the ESC key is pressed. * The dialog is closed by sending is a WINDOW_CLOSING event. * * @param dialog the dialog for which to install the action. */ - public static void installEscapeCloseOperation(final JDialog dialog) { - Action dispatchClosing = new AbstractAction() { - public void actionPerformed(ActionEvent event) { - dialog.dispatchEvent(new WindowEvent(dialog, WindowEvent.WINDOW_CLOSING)); - } - }; - JRootPane root = dialog.getRootPane(); - root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ESCAPE, CLOSE_ACTION_KEY); - root.getActionMap().put(CLOSE_ACTION_KEY, dispatchClosing); + public static void installEscapeCloseOperation(final JDialog dialog) { + Action dispatchClosing = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent event) { + log.user("Closing dialog " + dialog); + dialog.dispatchEvent(new WindowEvent(dialog, WindowEvent.WINDOW_CLOSING)); + } + }; + JRootPane root = dialog.getRootPane(); + root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ESCAPE, CLOSE_ACTION_KEY); + root.getActionMap().put(CLOSE_ACTION_KEY, dispatchClosing); } @@ -141,59 +158,61 @@ public class GUIUtil { throw new IllegalArgumentException("Attach button to a window first."); } if (!(w instanceof RootPaneContainer)) { - throw new IllegalArgumentException("Button not attached to RootPaneContainer, w="+w); + throw new IllegalArgumentException("Button not attached to RootPaneContainer, w=" + w); } - ((RootPaneContainer)w).getRootPane().setDefaultButton(button); + ((RootPaneContainer) w).getRootPane().setDefaultButton(button); } - + /** * Change the behavior of a component so that TAB and Shift-TAB cycles the focus of * the components. This is necessary for e.g. JTextArea. * * @param c the component to modify */ - public static void setTabToFocusing(Component c) { - Set strokes = new HashSet(Arrays.asList(KeyStroke.getKeyStroke("pressed TAB"))); - c.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, strokes); - strokes = new HashSet(Arrays.asList(KeyStroke.getKeyStroke("shift pressed TAB"))); - c.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, strokes); - } + public static void setTabToFocusing(Component c) { + Set strokes = new HashSet(Arrays.asList(KeyStroke.getKeyStroke("pressed TAB"))); + c.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, strokes); + strokes = new HashSet(Arrays.asList(KeyStroke.getKeyStroke("shift pressed TAB"))); + c.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, strokes); + } + + - - - /** - * Set the OpenRocket icons to the window icons. - * - * @param window the window to set. - */ - public static void setWindowIcons(Window window) { - window.setIconImages(images); - } - - /** - * Add a listener to the provided window that will call {@link #setNullModels(Component)} - * on the window once it is closed. This method may only be used on single-use - * windows and dialogs, that will never be shown again once closed! - * - * @param window the window to add the listener to. - */ - public static void addModelNullingListener(final Window window) { - window.addWindowListener(new WindowAdapter() { + /** + * Set the OpenRocket icons to the window icons. + * + * @param window the window to set. + */ + public static void setWindowIcons(Window window) { + window.setIconImages(images); + } + + /** + * Add a listener to the provided window that will call {@link #setNullModels(Component)} + * on the window once it is closed. This method may only be used on single-use + * windows and dialogs, that will never be shown again once closed! + * + * @param window the window to add the listener to. + */ + public static void addModelNullingListener(final Window window) { + window.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { + log.debug("Clearing all models of window " + window); setNullModels(window); + MemoryManagement.collectable(window); } - }); - } - + }); + } + + - - /** - * Set the best available look-and-feel into use. - */ - public static void setBestLAF() { + /** + * Set the best available look-and-feel into use. + */ + public static void setBestLAF() { /* * Set the look-and-feel. On Linux, Motif/Metal is sometimes incorrectly used * which is butt-ugly, so if the system l&f is Motif/Metal, we search for a few @@ -219,21 +238,60 @@ public class GUIUtil { ".*[nN][iI][mM][bB].*" }; - lf: for (String lafName: lafNames) { - for (UIManager.LookAndFeelInfo l: info) { + lf: for (String lafName : lafNames) { + for (UIManager.LookAndFeelInfo l : info) { if (l.getName().matches(lafName)) { UIManager.setLookAndFeel(l.getClassName()); break lf; } - } + } } } } catch (Exception e) { - System.err.println("Error setting LAF: " + e); + log.warn("Error setting LAF: " + e); } - } - - + } + + + /** + * Changes the size of the font of the specified component by the given amount. + * + * @param component the component for which to change the font + * @param size the change in the font size + */ + public static void changeFontSize(JComponent component, float size) { + Font font = component.getFont(); + font = font.deriveFont(font.getSize2D() + size); + component.setFont(font); + } + + + /** + * Changes the style of the font of the specified border. + * + * @param border the component for which to change the font + * @param style the change in the font style + */ + public static void changeFontStyle(TitledBorder border, int style) { + /* + * There's been an NPE caused by the font changing, this is debug for it. + */ + if (border == null) { + throw new BugException("border is null"); + } + Font font = border.getTitleFont(); + if (font == null) { + throw new BugException("Border font is null"); + } + font = font.deriveFont(style); + if (font == null) { + throw new BugException("Derived font is null"); + } + border.setTitleFont(font); + } + + + /** * Traverses recursively the component tree, and sets all applicable component * models to null, so as to remove the listener connections. After calling this @@ -245,155 +303,164 @@ public class GUIUtil { * @param c the component (null is ok) */ public static void setNullModels(Component c) { - if (c==null) + if (c == null) return; // Remove various listeners - for (ComponentListener l: c.getComponentListeners()) { + for (ComponentListener l : c.getComponentListeners()) { c.removeComponentListener(l); } - for (FocusListener l: c.getFocusListeners()) { + for (FocusListener l : c.getFocusListeners()) { c.removeFocusListener(l); } - for (MouseListener l: c.getMouseListeners()) { + for (MouseListener l : c.getMouseListeners()) { c.removeMouseListener(l); } - for (PropertyChangeListener l: c.getPropertyChangeListeners()) { + for (PropertyChangeListener l : c.getPropertyChangeListeners()) { c.removePropertyChangeListener(l); } - for (PropertyChangeListener l: c.getPropertyChangeListeners("model")) { + for (PropertyChangeListener l : c.getPropertyChangeListeners("model")) { c.removePropertyChangeListener("model", l); } - for (PropertyChangeListener l: c.getPropertyChangeListeners("action")) { + for (PropertyChangeListener l : c.getPropertyChangeListeners("action")) { c.removePropertyChangeListener("action", l); } // Remove models for known components // Why the FSCK must this be so hard?!?!? - + if (c instanceof JSpinner) { - JSpinner spinner = (JSpinner)c; - for (ChangeListener l: spinner.getChangeListeners()) { + JSpinner spinner = (JSpinner) c; + for (ChangeListener l : spinner.getChangeListeners()) { spinner.removeChangeListener(l); } + SpinnerModel model = spinner.getModel(); spinner.setModel(new SpinnerNumberModel()); + if (model instanceof Invalidatable) { + ((Invalidatable) model).invalidate(); + } } else if (c instanceof JSlider) { - JSlider slider = (JSlider)c; - for (ChangeListener l: slider.getChangeListeners()) { + JSlider slider = (JSlider) c; + for (ChangeListener l : slider.getChangeListeners()) { slider.removeChangeListener(l); } + BoundedRangeModel model = slider.getModel(); slider.setModel(new DefaultBoundedRangeModel()); + if (model instanceof Invalidatable) { + ((Invalidatable) model).invalidate(); + } } else if (c instanceof JComboBox) { - JComboBox combo = (JComboBox)c; - for (ActionListener l: combo.getActionListeners()) { + JComboBox combo = (JComboBox) c; + for (ActionListener l : combo.getActionListeners()) { combo.removeActionListener(l); } + ComboBoxModel model = combo.getModel(); combo.setModel(new DefaultComboBoxModel()); + if (model instanceof Invalidatable) { + ((Invalidatable) model).invalidate(); + } } else if (c instanceof AbstractButton) { - AbstractButton button = (AbstractButton)c; - for (ActionListener l: button.getActionListeners()) { + AbstractButton button = (AbstractButton) c; + for (ActionListener l : button.getActionListeners()) { button.removeActionListener(l); } + Action model = button.getAction(); button.setAction(new AbstractAction() { @Override - public void actionPerformed(ActionEvent e) { } + public void actionPerformed(ActionEvent e) { + } }); + if (model instanceof Invalidatable) { + ((Invalidatable) model).invalidate(); + } } else if (c instanceof JTable) { - JTable table = (JTable)c; + JTable table = (JTable) c; + TableModel model1 = table.getModel(); table.setModel(new DefaultTableModel()); + if (model1 instanceof Invalidatable) { + ((Invalidatable) model1).invalidate(); + } + + TableColumnModel model2 = table.getColumnModel(); table.setColumnModel(new DefaultTableColumnModel()); + if (model2 instanceof Invalidatable) { + ((Invalidatable) model2).invalidate(); + } + + ListSelectionModel model3 = table.getSelectionModel(); table.setSelectionModel(new DefaultListSelectionModel()); + if (model3 instanceof Invalidatable) { + ((Invalidatable) model3).invalidate(); + } } else if (c instanceof JTree) { - JTree tree = (JTree)c; - tree.setModel(new DefaultTreeModel(new TreeNode() { - @SuppressWarnings("unchecked") - @Override - public Enumeration children() { - return new Vector().elements(); - } - @Override - public boolean getAllowsChildren() { - return false; - } - @Override - public TreeNode getChildAt(int childIndex) { - return null; - } - @Override - public int getChildCount() { - return 0; - } - @Override - public int getIndex(TreeNode node) { - return 0; - } - @Override - public TreeNode getParent() { - return null; - } - @Override - public boolean isLeaf() { - return true; - } - })); + JTree tree = (JTree) c; + TreeModel model1 = tree.getModel(); + tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode())); + if (model1 instanceof Invalidatable) { + ((Invalidatable) model1).invalidate(); + } + + TreeSelectionModel model2 = tree.getSelectionModel(); tree.setSelectionModel(new DefaultTreeSelectionModel()); + if (model2 instanceof Invalidatable) { + ((Invalidatable) model2).invalidate(); + } } else if (c instanceof Resettable) { - ((Resettable)c).resetModel(); + ((Resettable) c).resetModel(); } - + // Recurse the component if (c instanceof Container) { - Component[] cs = ((Container)c).getComponents(); - for (Component sub: cs) + Component[] cs = ((Container) c).getComponents(); + for (Component sub : cs) setNullModels(sub); } - + } - - /** - * A mouse listener that toggles the state of a boolean value in a table model - * when clicked on another column of the table. - *

- * NOTE: If the table model does not extend AbstractTableModel, the model must - * fire a change event (which in normal table usage is not necessary). - * - * @author Sampo Niskanen - */ - public static class BooleanTableClickListener extends MouseAdapter { - - private final JTable table; - private final int clickColumn; - private final int booleanColumn; - - - public BooleanTableClickListener(JTable table) { - this(table, 1, 0); - } - - - public BooleanTableClickListener(JTable table, int clickColumn, int booleanColumn) { - this.table = table; - this.clickColumn = clickColumn; - this.booleanColumn = booleanColumn; - } - + /** + * A mouse listener that toggles the state of a boolean value in a table model + * when clicked on another column of the table. + *

+ * NOTE: If the table model does not extend AbstractTableModel, the model must + * fire a change event (which in normal table usage is not necessary). + * + * @author Sampo Niskanen + */ + public static class BooleanTableClickListener extends MouseAdapter { + + private final JTable table; + private final int clickColumn; + private final int booleanColumn; + + + public BooleanTableClickListener(JTable table) { + this(table, 1, 0); + } + + + public BooleanTableClickListener(JTable table, int clickColumn, int booleanColumn) { + this.table = table; + this.clickColumn = clickColumn; + this.booleanColumn = booleanColumn; + } + @Override public void mouseClicked(MouseEvent e) { if (e.getButton() != MouseEvent.BUTTON1) @@ -404,7 +471,7 @@ public class GUIUtil { if (col < 0) return; col = table.convertColumnIndexToModel(col); - if (col != clickColumn) + if (col != clickColumn) return; int row = table.rowAtPoint(p); @@ -418,18 +485,18 @@ public class GUIUtil { Object value = model.getValueAt(row, booleanColumn); if (!(value instanceof Boolean)) { - throw new IllegalStateException("Table value at row="+row+" col="+ - booleanColumn + " is not a Boolean, value=" +value); + throw new IllegalStateException("Table value at row=" + row + " col=" + + booleanColumn + " is not a Boolean, value=" + value); } - Boolean b = (Boolean)value; + Boolean b = (Boolean) value; b = !b; model.setValueAt(b, row, booleanColumn); if (model instanceof AbstractTableModel) { - ((AbstractTableModel)model).fireTableCellUpdated(row, booleanColumn); + ((AbstractTableModel) model).fireTableCellUpdated(row, booleanColumn); } } - - } - + + } + }