From 40a9d99496e098562f090fb7ffce9e749011b131 Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Mon, 20 May 2024 17:58:16 -0400 Subject: Formatting pass --- .../ui/swing/components/AbsoluteLayout.java | 61 +- .../ui/swing/components/AlphaTextField.java | 593 ++-- .../components/AlternatingRowCellRenderer.java | 157 +- .../ui/swing/components/BetterFlowLayout.java | 952 +++---- .../ui/swing/components/BetterRootLayout.java | 268 +- .../ui/swing/components/BetterTableUI.java | 176 +- .../wotonomy/ui/swing/components/ButtonPanel.java | 1066 ++++--- .../ui/swing/components/CheckButtonPanel.java | 455 ++- .../ui/swing/components/ColorCellEditor.java | 88 +- .../ui/swing/components/ColorCellRenderer.java | 79 +- .../ui/swing/components/ComboBoxCellRenderer.java | 44 +- .../ui/swing/components/DateTextField.java | 1098 ++++---- .../ui/swing/components/FormattedCellRenderer.java | 460 ++-- .../ui/swing/components/IconCellRenderer.java | 1272 ++++----- .../wotonomy/ui/swing/components/ImagePanel.java | 91 +- .../wotonomy/ui/swing/components/InfoPanel.java | 2908 ++++++++++---------- .../ui/swing/components/KeyDelayTimer.java | 301 +- .../ui/swing/components/KeyableCellEditor.java | 566 ++-- .../ui/swing/components/LineWrappingRenderer.java | 225 +- .../ui/swing/components/MultiLineLabel.java | 163 +- .../ui/swing/components/NumericTextField.java | 754 +++-- .../ui/swing/components/PropertyEditorTable.java | 927 +++---- .../swing/components/PropertyEditorTableModel.java | 538 ++-- .../ui/swing/components/RadioButtonPanel.java | 278 +- .../ui/swing/components/SmartPasswordField.java | 456 ++- .../ui/swing/components/SmartTextField.java | 398 ++- .../ui/swing/components/StatusButtonPanel.java | 435 ++- .../ui/swing/components/TintedImageFilter.java | 122 +- .../wotonomy/ui/swing/components/TreeChooser.java | 1134 ++++---- .../ui/swing/components/TreeTableCellRenderer.java | 305 +- 30 files changed, 7663 insertions(+), 8707 deletions(-) (limited to 'projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components') diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AbsoluteLayout.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AbsoluteLayout.java index 1fef587..579a595 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AbsoluteLayout.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AbsoluteLayout.java @@ -25,50 +25,43 @@ import java.awt.LayoutManager; import java.io.Serializable; /** - * AbsoluteLayout specifies that all components in the - * container will be placed according to their size - * and their location relative to the container's origin.

+ * AbsoluteLayout specifies that all components in the container will be placed + * according to their size and their location relative to the container's + * origin.
+ *
* - * You can achieve the same effect by setting a container's - * layout manager to null, but this class allows you to subclass - * it if you need specific control or functionality. + * You can achieve the same effect by setting a container's layout manager to + * null, but this class allows you to subclass it if you need specific control + * or functionality. * * @author michael@mpowers.net * @author $Author: cgruber $ * @version $Revision: 904 $ */ -public class AbsoluteLayout implements LayoutManager, Serializable -{ - public void addLayoutComponent(String name, - Component comp) - { - } +public class AbsoluteLayout implements LayoutManager, Serializable { + public void addLayoutComponent(String name, Component comp) { + } - public void removeLayoutComponent(Component comp) - { - } + public void removeLayoutComponent(Component comp) { + } - public Dimension preferredLayoutSize(Container parent) - { - return minimumLayoutSize( parent ); - } + public Dimension preferredLayoutSize(Container parent) { + return minimumLayoutSize(parent); + } - public Dimension minimumLayoutSize(Container parent) - { - int width = 0; - int height = 0; + public Dimension minimumLayoutSize(Container parent) { + int width = 0; + int height = 0; - Component[] c = parent.getComponents(); - for ( int i = 0; i < c.length; i++ ) - { - width = Math.max( width, c[i].getLocation().x + c[i].getBounds().width ); - height = Math.max( height, c[i].getLocation().y + c[i].getBounds().height ); - } + Component[] c = parent.getComponents(); + for (int i = 0; i < c.length; i++) { + width = Math.max(width, c[i].getLocation().x + c[i].getBounds().width); + height = Math.max(height, c[i].getLocation().y + c[i].getBounds().height); + } - return new Dimension( width, height ); - } + return new Dimension(width, height); + } - public void layoutContainer(Container parent) - { - } + public void layoutContainer(Container parent) { + } } diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlphaTextField.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlphaTextField.java index c36f5e2..9300d35 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlphaTextField.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlphaTextField.java @@ -19,317 +19,286 @@ License along with this library; if not, see http://www.gnu.org package net.wotonomy.ui.swing.components; /** -* AlphaTextField is a "smart" text field that restricts the user's input. The -* input can be restricted to alphabetic, alphanumeric, or all characters. The -* maximum number of characters can also be limited. -* The defaults for this component is alphabetic only string of unlimited length. -* -* @author rob@straylight.princeton.com -* @author $Author: cgruber $ -* @version $Revision: 893 $ -*/ -public class AlphaTextField extends SmartTextField -{ - -/******************************* -* CONSTANTS -*******************************/ - -/** -* Sets the input to alphabetic characters only. The characters "a-z" and "A-Z" -* are the only valid characters. All other characters will be ignored. -* @see #getAlphaType() -*/ - public static final int ALPHABETIC = 0; - -/** -* Sets the input to alphanumeric characters only. The characters "a-z", "A-Z" -* and "0-9" are the only valid characters. All other characters will be ignored. -* @see #getAlphaType() -*/ - public static final int ALPHANUMERIC = 1; - -/** -* Sets the input to alphanumeric characters and a few special characters only. -* The valid characters are "a-z", "A-Z", "0-9", space, "-", "_", "\", and ":". -* This is helpful for file names (with paths) as input strings. -* All other characters will be ignored. -* @see #getAlphaType() -*/ - public static final int ALPHANUMERIC_PLUS = 2; - -/** -* Sets the input to all characters without restriction. -* @see #getAlphaType() -*/ - public static final int ALL = 3; - - -/******************************* -* DATA MEMBERS -*******************************/ - - // The level of input restrictions, defaults to ALPHABETIC - private int alphaType; - - // The maximum length of the input string, defaults to 0, no maximum - private int stringLength; - - -/******************************* -* PUBLIC METHODS -*******************************/ - -/** -* The default constructor of this class. The default string of this text -* field is set to the empty string (""). The maximum length is set to 0, -* which specifies no limit. -*/ - public AlphaTextField() - { - this("", 0); - } - -/** -* Constructor of this class with the initial text of the text field specified. -* The maximum length is set to 0, which specifies no limit. -* @param text Initial text of the text field. -*/ - public AlphaTextField(String text) - { - this(text, 0); - } - -/** -* Constructor of this class with width (in columns) of the text field specified. -* The initial text is set to the empty string (""). The maximum length is set -* to 0, which specifies no limit. -* @param columns The width of the text field in characters. -*/ - public AlphaTextField(int columns) - { - this("", columns); - } - -/** -* Constructor of this class with width (in columns) and initial text of the -* text field specified. The maximum length is set to 0, which specifies no limit. -* @param text Initial text of the text field. -* @param columns The width of the text field in characters. -*/ - public AlphaTextField(String text, int columns) - { - super(text, columns); - } - -/** -* Constructor that allows the user to set the Alpha type of the text field -* and the maximum string length. -* @param anAlphaType The character restriction type. -* @param aLength The maximum number of characters allowed in the string. -*/ - public AlphaTextField(int anAlphaType, int aLength) - { - super( "", 0 ); - setAlphaType( anAlphaType ); - setStringLength( aLength ); - } - -/** -* Gets the current restriction type of this text field. -* @see #ALPHABETIC -* @see #ALPHANUMERIC -* @see #ALPHANUMERIC_PLUS -* @see #ALL -* @return The current restriction type as defined by the constansts of this class. -*/ - public int getAlphaType() - { - return alphaType; - } - -/** -* Sets the restriction type of this text field. -* @see #ALPHABETIC -* @see #ALPHANUMERIC -* @see #ALPHANUMERIC_PLUS -* @see #ALL -* @param newAlphaType The restriction of this text field. -*/ - public void setAlphaType(int newAlphaType) - { - switch (newAlphaType) - { - case ALPHABETIC: - case ALPHANUMERIC: - case ALPHANUMERIC_PLUS: - case ALL: - { - alphaType = newAlphaType; - break; - } - default: - { - alphaType = ALPHABETIC; - break; - } - } - } - -/** -* Sets the maximum string length of this text field. If the length is set to -* zero, then there is no limit. The default string length is zero. Negative -* sizes will set the length to zero. -* @param newStringLength The maximum length of the string that the user can input. -*/ - public void setStringLength(int newStringLength) - { - if (newStringLength < 0) - { - stringLength = 0; - } - else - { - stringLength = newStringLength; - } - } - -/** -* Gets the current length of the maximum string size the user can enter. -* @return The maximum length the string of the text field can be. -*/ - public int getStringLength() - { - return stringLength; - } - - -/******************************* -* PROTECTED METHODS -*******************************/ - - protected boolean isValidCharacter(char aChar) - { - // if its a non-printable character, then its ok - if ((aChar < ' ') || (aChar > '~')) - { - return true; - } - - // can only be a printable character now, check it for validation - return isValidCharacterType(aChar); - } - - protected boolean isValidString(String aString) - { - if (aString.length() > stringLength) - { - return false; - } - - for (int i = 0; i < aString.length(); ++i) - { - if (!(isValidCharacterType(aString.charAt(i)))) - { - return false; - } - } - - return true; - } - - protected void postProcessing() - { - // No need to do anything. - } - - -/******************************* -* PROTECTED METHODS -*******************************/ - - private boolean isValidCharacterType(char aChar) - { - switch (alphaType) - { - case ALPHABETIC: - { - if (!(isValidAlphabeticCharacter(aChar))) - { - return false; - } - break; - } - case ALPHANUMERIC: - { - if (!(isValidAlphanumericCharacter(aChar))) - { - return false; - } - break; - } - case ALPHANUMERIC_PLUS: - { - if (!(isValidAlphanumericPlusCharacter(aChar))) - { - return false; - } - break; - } - case ALL: - { - if (!(isValidAllCharacter(aChar))) - { - return false; - } - break; - } - default: - { - return false; - } - } - - return true; - } - - private boolean isValidAlphabeticCharacter(char aChar) - { - if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z'))) - { - return false; - } - return true; - } - - private boolean isValidAlphanumericCharacter(char aChar) - { - if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z')) && ((aChar < '0') || (aChar > '9'))) - { - return false; - } - return true; - } - - private boolean isValidAlphanumericPlusCharacter(char aChar) - { - if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z')) && ((aChar < '0') || (aChar > '9'))) - { - if ((aChar != ' ') && (aChar != '_') && (aChar != '-') && (aChar != ':') && (aChar != '\\')) - { - return false; - } - } - return true; - } - - private boolean isValidAllCharacter(char aChar) - { - if ((aChar < ' ') || (aChar > '~')) - { - return false; - } - return true; - } + * AlphaTextField is a "smart" text field that restricts the user's input. The + * input can be restricted to alphabetic, alphanumeric, or all characters. The + * maximum number of characters can also be limited. The defaults for this + * component is alphabetic only string of unlimited length. + * + * @author rob@straylight.princeton.com + * @author $Author: cgruber $ + * @version $Revision: 893 $ + */ +public class AlphaTextField extends SmartTextField { + + /******************************* + * CONSTANTS + *******************************/ + + /** + * Sets the input to alphabetic characters only. The characters "a-z" and "A-Z" + * are the only valid characters. All other characters will be ignored. + * + * @see #getAlphaType() + */ + public static final int ALPHABETIC = 0; + + /** + * Sets the input to alphanumeric characters only. The characters "a-z", "A-Z" + * and "0-9" are the only valid characters. All other characters will be + * ignored. + * + * @see #getAlphaType() + */ + public static final int ALPHANUMERIC = 1; + + /** + * Sets the input to alphanumeric characters and a few special characters only. + * The valid characters are "a-z", "A-Z", "0-9", space, "-", "_", "\", and ":". + * This is helpful for file names (with paths) as input strings. All other + * characters will be ignored. + * + * @see #getAlphaType() + */ + public static final int ALPHANUMERIC_PLUS = 2; + + /** + * Sets the input to all characters without restriction. + * + * @see #getAlphaType() + */ + public static final int ALL = 3; + + /******************************* + * DATA MEMBERS + *******************************/ + + // The level of input restrictions, defaults to ALPHABETIC + private int alphaType; + + // The maximum length of the input string, defaults to 0, no maximum + private int stringLength; + + /******************************* + * PUBLIC METHODS + *******************************/ + + /** + * The default constructor of this class. The default string of this text field + * is set to the empty string (""). The maximum length is set to 0, which + * specifies no limit. + */ + public AlphaTextField() { + this("", 0); + } + + /** + * Constructor of this class with the initial text of the text field specified. + * The maximum length is set to 0, which specifies no limit. + * + * @param text Initial text of the text field. + */ + public AlphaTextField(String text) { + this(text, 0); + } + + /** + * Constructor of this class with width (in columns) of the text field + * specified. The initial text is set to the empty string (""). The maximum + * length is set to 0, which specifies no limit. + * + * @param columns The width of the text field in characters. + */ + public AlphaTextField(int columns) { + this("", columns); + } + + /** + * Constructor of this class with width (in columns) and initial text of the + * text field specified. The maximum length is set to 0, which specifies no + * limit. + * + * @param text Initial text of the text field. + * @param columns The width of the text field in characters. + */ + public AlphaTextField(String text, int columns) { + super(text, columns); + } + + /** + * Constructor that allows the user to set the Alpha type of the text field and + * the maximum string length. + * + * @param anAlphaType The character restriction type. + * @param aLength The maximum number of characters allowed in the string. + */ + public AlphaTextField(int anAlphaType, int aLength) { + super("", 0); + setAlphaType(anAlphaType); + setStringLength(aLength); + } + + /** + * Gets the current restriction type of this text field. + * + * @see #ALPHABETIC + * @see #ALPHANUMERIC + * @see #ALPHANUMERIC_PLUS + * @see #ALL + * @return The current restriction type as defined by the constansts of this + * class. + */ + public int getAlphaType() { + return alphaType; + } + + /** + * Sets the restriction type of this text field. + * + * @see #ALPHABETIC + * @see #ALPHANUMERIC + * @see #ALPHANUMERIC_PLUS + * @see #ALL + * @param newAlphaType The restriction of this text field. + */ + public void setAlphaType(int newAlphaType) { + switch (newAlphaType) { + case ALPHABETIC: + case ALPHANUMERIC: + case ALPHANUMERIC_PLUS: + case ALL: { + alphaType = newAlphaType; + break; + } + default: { + alphaType = ALPHABETIC; + break; + } + } + } + + /** + * Sets the maximum string length of this text field. If the length is set to + * zero, then there is no limit. The default string length is zero. Negative + * sizes will set the length to zero. + * + * @param newStringLength The maximum length of the string that the user can + * input. + */ + public void setStringLength(int newStringLength) { + if (newStringLength < 0) { + stringLength = 0; + } else { + stringLength = newStringLength; + } + } + + /** + * Gets the current length of the maximum string size the user can enter. + * + * @return The maximum length the string of the text field can be. + */ + public int getStringLength() { + return stringLength; + } + + /******************************* + * PROTECTED METHODS + *******************************/ + + protected boolean isValidCharacter(char aChar) { + // if its a non-printable character, then its ok + if ((aChar < ' ') || (aChar > '~')) { + return true; + } + + // can only be a printable character now, check it for validation + return isValidCharacterType(aChar); + } + + protected boolean isValidString(String aString) { + if (aString.length() > stringLength) { + return false; + } + + for (int i = 0; i < aString.length(); ++i) { + if (!(isValidCharacterType(aString.charAt(i)))) { + return false; + } + } + + return true; + } + + protected void postProcessing() { + // No need to do anything. + } + + /******************************* + * PROTECTED METHODS + *******************************/ + + private boolean isValidCharacterType(char aChar) { + switch (alphaType) { + case ALPHABETIC: { + if (!(isValidAlphabeticCharacter(aChar))) { + return false; + } + break; + } + case ALPHANUMERIC: { + if (!(isValidAlphanumericCharacter(aChar))) { + return false; + } + break; + } + case ALPHANUMERIC_PLUS: { + if (!(isValidAlphanumericPlusCharacter(aChar))) { + return false; + } + break; + } + case ALL: { + if (!(isValidAllCharacter(aChar))) { + return false; + } + break; + } + default: { + return false; + } + } + + return true; + } + + private boolean isValidAlphabeticCharacter(char aChar) { + if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z'))) { + return false; + } + return true; + } + + private boolean isValidAlphanumericCharacter(char aChar) { + if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z')) && ((aChar < '0') || (aChar > '9'))) { + return false; + } + return true; + } + + private boolean isValidAlphanumericPlusCharacter(char aChar) { + if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z')) && ((aChar < '0') || (aChar > '9'))) { + if ((aChar != ' ') && (aChar != '_') && (aChar != '-') && (aChar != ':') && (aChar != '\\')) { + return false; + } + } + return true; + } + + private boolean isValidAllCharacter(char aChar) { + if ((aChar < ' ') || (aChar > '~')) { + return false; + } + return true; + } } diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlternatingRowCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlternatingRowCellRenderer.java index 46d2693..fb4824c 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlternatingRowCellRenderer.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlternatingRowCellRenderer.java @@ -28,102 +28,79 @@ import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; /** -* A TableCellRenderer that wraps another TableCellRenderer -* and sets the background to the specified color for odd-numbered rows. -* This makes every other row appear to be a different color, -* which helps users distinguish rows of data in densely-packed -* tables. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ + * A TableCellRenderer that wraps another TableCellRenderer and sets the + * background to the specified color for odd-numbered rows. This makes every + * other row appear to be a different color, which helps users distinguish rows + * of data in densely-packed tables. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ public class AlternatingRowCellRenderer implements TableCellRenderer { - protected TableCellRenderer wrappedRenderer; - protected Color alternateColor; - - /** - * Default constructor uses a lighter shade of the system control color - * and wraps a DefaultTableCellRenderer. - */ - public AlternatingRowCellRenderer() - { - this( new DefaultTableCellRenderer() ); - } - - /** - * Uses the specified color for the background of the alternating rows, - * and wraps a DefaultTableCellRenderer. - */ - public AlternatingRowCellRenderer( - Color aColor ) - { - this( aColor, new DefaultTableCellRenderer() ); - } + protected TableCellRenderer wrappedRenderer; + protected Color alternateColor; - /** - * Uses the uses a lighter shade of the system control color - * for the background of the alternating rows, - * and wraps the specified TableCellRenderer. - */ - public AlternatingRowCellRenderer( - TableCellRenderer aRenderer ) - { - Color c = UIManager.getColor( "control" ); - c = new Color( // lighten this color just slightly - (int) ( c.getRed() + ( ( 255 - c.getRed() ) / 1.5 ) ), - (int) ( c.getGreen() + ( ( 255 - c.getGreen() ) / 1.5 ) ), - (int) ( c.getBlue() + ( ( 255 - c.getBlue() ) / 1.5 ) ) ); + /** + * Default constructor uses a lighter shade of the system control color and + * wraps a DefaultTableCellRenderer. + */ + public AlternatingRowCellRenderer() { + this(new DefaultTableCellRenderer()); + } - alternateColor = c; - wrappedRenderer = aRenderer; - } + /** + * Uses the specified color for the background of the alternating rows, and + * wraps a DefaultTableCellRenderer. + */ + public AlternatingRowCellRenderer(Color aColor) { + this(aColor, new DefaultTableCellRenderer()); + } - /** - * Uses the specified color for the background of the alternating rows, - * and wraps the specified TableCellRenderer. - */ - public AlternatingRowCellRenderer( - Color aColor, TableCellRenderer aRenderer ) - { - alternateColor = aColor; - wrappedRenderer = aRenderer; - } + /** + * Uses the uses a lighter shade of the system control color for the background + * of the alternating rows, and wraps the specified TableCellRenderer. + */ + public AlternatingRowCellRenderer(TableCellRenderer aRenderer) { + Color c = UIManager.getColor("control"); + c = new Color( // lighten this color just slightly + (int) (c.getRed() + ((255 - c.getRed()) / 1.5)), (int) (c.getGreen() + ((255 - c.getGreen()) / 1.5)), + (int) (c.getBlue() + ((255 - c.getBlue()) / 1.5))); - public Component getTableCellRendererComponent( - JTable table, Object value, - boolean isSelected, boolean hasFocus, - int row, int column) - { - Component result = wrappedRenderer.getTableCellRendererComponent( - table, value, isSelected, hasFocus, row, column ); - if ( ! isSelected ) - { - if ( row % 2 == 0 ) - { - if ( ! result.getBackground().equals( table.getBackground() ) ) - { - result.setBackground( table.getBackground() ); - } - } - else - { - if ( ! result.getBackground().equals( alternateColor ) ) - { - // jdk1.3's default renderer is opaque - if ( result instanceof JComponent ) - { - ((JComponent)result).setOpaque( true ); - } - - result.setBackground( alternateColor ); - } - } - } - return result; - } -} + alternateColor = c; + wrappedRenderer = aRenderer; + } + /** + * Uses the specified color for the background of the alternating rows, and + * wraps the specified TableCellRenderer. + */ + public AlternatingRowCellRenderer(Color aColor, TableCellRenderer aRenderer) { + alternateColor = aColor; + wrappedRenderer = aRenderer; + } + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { + Component result = wrappedRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, + column); + if (!isSelected) { + if (row % 2 == 0) { + if (!result.getBackground().equals(table.getBackground())) { + result.setBackground(table.getBackground()); + } + } else { + if (!result.getBackground().equals(alternateColor)) { + // jdk1.3's default renderer is opaque + if (result instanceof JComponent) { + ((JComponent) result).setOpaque(true); + } + result.setBackground(alternateColor); + } + } + } + return result; + } +} diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterFlowLayout.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterFlowLayout.java index 1c438b6..632fb59 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterFlowLayout.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterFlowLayout.java @@ -25,491 +25,505 @@ import java.awt.FlowLayout; import java.awt.Insets; /** - * BetterFlowLayout works just like FlowLayout, except that - * you can specify a vertical orientation in addition to the - * usual horizontal orientations. You can also specify that - * all the components be sized to the same height and/or width. - * By default, the behavior is identical to FlowLayout. + * BetterFlowLayout works just like FlowLayout, except that you can specify a + * vertical orientation in addition to the usual horizontal orientations. You + * can also specify that all the components be sized to the same height and/or + * width. By default, the behavior is identical to FlowLayout. * * @author michael@mpowers.net * @author $Author: cgruber $ - * @version $Revision: 904 $ - * $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ + * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) + * $ */ public class BetterFlowLayout extends FlowLayout { - /** - * This value indicates vertical orientation and - * that each column of components should be top-justified. - */ - public static final int TOP = 32; - - /** - * This value indicates vertical orientation and - * that each column of components should be centered. - */ - public static final int CENTER_VERTICAL = 16; - - /** - * This value indicates vertical orientation and - * that each column of components should be bottom-justified. - */ - public static final int BOTTOM = 8; - - /** - * Tracks orientation. - */ - protected boolean isHorizontal = true; - - /** - * Tracks component sizing of width. - */ - protected boolean isWidthUniform = false; - /** - * Tracks component sizing of height. - */ - protected boolean isHeightUniform = false; - - /** - * Constructs a new Flow Layout with a centered alignment and a - * default 5-unit horizontal and vertical gap. - */ - public BetterFlowLayout() { - this(CENTER, 5, 5); - } - - /** - * Constructs a new Flow Layout with the specified alignment and a - * default 5-unit horizontal and vertical gap. - * The value of the alignment argument must be one of - * BetterFlowLayout.LEFT, BetterFlowLayout.RIGHT, - * or BetterFlowLayout.CENTER. - * @param align the alignment value - */ - public BetterFlowLayout(int align) { - this(align, 5, 5); - } - - /** - * Creates a new flow layout manager with the indicated alignment - * and the indicated horizontal and vertical gaps. - *

- * The value of the alignment argument must be one of - * BetterFlowLayout.LEFT, BetterFlowLayout.RIGHT, - * or BetterFlowLayout.CENTER. - * @param align the alignment value. - * @param hgap the horizontal gap between components. - * @param vgap the vertical gap between components. - */ - public BetterFlowLayout(int align, int hgap, int vgap) { - setHgap(hgap); - setVgap(vgap); - setAlignment(align); - } - - /** - * Sets whether all components should have the same height. - * @param isUniform the new value. - * @see #isHeightUniform - */ - public void setHeightUniform(boolean isUniform) { - isHeightUniform = isUniform; - } - - /** - * Sets whether all components should have the same width. - * @param isUniform the new value. - * @see #isWidthUniform - */ - public void setWidthUniform(boolean isUniform) { - isWidthUniform = isUniform; - } - - /** - * Determines whether all components will have the same height. - * The uniform height will be the maximum of the preferred heights - * of all the components in the container. - * This value defaults to false. - * @return whether components will have the same height. - */ - public boolean isHeightUniform() { - return isHeightUniform; - } - - /** - * Determines whether all components will have the same width. - * The uniform height will be the maximum of the preferred widths - * of all the components in the container. - * This value defaults to false. - * @return whether components will have the same width. - */ - public boolean isWidthUniform() { - return isWidthUniform; - } - - /** - * Sets the alignment for this layout. - * Possible values for horizontal orientation are LEFT, - * RIGHT, and CENTER. - * Possible values for vertical orientation are TOP, - * BOTTOM, and CENTER_VERTICAL. - * @param align the alignment value. - * @see java.awt.FlowLayout#getAlignment - */ - public void setAlignment(int align) { - if ( ( align == TOP ) || ( align == BOTTOM ) || ( align == CENTER_VERTICAL ) ) - { - isHorizontal = false; - } - else - { - isHorizontal = true; - } - - super.setAlignment( align ); - } - - /** - * Returns the preferred dimensions for this layout given the components - * in the specified target container. - * @param target the component which needs to be laid out - * @return the preferred dimensions to lay out the - * subcomponents of the specified container. - * @see Container - * @see #minimumLayoutSize - * @see java.awt.Container#getPreferredSize - */ - public Dimension preferredLayoutSize(Container target) { - if ( isHorizontal ) { - return preferredLayoutSizeHorizontal( target ); - } else { - return preferredLayoutSizeVertical( target ); - } - } - - /** - * Returns the preferred dimensions for this layout given the components - * in the specified target container. - * @param target the component which needs to be laid out - * @return the preferred dimensions to lay out the - * subcomponents of the specified container. - * @see Container - * @see #minimumLayoutSize - * @see java.awt.Container#getPreferredSize - */ - public Dimension preferredLayoutSizeHorizontal(Container target) { - synchronized (target.getTreeLock()) { - Dimension dim = new Dimension(0, 0); - int nmembers = target.getComponentCount(); - int maxWidth = 0; - - for (int i = 0 ; i < nmembers ; i++) { - Component m = target.getComponent(i); - if (m.isVisible()) { - Dimension d = m.getPreferredSize(); - dim.height = Math.max(dim.height, d.height); - maxWidth = Math.max(maxWidth, d.width); - if (i > 0) { - dim.width += getHgap(); - } - dim.width += d.width; - } - } - if ( isWidthUniform ) - dim.width = ( maxWidth + getHgap() ) * nmembers - getHgap(); - Insets insets = target.getInsets(); - dim.width += insets.left + insets.right + getHgap()*2; - dim.height += insets.top + insets.bottom + getVgap()*2; - return dim; - } - } - - /** - * Returns the preferred dimensions for this layout given the components - * in the specified target container. - * @param target the component which needs to be laid out - * @return the preferred dimensions to lay out the - * subcomponents of the specified container. - * @see Container - * @see #minimumLayoutSize - * @see java.awt.Container#getPreferredSize - */ - public Dimension preferredLayoutSizeVertical(Container target) { - synchronized (target.getTreeLock()) { - Dimension dim = new Dimension(0, 0); - int nmembers = target.getComponentCount(); - int maxHeight = 0; - - for (int i = 0 ; i < nmembers ; i++) { - Component m = target.getComponent(i); - if (m.isVisible()) { - Dimension d = m.getPreferredSize(); - dim.width = Math.max(dim.width, d.width); - maxHeight = Math.max(maxHeight, d.height); - if (i > 0) { - dim.height += getVgap(); - } - dim.height += d.height; - } - } - if ( isHeightUniform ) - dim.height = ( maxHeight + getVgap() ) * nmembers - getVgap(); - Insets insets = target.getInsets(); - dim.width += insets.left + insets.right + getHgap()*2; - dim.height += insets.top + insets.bottom + getVgap()*2; - return dim; - } - } - - /** - * Returns the minimum dimensions needed to layout the components - * contained in the specified target container. - * @param target the component which needs to be laid out - * @return the minimum dimensions to lay out the - * subcomponents of the specified container. - * @see #preferredLayoutSize - * @see java.awt.Container - * @see java.awt.Container#doLayout - */ - public Dimension minimumLayoutSize(Container target) { - // preferred size is also the minimum size - if ( isHorizontal ) { - return preferredLayoutSizeHorizontal( target ); - } else { - return preferredLayoutSizeVertical( target ); - } - } - - /** - * Lays out the container. This method lets each component take - * its preferred size by reshaping the components in the - * target container in order to satisfy the constraints of - * this BetterFlowLayout object. - * @param target the specified component being laid out. - * @see Container - * @see java.awt.Container#doLayout - */ - public void layoutContainer(Container target) { - if ( isHorizontal ) { - layoutContainerHorizontal( target ); - } else { - layoutContainerVertical( target ); - } - } - - /** - * Lays out the container. This method lets each component take - * its preferred size by reshaping the components in the - * target container in order to satisfy the constraints of - * this BetterFlowLayout object. - * @param target the specified component being laid out. - * @see Container - * @see java.awt.Container#doLayout - */ - protected void layoutContainerHorizontal(Container target) { - synchronized (target.getTreeLock()) { - Insets insets = target.getInsets(); - int maxwidth = target.getSize().width - (insets.left + insets.right + getHgap()*2); - int nmembers = target.getComponentCount(); - int x = 0, y = insets.top + getVgap(); - int rowh = 0, start = 0; - - boolean ltr = true; // target.getComponentOrientation().isLeftToRight(); - Dimension uniform = getUniformDimension( target ); - - for (int i = 0 ; i < nmembers ; i++) { - Component m = target.getComponent(i); - if (m.isVisible()) { - Dimension d = m.getPreferredSize(); - if ( isWidthUniform ) - d.width = uniform.width; - if ( isHeightUniform ) - d.height = uniform.height; - m.setSize(d.width, d.height); - - if ((x == 0) || ((x + d.width) <= maxwidth)) { - if (x > 0) { - x += getHgap(); - } - x += d.width; - rowh = Math.max(rowh, d.height); - } else { - moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, i, ltr); - x = d.width; - y += getVgap() + rowh; - rowh = d.height; - start = i; - } - } - } - moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, nmembers, ltr); - } - } - - /** - * Centers the elements in the specified row, if there is any slack. - * @param target the component which needs to be moved - * @param x the x coordinate - * @param y the y coordinate - * @param width the width dimensions - * @param height the height dimensions - * @param rowStart the beginning of the row - * @param rowEnd the the ending of the row - */ - private void moveComponentsHorizontal(Container target, int x, int y, int width, int height, - int rowStart, int rowEnd, boolean ltr) { - synchronized (target.getTreeLock()) { - switch (getAlignment()) { - case LEFT: - x += ltr ? 0 : width; - break; - case CENTER: - x += width / 2; - break; - case RIGHT: - x += ltr ? width : 0; - break; + /** + * This value indicates vertical orientation and that each column of components + * should be top-justified. + */ + public static final int TOP = 32; + + /** + * This value indicates vertical orientation and that each column of components + * should be centered. + */ + public static final int CENTER_VERTICAL = 16; + + /** + * This value indicates vertical orientation and that each column of components + * should be bottom-justified. + */ + public static final int BOTTOM = 8; + + /** + * Tracks orientation. + */ + protected boolean isHorizontal = true; + + /** + * Tracks component sizing of width. + */ + protected boolean isWidthUniform = false; + /** + * Tracks component sizing of height. + */ + protected boolean isHeightUniform = false; + + /** + * Constructs a new Flow Layout with a centered alignment and a default 5-unit + * horizontal and vertical gap. + */ + public BetterFlowLayout() { + this(CENTER, 5, 5); + } + + /** + * Constructs a new Flow Layout with the specified alignment and a default + * 5-unit horizontal and vertical gap. The value of the alignment argument must + * be one of BetterFlowLayout.LEFT, + * BetterFlowLayout.RIGHT, or BetterFlowLayout.CENTER. + * + * @param align the alignment value + */ + public BetterFlowLayout(int align) { + this(align, 5, 5); + } + + /** + * Creates a new flow layout manager with the indicated alignment and the + * indicated horizontal and vertical gaps. + *

+ * The value of the alignment argument must be one of + * BetterFlowLayout.LEFT, BetterFlowLayout.RIGHT, or + * BetterFlowLayout.CENTER. + * + * @param align the alignment value. + * @param hgap the horizontal gap between components. + * @param vgap the vertical gap between components. + */ + public BetterFlowLayout(int align, int hgap, int vgap) { + setHgap(hgap); + setVgap(vgap); + setAlignment(align); + } + + /** + * Sets whether all components should have the same height. + * + * @param isUniform the new value. + * @see #isHeightUniform + */ + public void setHeightUniform(boolean isUniform) { + isHeightUniform = isUniform; + } + + /** + * Sets whether all components should have the same width. + * + * @param isUniform the new value. + * @see #isWidthUniform + */ + public void setWidthUniform(boolean isUniform) { + isWidthUniform = isUniform; + } + + /** + * Determines whether all components will have the same height. The uniform + * height will be the maximum of the preferred heights of all the components in + * the container. This value defaults to false. + * + * @return whether components will have the same height. + */ + public boolean isHeightUniform() { + return isHeightUniform; + } + + /** + * Determines whether all components will have the same width. The uniform + * height will be the maximum of the preferred widths of all the components in + * the container. This value defaults to false. + * + * @return whether components will have the same width. + */ + public boolean isWidthUniform() { + return isWidthUniform; + } + + /** + * Sets the alignment for this layout. Possible values for horizontal + * orientation are LEFT, RIGHT, and + * CENTER. Possible values for vertical orientation are + * TOP, BOTTOM, and CENTER_VERTICAL. + * + * @param align the alignment value. + * @see java.awt.FlowLayout#getAlignment + */ + public void setAlignment(int align) { + if ((align == TOP) || (align == BOTTOM) || (align == CENTER_VERTICAL)) { + isHorizontal = false; + } else { + isHorizontal = true; + } + + super.setAlignment(align); + } + + /** + * Returns the preferred dimensions for this layout given the components in the + * specified target container. + * + * @param target the component which needs to be laid out + * @return the preferred dimensions to lay out the subcomponents of the + * specified container. + * @see Container + * @see #minimumLayoutSize + * @see java.awt.Container#getPreferredSize + */ + public Dimension preferredLayoutSize(Container target) { + if (isHorizontal) { + return preferredLayoutSizeHorizontal(target); + } else { + return preferredLayoutSizeVertical(target); + } + } + + /** + * Returns the preferred dimensions for this layout given the components in the + * specified target container. + * + * @param target the component which needs to be laid out + * @return the preferred dimensions to lay out the subcomponents of the + * specified container. + * @see Container + * @see #minimumLayoutSize + * @see java.awt.Container#getPreferredSize + */ + public Dimension preferredLayoutSizeHorizontal(Container target) { + synchronized (target.getTreeLock()) { + Dimension dim = new Dimension(0, 0); + int nmembers = target.getComponentCount(); + int maxWidth = 0; + + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + if (m.isVisible()) { + Dimension d = m.getPreferredSize(); + dim.height = Math.max(dim.height, d.height); + maxWidth = Math.max(maxWidth, d.width); + if (i > 0) { + dim.width += getHgap(); + } + dim.width += d.width; + } + } + if (isWidthUniform) + dim.width = (maxWidth + getHgap()) * nmembers - getHgap(); + Insets insets = target.getInsets(); + dim.width += insets.left + insets.right + getHgap() * 2; + dim.height += insets.top + insets.bottom + getVgap() * 2; + return dim; + } + } + + /** + * Returns the preferred dimensions for this layout given the components in the + * specified target container. + * + * @param target the component which needs to be laid out + * @return the preferred dimensions to lay out the subcomponents of the + * specified container. + * @see Container + * @see #minimumLayoutSize + * @see java.awt.Container#getPreferredSize + */ + public Dimension preferredLayoutSizeVertical(Container target) { + synchronized (target.getTreeLock()) { + Dimension dim = new Dimension(0, 0); + int nmembers = target.getComponentCount(); + int maxHeight = 0; + + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + if (m.isVisible()) { + Dimension d = m.getPreferredSize(); + dim.width = Math.max(dim.width, d.width); + maxHeight = Math.max(maxHeight, d.height); + if (i > 0) { + dim.height += getVgap(); + } + dim.height += d.height; + } + } + if (isHeightUniform) + dim.height = (maxHeight + getVgap()) * nmembers - getVgap(); + Insets insets = target.getInsets(); + dim.width += insets.left + insets.right + getHgap() * 2; + dim.height += insets.top + insets.bottom + getVgap() * 2; + return dim; + } + } + + /** + * Returns the minimum dimensions needed to layout the components contained in + * the specified target container. + * + * @param target the component which needs to be laid out + * @return the minimum dimensions to lay out the subcomponents of the specified + * container. + * @see #preferredLayoutSize + * @see java.awt.Container + * @see java.awt.Container#doLayout + */ + public Dimension minimumLayoutSize(Container target) { + // preferred size is also the minimum size + if (isHorizontal) { + return preferredLayoutSizeHorizontal(target); + } else { + return preferredLayoutSizeVertical(target); + } + } + + /** + * Lays out the container. This method lets each component take its preferred + * size by reshaping the components in the target container in order to satisfy + * the constraints of this BetterFlowLayout object. + * + * @param target the specified component being laid out. + * @see Container + * @see java.awt.Container#doLayout + */ + public void layoutContainer(Container target) { + if (isHorizontal) { + layoutContainerHorizontal(target); + } else { + layoutContainerVertical(target); + } + } + + /** + * Lays out the container. This method lets each component take its preferred + * size by reshaping the components in the target container in order to satisfy + * the constraints of this BetterFlowLayout object. + * + * @param target the specified component being laid out. + * @see Container + * @see java.awt.Container#doLayout + */ + protected void layoutContainerHorizontal(Container target) { + synchronized (target.getTreeLock()) { + Insets insets = target.getInsets(); + int maxwidth = target.getSize().width - (insets.left + insets.right + getHgap() * 2); + int nmembers = target.getComponentCount(); + int x = 0, y = insets.top + getVgap(); + int rowh = 0, start = 0; + + boolean ltr = true; // target.getComponentOrientation().isLeftToRight(); + Dimension uniform = getUniformDimension(target); + + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + if (m.isVisible()) { + Dimension d = m.getPreferredSize(); + if (isWidthUniform) + d.width = uniform.width; + if (isHeightUniform) + d.height = uniform.height; + m.setSize(d.width, d.height); + + if ((x == 0) || ((x + d.width) <= maxwidth)) { + if (x > 0) { + x += getHgap(); + } + x += d.width; + rowh = Math.max(rowh, d.height); + } else { + moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, i, ltr); + x = d.width; + y += getVgap() + rowh; + rowh = d.height; + start = i; + } + } + } + moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, nmembers, ltr); + } + } + + /** + * Centers the elements in the specified row, if there is any slack. + * + * @param target the component which needs to be moved + * @param x the x coordinate + * @param y the y coordinate + * @param width the width dimensions + * @param height the height dimensions + * @param rowStart the beginning of the row + * @param rowEnd the the ending of the row + */ + private void moveComponentsHorizontal(Container target, int x, int y, int width, int height, int rowStart, + int rowEnd, boolean ltr) { + synchronized (target.getTreeLock()) { + switch (getAlignment()) { + case LEFT: + x += ltr ? 0 : width; + break; + case CENTER: + x += width / 2; + break; + case RIGHT: + x += ltr ? width : 0; + break; //1.2 case LEADING: //1.2 break; //1.2 case TRAILING: //1.2 x += width; //1.2 break; + } + for (int i = rowStart; i < rowEnd; i++) { + Component m = target.getComponent(i); + if (m.isVisible()) { + if (ltr) { + m.setLocation(x, y + (height - m.getBounds().height) / 2); + } else { + m.setLocation(target.getBounds().width - x - m.getBounds().width, + y + (height - m.getBounds().height) / 2); + } + x += m.getBounds().width + getHgap(); + } + } + } } - for (int i = rowStart ; i < rowEnd ; i++) { - Component m = target.getComponent(i); - if (m.isVisible()) { - if (ltr) { - m.setLocation(x, y + (height - m.getBounds().height) / 2); - } else { - m.setLocation(target.getBounds().width - x - m.getBounds().width, y + (height - m.getBounds().height) / 2); - } - x += m.getBounds().width + getHgap(); - } + + /** + * Lays out the container. This method lets each component take its preferred + * size by reshaping the components in the target container in order to satisfy + * the constraints of this BetterFlowLayout object. + * + * @param target the specified component being laid out. + * @see Container + * @see java.awt.Container#doLayout + */ + protected void layoutContainerVertical(Container target) { + synchronized (target.getTreeLock()) { + + Insets insets = target.getInsets(); + int maxheight = target.getBounds().height - (insets.top + insets.bottom + getVgap() * 2); + int nmembers = target.getComponentCount(); + int y = 0, x = insets.left + getHgap(); + int colw = 0, start = 0; + + Dimension uniform = getUniformDimension(target); + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + if (m.isVisible()) { + Dimension d = m.getPreferredSize(); + if (isWidthUniform) + d.width = uniform.width; + if (isHeightUniform) + d.height = uniform.height; + m.setSize(d.width, d.height); + + if ((y == 0) || ((y + d.height) <= maxheight)) { + if (y > 0) { + y += getVgap(); + } + y += d.height; + colw = Math.max(colw, d.width); + } else { + moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, i); + y = d.height; + x += getHgap() + colw; + colw = d.width; + start = i; + } + } + } + moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, nmembers); + } } - } - } - - /** - * Lays out the container. This method lets each component take - * its preferred size by reshaping the components in the - * target container in order to satisfy the constraints of - * this BetterFlowLayout object. - * @param target the specified component being laid out. - * @see Container - * @see java.awt.Container#doLayout - */ - protected void layoutContainerVertical(Container target) { - synchronized (target.getTreeLock()) { - - Insets insets = target.getInsets(); - int maxheight = target.getBounds().height - (insets.top + insets.bottom + getVgap()*2); - int nmembers = target.getComponentCount(); - int y = 0, x = insets.left + getHgap(); - int colw = 0, start = 0; - - Dimension uniform = getUniformDimension( target ); - for (int i = 0 ; i < nmembers ; i++) { - Component m = target.getComponent(i); - if (m.isVisible()) { - Dimension d = m.getPreferredSize(); - if ( isWidthUniform ) - d.width = uniform.width; - if ( isHeightUniform ) - d.height = uniform.height; - m.setSize(d.width, d.height); - - if ((y == 0) || ((y + d.height) <= maxheight)) { - if (y > 0) { - y += getVgap(); - } - y += d.height; - colw = Math.max(colw, d.width); - } else { - moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, i ); - y = d.height; - x += getHgap() + colw; - colw = d.width; - start = i; - } - } - } - moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, nmembers ); - } - } - - /** - * Centers the elements in the specified row, if there is any slack. - * @param target the component which needs to be moved - * @param x the x coordinate - * @param y the y coordinate - * @param width the width dimensions - * @param height the height dimensions - * @param colStart the beginning of the column - * @param colEnd the the ending of the column - */ - private void moveComponentsVertical(Container target, int x, int y, int width, int height, - int colStart, int colEnd) { - synchronized (target.getTreeLock()) { - switch (getAlignment()) { - case TOP: - y += 0; - break; - case CENTER_VERTICAL: - y += ( height / 2 ); // - preferredLayoutSize( target ).height ) / 2 ); - break; - case BOTTOM: - y += height; - break; - } - for (int i = colStart ; i < colEnd ; i++) { - Component m = target.getComponent(i); - if (m.isVisible()) { - m.setLocation(x + (width - m.getBounds().width) / 2, y ); + + /** + * Centers the elements in the specified row, if there is any slack. + * + * @param target the component which needs to be moved + * @param x the x coordinate + * @param y the y coordinate + * @param width the width dimensions + * @param height the height dimensions + * @param colStart the beginning of the column + * @param colEnd the the ending of the column + */ + private void moveComponentsVertical(Container target, int x, int y, int width, int height, int colStart, + int colEnd) { + synchronized (target.getTreeLock()) { + switch (getAlignment()) { + case TOP: + y += 0; + break; + case CENTER_VERTICAL: + y += (height / 2); // - preferredLayoutSize( target ).height ) / 2 ); + break; + case BOTTOM: + y += height; + break; + } + for (int i = colStart; i < colEnd; i++) { + Component m = target.getComponent(i); + if (m.isVisible()) { + m.setLocation(x + (width - m.getBounds().width) / 2, y); // m.setLocation(x, y ); // m.setSize( width, m.getBounds().height ); //! - y += m.getBounds().height + getVgap(); - } - } - } - } - - /** - * Returns a dimension representing the maximum preferred - * height and width of all the components in the container. - * @param target the container to scan. - * @return a dimension containing the maximum values. - */ - protected Dimension getUniformDimension(Container target) { - Component m = null; - Dimension preferred = null; - int maxWidth = 0, maxHeight = 0; - int nmembers = target.getComponentCount(); - for ( int i = 0; i < nmembers; i++ ) { - m = target.getComponent( i ); - if ( m.isVisible() ) { - preferred = m.getPreferredSize(); - maxWidth = Math.max( maxWidth, preferred.width ); - maxHeight = Math.max( maxHeight, preferred.height ); - } - } - return new Dimension( maxWidth, maxHeight ); - } - - /** - * Returns a string representation of this BetterFlowLayout - * object and its values. - * @return a string representation of this layout. - */ - public String toString() { - String str = ""; - switch (getAlignment()) { - case TOP: str = ",align=top"; break; - case CENTER_VERTICAL: str = ",align=vertical"; break; - case BOTTOM: str = ",align=bottom"; break; - default: return super.toString(); + y += m.getBounds().height + getVgap(); + } + } + } + } + + /** + * Returns a dimension representing the maximum preferred height and width of + * all the components in the container. + * + * @param target the container to scan. + * @return a dimension containing the maximum values. + */ + protected Dimension getUniformDimension(Container target) { + Component m = null; + Dimension preferred = null; + int maxWidth = 0, maxHeight = 0; + int nmembers = target.getComponentCount(); + for (int i = 0; i < nmembers; i++) { + m = target.getComponent(i); + if (m.isVisible()) { + preferred = m.getPreferredSize(); + maxWidth = Math.max(maxWidth, preferred.width); + maxHeight = Math.max(maxHeight, preferred.height); + } + } + return new Dimension(maxWidth, maxHeight); } - return getClass().getName() + "[hgap=" + getHgap() + ",vgap=" + getVgap() + str + "]"; - } + /** + * Returns a string representation of this BetterFlowLayout object + * and its values. + * + * @return a string representation of this layout. + */ + public String toString() { + String str = ""; + switch (getAlignment()) { + case TOP: + str = ",align=top"; + break; + case CENTER_VERTICAL: + str = ",align=vertical"; + break; + case BOTTOM: + str = ",align=bottom"; + break; + default: + return super.toString(); + } + return getClass().getName() + "[hgap=" + getHgap() + ",vgap=" + getVgap() + str + "]"; + } } diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterRootLayout.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterRootLayout.java index 6e23ca1..238dd14 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterRootLayout.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterRootLayout.java @@ -28,247 +28,209 @@ import java.awt.Rectangle; import javax.swing.JPanel; import javax.swing.JRootPane; -/** -* A custom layout for a JRootPane that handles the the layout of a -* JRootPane's layeredPane, glassPane, and menuBar, and in addition -* handles four decorative components arranged in a border layout. -* Add the decorative components to the JRootPane using the directional -* constants; CENTER is reserved for the content pane and menu bar. -* -* @author michael@mpowers.net -* @version $Revision: 904 $ -*/ -public class BetterRootLayout extends BorderLayout -{ +/** + * A custom layout for a JRootPane that handles the the layout of a JRootPane's + * layeredPane, glassPane, and menuBar, and in addition handles four decorative + * components arranged in a border layout. Add the decorative components to the + * JRootPane using the directional constants; CENTER is reserved for the content + * pane and menu bar. + * + * @author michael@mpowers.net + * @version $Revision: 904 $ + */ +public class BetterRootLayout extends BorderLayout { /** * Returns the amount of space the layout would like to have. * * @param the Container for which this layout manager is being used * @return a Dimension object containing the layout's preferred size * @throws ClassCastException if parent is not a JRootPane - */ - public Dimension preferredLayoutSize(Container parent) - { + */ + public Dimension preferredLayoutSize(Container parent) { JRootPane rootPane = (JRootPane) parent; - + JPanel proxyPanel = new JPanel(); - proxyPanel.setLayout( new BorderLayout() ); - + proxyPanel.setLayout(new BorderLayout()); + JPanel contentProxy = null; - if(rootPane.getContentPane() != null) { + if (rootPane.getContentPane() != null) { contentProxy = new JPanel(); - contentProxy.setMinimumSize( - rootPane.getContentPane().getMinimumSize() ); - contentProxy.setMaximumSize( - rootPane.getContentPane().getMaximumSize() ); - contentProxy.setPreferredSize( - rootPane.getContentPane().getPreferredSize() ); - proxyPanel.add( contentProxy, CENTER ); + contentProxy.setMinimumSize(rootPane.getContentPane().getMinimumSize()); + contentProxy.setMaximumSize(rootPane.getContentPane().getMaximumSize()); + contentProxy.setPreferredSize(rootPane.getContentPane().getPreferredSize()); + proxyPanel.add(contentProxy, CENTER); } JPanel menuProxy = null; - if(rootPane.getJMenuBar() != null) { + if (rootPane.getJMenuBar() != null) { menuProxy = new JPanel(); - menuProxy.setMinimumSize( - rootPane.getJMenuBar().getMinimumSize() ); - menuProxy.setMaximumSize( - rootPane.getJMenuBar().getMaximumSize() ); - menuProxy.setPreferredSize( - rootPane.getJMenuBar().getPreferredSize() ); - proxyPanel.add( menuProxy, NORTH ); + menuProxy.setMinimumSize(rootPane.getJMenuBar().getMinimumSize()); + menuProxy.setMaximumSize(rootPane.getJMenuBar().getMaximumSize()); + menuProxy.setPreferredSize(rootPane.getJMenuBar().getPreferredSize()); + proxyPanel.add(menuProxy, NORTH); } - - this.addLayoutComponent( proxyPanel, CENTER ); - - Dimension result = super.preferredLayoutSize( parent ); - this.removeLayoutComponent( proxyPanel ); - + this.addLayoutComponent(proxyPanel, CENTER); + + Dimension result = super.preferredLayoutSize(parent); + + this.removeLayoutComponent(proxyPanel); + proxyPanel.removeAll(); - + return result; } - + /** * Returns the minimum amount of space the layout needs. * * @param the Container for which this layout manager is being used * @return a Dimension object containing the layout's minimum size * @throws ClassCastException if parent is not a JRootPane - */ - public Dimension minimumLayoutSize(Container parent) - { + */ + public Dimension minimumLayoutSize(Container parent) { JRootPane rootPane = (JRootPane) parent; - + JPanel proxyPanel = new JPanel(); - proxyPanel.setLayout( new BorderLayout() ); - + proxyPanel.setLayout(new BorderLayout()); + JPanel contentProxy = null; - if(rootPane.getContentPane() != null) { + if (rootPane.getContentPane() != null) { contentProxy = new JPanel(); - contentProxy.setMinimumSize( - rootPane.getContentPane().getMinimumSize() ); - contentProxy.setMaximumSize( - rootPane.getContentPane().getMaximumSize() ); - contentProxy.setPreferredSize( - rootPane.getContentPane().getPreferredSize() ); - proxyPanel.add( contentProxy, CENTER ); + contentProxy.setMinimumSize(rootPane.getContentPane().getMinimumSize()); + contentProxy.setMaximumSize(rootPane.getContentPane().getMaximumSize()); + contentProxy.setPreferredSize(rootPane.getContentPane().getPreferredSize()); + proxyPanel.add(contentProxy, CENTER); } JPanel menuProxy = null; - if(rootPane.getJMenuBar() != null) { + if (rootPane.getJMenuBar() != null) { menuProxy = new JPanel(); - menuProxy.setMinimumSize( - rootPane.getJMenuBar().getMinimumSize() ); - menuProxy.setMaximumSize( - rootPane.getJMenuBar().getMaximumSize() ); - menuProxy.setPreferredSize( - rootPane.getJMenuBar().getPreferredSize() ); - proxyPanel.add( menuProxy, NORTH ); + menuProxy.setMinimumSize(rootPane.getJMenuBar().getMinimumSize()); + menuProxy.setMaximumSize(rootPane.getJMenuBar().getMaximumSize()); + menuProxy.setPreferredSize(rootPane.getJMenuBar().getPreferredSize()); + proxyPanel.add(menuProxy, NORTH); } - - this.addLayoutComponent( proxyPanel, CENTER ); - - Dimension result = super.minimumLayoutSize( parent ); - this.removeLayoutComponent( proxyPanel ); - + this.addLayoutComponent(proxyPanel, CENTER); + + Dimension result = super.minimumLayoutSize(parent); + + this.removeLayoutComponent(proxyPanel); + proxyPanel.removeAll(); - + return result; } - + /** * Returns the maximum amount of space the layout can use. * * @param the Container for which this layout manager is being used * @return a Dimension object containing the layout's maximum size * @throws ClassCastException if parent is not a JRootPane - */ - public Dimension maximumLayoutSize(Container target) - { + */ + public Dimension maximumLayoutSize(Container target) { JRootPane rootPane = (JRootPane) target; - + JPanel proxyPanel = new JPanel(); - proxyPanel.setLayout( new BorderLayout() ); - + proxyPanel.setLayout(new BorderLayout()); + JPanel contentProxy = null; - if(rootPane.getContentPane() != null) { + if (rootPane.getContentPane() != null) { contentProxy = new JPanel(); - contentProxy.setMinimumSize( - rootPane.getContentPane().getMinimumSize() ); - contentProxy.setMaximumSize( - rootPane.getContentPane().getMaximumSize() ); - contentProxy.setPreferredSize( - rootPane.getContentPane().getPreferredSize() ); - proxyPanel.add( contentProxy, CENTER ); + contentProxy.setMinimumSize(rootPane.getContentPane().getMinimumSize()); + contentProxy.setMaximumSize(rootPane.getContentPane().getMaximumSize()); + contentProxy.setPreferredSize(rootPane.getContentPane().getPreferredSize()); + proxyPanel.add(contentProxy, CENTER); } JPanel menuProxy = null; - if(rootPane.getJMenuBar() != null) { + if (rootPane.getJMenuBar() != null) { menuProxy = new JPanel(); - menuProxy.setMinimumSize( - rootPane.getJMenuBar().getMinimumSize() ); - menuProxy.setMaximumSize( - rootPane.getJMenuBar().getMaximumSize() ); - menuProxy.setPreferredSize( - rootPane.getJMenuBar().getPreferredSize() ); - proxyPanel.add( menuProxy, NORTH ); + menuProxy.setMinimumSize(rootPane.getJMenuBar().getMinimumSize()); + menuProxy.setMaximumSize(rootPane.getJMenuBar().getMaximumSize()); + menuProxy.setPreferredSize(rootPane.getJMenuBar().getPreferredSize()); + proxyPanel.add(menuProxy, NORTH); } - - this.addLayoutComponent( proxyPanel, CENTER ); - - Dimension result = super.maximumLayoutSize( target ); - this.removeLayoutComponent( proxyPanel ); - + this.addLayoutComponent(proxyPanel, CENTER); + + Dimension result = super.maximumLayoutSize(target); + + this.removeLayoutComponent(proxyPanel); + proxyPanel.removeAll(); - + return result; } - + /** * Instructs the layout manager to perform the layout for the specified * container. * * @param the Container for which this layout manager is being used * @throws ClassCastException if parent is not a JRootPane - */ - public void layoutContainer(Container parent) - { + */ + public void layoutContainer(Container parent) { JRootPane rootPane = (JRootPane) parent; - + Rectangle b = parent.getBounds(); Insets i = rootPane.getInsets(); int w = b.width - i.right - i.left; int h = b.height - i.top - i.bottom; - + // layout panes - if(rootPane.getLayeredPane() != null) { + if (rootPane.getLayeredPane() != null) { rootPane.getLayeredPane().setBounds(i.left, i.top, w, h); } - if(rootPane.getGlassPane() != null) { + if (rootPane.getGlassPane() != null) { rootPane.getGlassPane().setBounds(i.left, i.top, w, h); } - + // handle proxy panel - + JPanel proxyPanel = new JPanel(); - proxyPanel.setLayout( new BorderLayout() ); - - this.addLayoutComponent( proxyPanel, CENTER ); - - super.layoutContainer( parent ); + proxyPanel.setLayout(new BorderLayout()); + + this.addLayoutComponent(proxyPanel, CENTER); + + super.layoutContainer(parent); // use proxy sizes to set sizes of layeredPane's children Rectangle proxyRect = proxyPanel.getBounds(); - if(rootPane.getJMenuBar() != null) { + if (rootPane.getJMenuBar() != null) { Rectangle menuRect = proxyPanel.getBounds(); menuRect.height = rootPane.getJMenuBar().getPreferredSize().height; - rootPane.getJMenuBar().setBounds( menuRect ); + rootPane.getJMenuBar().setBounds(menuRect); proxyRect.y += menuRect.height; proxyRect.height -= menuRect.height; } - if(rootPane.getContentPane() != null) { - rootPane.getContentPane().setBounds( proxyRect ); + if (rootPane.getContentPane() != null) { + rootPane.getContentPane().setBounds(proxyRect); } - this.removeLayoutComponent( proxyPanel ); - + this.removeLayoutComponent(proxyPanel); + proxyPanel.removeAll(); } - + /** - * Passes NORTH, SOUTH, EAST, WEST and CENTER to super implementation, - * and ignores all others. - */ - public void addLayoutComponent(Component comp, Object constraints) - { - if ( NORTH.equals( constraints ) ) - { - super.addLayoutComponent( comp, constraints ); - } - else - if ( SOUTH.equals( constraints ) ) - { - super.addLayoutComponent( comp, constraints ); - } - else - if ( EAST.equals( constraints ) ) - { - super.addLayoutComponent( comp, constraints ); + * Passes NORTH, SOUTH, EAST, WEST and CENTER to super implementation, and + * ignores all others. + */ + public void addLayoutComponent(Component comp, Object constraints) { + if (NORTH.equals(constraints)) { + super.addLayoutComponent(comp, constraints); + } else if (SOUTH.equals(constraints)) { + super.addLayoutComponent(comp, constraints); + } else if (EAST.equals(constraints)) { + super.addLayoutComponent(comp, constraints); + } else if (WEST.equals(constraints)) { + super.addLayoutComponent(comp, constraints); + } else if (CENTER.equals(constraints)) { + super.addLayoutComponent(comp, constraints); } - else - if ( WEST.equals( constraints ) ) - { - super.addLayoutComponent( comp, constraints ); - } - else - if ( CENTER.equals( constraints ) ) - { - super.addLayoutComponent( comp, constraints ); - } - + // otherwise, ignore } } - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterTableUI.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterTableUI.java index deb0eb6..cb1d48a 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterTableUI.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterTableUI.java @@ -24,100 +24,84 @@ import javax.swing.event.MouseInputListener; import javax.swing.plaf.basic.BasicTableUI; /** -* BetterTableUI allows a JTable to be disabled by -* listening for MouseEvents and then forwarding them -* to the usual MouseInputHandler only if the table -* is enabled.

-* -* This class also works around a bug where an editable -* table's selection is changed when clicking in an edit -* cell while the control key is down. This typically -* happened while users were copying/pasting data from -* cell to cell.

-* -* To use, call JTable.setUI() on any -* JTable with a BetterTableUI as the parameter. -* -* @author michael@mpowers.net -* @version $Revision: 904 $ -*/ -public class BetterTableUI extends BasicTableUI implements MouseInputListener -{ -/** -* The listener to get all mouse events when the table is enabled. -*/ - protected MouseInputListener delegateHandler; - -/** -* Overridden to set self as mouse listener and create delegate. -*/ - protected MouseInputListener createMouseInputListener() - { - // normal handler is a protected inner class of parent - delegateHandler = new MouseInputHandler(); - - return this; - } - - // interface MouseInputListener - - public void mouseClicked(MouseEvent event) - { - if ( (table!=null) && (table.isEnabled()) ) - { - delegateHandler.mouseClicked(event); - } - } - - public void mouseDragged(MouseEvent event) - { - if ( (table!=null) && (table.isEnabled()) ) - { - delegateHandler.mouseDragged(event); - } - } - - public void mouseEntered(MouseEvent event) - { - if ( (table!=null) && (table.isEnabled()) ) - { - delegateHandler.mouseEntered(event); - } - } - - public void mouseExited(MouseEvent event) - { - if ( (table!=null) && (table.isEnabled()) ) - { - delegateHandler.mouseExited(event); - } - } - - public void mouseMoved(MouseEvent event) - { - if ( (table!=null) && (table.isEnabled()) ) - { - delegateHandler.mouseMoved(event); - } - } - - public void mousePressed(MouseEvent event) - { - if ( (table!=null) && (table.isEnabled()) ) - { - // workaround bug - control key removes an existing selection - if ( table.isEditing() && event.isControlDown() ) return; - - delegateHandler.mousePressed(event); - } - } - - public void mouseReleased(MouseEvent event) - { - if ( (table!=null) && (table.isEnabled()) ) - { - delegateHandler.mouseReleased(event); - } - } + * BetterTableUI allows a JTable to be disabled by listening for MouseEvents and + * then forwarding them to the usual MouseInputHandler only if the table is + * enabled.
+ *
+ * + * This class also works around a bug where an editable table's selection is + * changed when clicking in an edit cell while the control key is down. This + * typically happened while users were copying/pasting data from cell to cell. + *
+ *
+ * + * To use, call JTable.setUI() on any JTable with a BetterTableUI + * as the parameter. + * + * @author michael@mpowers.net + * @version $Revision: 904 $ + */ +public class BetterTableUI extends BasicTableUI implements MouseInputListener { + /** + * The listener to get all mouse events when the table is enabled. + */ + protected MouseInputListener delegateHandler; + + /** + * Overridden to set self as mouse listener and create delegate. + */ + protected MouseInputListener createMouseInputListener() { + // normal handler is a protected inner class of parent + delegateHandler = new MouseInputHandler(); + + return this; + } + + // interface MouseInputListener + + public void mouseClicked(MouseEvent event) { + if ((table != null) && (table.isEnabled())) { + delegateHandler.mouseClicked(event); + } + } + + public void mouseDragged(MouseEvent event) { + if ((table != null) && (table.isEnabled())) { + delegateHandler.mouseDragged(event); + } + } + + public void mouseEntered(MouseEvent event) { + if ((table != null) && (table.isEnabled())) { + delegateHandler.mouseEntered(event); + } + } + + public void mouseExited(MouseEvent event) { + if ((table != null) && (table.isEnabled())) { + delegateHandler.mouseExited(event); + } + } + + public void mouseMoved(MouseEvent event) { + if ((table != null) && (table.isEnabled())) { + delegateHandler.mouseMoved(event); + } + } + + public void mousePressed(MouseEvent event) { + if ((table != null) && (table.isEnabled())) { + // workaround bug - control key removes an existing selection + if (table.isEditing() && event.isControlDown()) + return; + + delegateHandler.mousePressed(event); + } + } + + public void mouseReleased(MouseEvent event) { + if ((table != null) && (table.isEnabled())) { + delegateHandler.mouseReleased(event); + } + } } - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ButtonPanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ButtonPanel.java index 769e866..7d662ae 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ButtonPanel.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ButtonPanel.java @@ -46,565 +46,527 @@ import javax.swing.JPanel; import javax.swing.UIManager; /** -* ButtonPanel handles display and event broadcasting of standard buttons like -* OK/Cancel/Save/etc. The constructor takes a list or array of strings, each -* representing a button to appear on the panel from left to right. -* Any button click will send an action event to all listeners with the action -* command containing the corresponding string. Note action events are simply -* forwarded from the buttons themselves, so the source of the event will be -* the button, not the button panel. The button panel is the source of the -* STATE_CHANGED events that notify about changes to the panel itself.

-* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ -public class ButtonPanel extends JPanel implements ActionListener, MouseMotionListener -{ - // TODO: Button text should be read from resources. -/** -* Specifies a "OK" button. -* This is also the action command sent by the OK button. -*/ - public static final String OK = "OK"; -/** -* Specifies a "Save" button. -* This is also the action command sent by the Save button. -*/ - public static final String SAVE = "Save"; -/** -* Specifies a "Refresh" button. -* This is also the action command sent by the Refresh button. -*/ - public static final String REFRESH = "Refresh"; -/** -* Specifies a "Clear All" button. -* This is also the action command sent by the Clear All button. -*/ - public static final String CLEAR_ALL = "Clear All"; -/** -* Specifies a "Refresh" button. -* This is also the action command sent by the Cancel button. -*/ - public static final String CANCEL = "Cancel"; -/** -* Specifies a "Yes" button. -* This is also the action command sent by the Yes button. -*/ - public static final String YES = "Yes"; -/** -* Specifies a "No" button. -* This is also the action command sent by the No button. -*/ - public static final String NO = "No"; -/** -* Specifies an "Add" button. -* This is also the action command sent by the Add button. -*/ - public static final String ADD = "Add"; -/** -* Specifies a "Remove" button. -* This is also the action command sent by the Remove button. -*/ - public static final String REMOVE = "Remove"; -/** -* This is the action command to all listeners when the button state is changed. -*/ - public static final String STATE_CHANGED = "STATE_CHANGED"; - -/** -* This is the container to which buttons are added. -*/ - protected Container buttonContainer = null; // useful for subclasses -/** -* This is the list of all buttons on the panel. -*/ - protected Vector buttonList = null; -/** -* The insets for this panel, so they can be modified. -*/ - protected Insets insets = new Insets( 5, 5, 5, 5 ); - -/** -* This is the layout manager - which must be a FlowLayout or subclass. -*/ - protected FlowLayout buttonPanelLayout = null; - - // for action multicasting - protected ActionListener actionListener = null; - - -/** -* Constructs a ButtonPanel. Three buttons are created -* so the panel is filled when used in a GUI-builder environment. -*/ - public ButtonPanel() - { - buttonList = new Vector(); - initLayout(); - - // default labels for bean layout - setLabels( new String[] { "One", "Two", "Three" } ); - } - -/** -* This method is responsible for the initial layout of the panel. -* Subclasses can implement different layouts, but this method -* is responsible for initializing buttonContainer and buttonPanelLayout -* and setting the container to use the layout. -*/ - protected void initLayout() - { - this.setInsets( super.getInsets() ); - buttonContainer = this; - buttonPanelLayout = new BetterFlowLayout( BetterFlowLayout.RIGHT ); - buttonContainer.setLayout(buttonPanelLayout); - ((BetterFlowLayout)buttonPanelLayout).setWidthUniform( true ); - - // setBackground( Color.blue ); // useful for debugging - } - -/** -* Constructs a ButtonPanel using specified buttons. -* @param buttonList An array containing the strings to be used in labeling the buttons. -*/ - public ButtonPanel( String[] buttonList ) - { - this(); - setLabels( buttonList ); - } - -/** -* Constructs a ButtonPane using specified actions. For each action, a button -* is created, that when pressed the corresponding action is activated. The -* "name" of the action is used as the title of the button. -* @param actionList An array of actions to be used to create buttons with. -*/ - public ButtonPanel( Action[] actionList ) - { - this(); - setLabels( actionList ); - } - -/** -* Creates the buttons to appear on the panel. Any existing buttons -* are replaced. The labels are used as names and action commands -* in addition to labels. -* @param labels An array of strings to be used in labeling the buttons. -* If null, all buttons will be removed. -*/ - public void setLabels( String[] labels ) - { - if ( labels == null ) - { - labels = new String[] {}; - } - - buttonContainer.removeAll(); - this.buttonList = new Vector( labels.length ); - - String item = null; - Component button; - for ( int i = 0; i < labels.length; i++ ) - { - item = labels[i]; - if ( item != null ) - { - button = createComponentWithLabel( item.toString() ); - this.buttonList.addElement( item ); - addComponentToPanel( button ); - button.setEnabled( this.isEnabled() ); -/* - if ( i == 0 ) - { - JRootPane root = SwingUtilities.getRootPane( button ); - if ( root != null ) - root.setDefaultButton( button ); - } -*/ - } - else - { - throw new IllegalArgumentException( "ButtonPanel.setButtons: nulls are not allowed." ); - } - } - - this.revalidate(); - this.repaint(); - broadcastEvent( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, STATE_CHANGED ) ); - } - -/** -* -*/ - public void setLabels( Action[] actions ) - { - if ( actions == null ) - { - actions = new Action[] {}; - } - - buttonContainer.removeAll(); - this.buttonList = new Vector( actions.length ); - - Action action = null; - Component button; - for ( int i = 0; i < actions.length; i++ ) - { - action = actions[i]; - if ( action != null ) - { - String name = ( String )action.getValue( Action.NAME ); - button = createComponentWithLabel( name ); - this.buttonList.addElement( name ); - addComponentToPanel( button ); - button.setEnabled( this.isEnabled() ? action.isEnabled() : false ); - - // Add the action to the "button" if it knows about action listeners. - try - { - Method addActionListenerMethod = - button.getClass().getMethod( "addActionListener", new Class[] { ActionListener.class } ); - addActionListenerMethod.invoke( button, new Object[] { action } ); - } - catch ( NoSuchMethodException e ) { /* Do Nothing */ } - catch ( IllegalAccessException e ) { e.printStackTrace(); /* TODO: Do Something? */ } - catch ( InvocationTargetException e ) { e.printStackTrace(); /* TODO: Do Something? */ } - - // Create a new listener for property change events and have - // the action broadcast to that listener. - PropertyChangeListener pcListener = new ActionChangeListener( button ); - action.addPropertyChangeListener( pcListener ); - } - else - { - throw new IllegalArgumentException( "ButtonPanel.setButtons: nulls are not allowed." ); - } - } - - this.revalidate(); - this.repaint(); - broadcastEvent( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, STATE_CHANGED ) ); - } - - -/** -* Gets the labels of the buttons that appear on the panel, ordered from left to right. -* @return A new list containing strings used in labeling the buttons. -*/ - public String[] getLabels() - { - String[] labels = new String[ buttonList.size() ]; - int i = 0; - for ( Enumeration it = buttonList.elements(); it.hasMoreElements(); ) - { - labels[i++] = it.nextElement().toString(); - } - return labels; - } - -/** -* Gets the first component having the specified name. -* @return A component with the specified name, or null if none match. -*/ - public Component getButton( String aLabel ) - { - if ( aLabel == null ) return null; - - Component c = null; - int count = buttonContainer.getComponentCount(); - for ( int i = 0; i < count; i++ ) - { - c = buttonContainer.getComponent( i ); - if ( aLabel.equals( c.getName() ) ) - { - return c; - } - } - return null; - } - -/** -* Creates a new component with the specified label. -* The label is also used for the component's name -* and action command, if any. -* (This implementation returns a JButton.) -* @param aLabel The label for the component that will be created. -* @return The newly created component. -*/ - protected Component createComponentWithLabel( String aLabel ) - { - String buttonLabel = aLabel; // TODO: get string from resource - JButton newButton = new JButton(); // might allow other types in future - newButton.setName( aLabel ); - newButton.setText( buttonLabel ); - newButton.setActionCommand( aLabel ); - newButton.addActionListener( this ); - return newButton; - } - -/** -* Adds a component to the right-most side of the layout. -* @param aComponent The component to be added to the layout. -*/ - protected void addComponentToPanel( Component aComponent ) - { - buttonContainer.add( aComponent ); - } - - -/** -* Changes the alignment of the buttons in the panel. Defaults to right-justified. -* @param alignment A valid alignment code, per BetterFlowLayout implementation. -* @see BetterFlowLayout -*/ - public void setAlignment( int alignment ) - { - buttonPanelLayout.setAlignment(alignment); - buttonContainer.doLayout(); - } -/** -* Gets the alignment of the buttons in the panel. -* @return An alignment code, per FlowLayout implementation. -* @see FlowLayout -*/ - public int getAlignment() - { - return buttonPanelLayout.getAlignment(); - } - -/** -* Changes the horizontal spacing between components in the panel. -* @param newHgap the new spacing, in pixels. May not be negative. -*/ - public void setHgap( int newHgap ) - { - if ( newHgap < 0 ) return; // may not be negative - buttonPanelLayout.setHgap( newHgap ); - } - -/** -* Gets the current horizontal spacing between components. -* @return the current horizontal spacing, in pixels. -*/ - public int getHgap() - { - return buttonPanelLayout.getHgap(); - } - -/** -* Changes the vertical spacing between components in the panel. -* @param newVgap the new spacing, in pixels. May not be negative. -*/ - public void setVgap( int newVgap ) - { - if ( newVgap < 0 ) return; // may not be negative - buttonPanelLayout.setVgap( newVgap ); - } - -/** -* Gets the current vertical spacing between components. -* @return the current vertical spacing, in pixels. -*/ - public int getVgap() - { - return buttonPanelLayout.getVgap(); - } - -/** -* Changes the insets for this panel. -* @param newInsets the new insets. -*/ - public void setInsets( Insets newInsets ) - { - insets = newInsets; - } - -/** -* Overridden to return the user-specified insets for this panel. -* @return the current insets for this panel. -*/ - public Insets getInsets() - { - return insets; - } - -/** -* Overridden to call setEnabled on all components on panel. -* @param isEnabled whether to enable the panel and all components on it. -*/ - public void setEnabled( boolean isEnabled ) - { - super.setEnabled( isEnabled ); - int count = buttonContainer.getComponentCount(); - for ( int i = 0; i < count; i++ ) - { - buttonContainer.getComponent( i ).setEnabled( isEnabled ); - } - } - - // Action Multicast methods - -/** -* Adds an action listener to the list that will be -* notified by button events and changes in button state. -* @param l An action listener to be notified. -*/ - public void addActionListener(ActionListener l) - { - actionListener = AWTEventMulticaster.add(actionListener, l); - } -/** -* Removes an action listener from the list that will be -* notified by button events and changes in button state. -* @param l An action listener to be removed. -*/ - public void removeActionListener(ActionListener l) - { - actionListener = AWTEventMulticaster.remove(actionListener, l); - } -/** -* Notifies all registered action listeners of a pending Action Event. -* @param e An action event to be broadcast. -*/ - protected void broadcastEvent(ActionEvent e) - { - if (actionListener != null) - { - actionListener.actionPerformed(e); - } - } - - // interface ActionListener - -/** -* Called by buttons on panel and by other components that -* might be set to broadcast events to this listener. -* @param e An action event to be received. -*/ - public void actionPerformed(ActionEvent e) - { - broadcastEvent(e); - } - -/** -* A property change listener that listens specifically for property changes -* from action objects. This is the class that ties in the action to the -* button. This class is added to an action as a property change listener. -* The corresponding component is referenced by this class toe easily handle -* updates to the component caused by changes to the action. -*/ - public class ActionChangeListener implements PropertyChangeListener - { - /** The UI component that is affected by the action's changes. */ - Component theComponent; - - /** - * Constructs an ActionChangeListener with the given component being - * the recipient of the action's changes. - * @param The component to bind with the action. - */ - public ActionChangeListener( Component aComponent ) - { - super(); - theComponent = aComponent; - } - - /** - * Called whenever a property changes on the action object. - * @pram e The property change event generated by the action. - */ - public void propertyChange( PropertyChangeEvent e ) - { - String propertyName = e.getPropertyName(); - if ( propertyName.equals( Action.NAME ) ) - { - String name = ( String )e.getNewValue(); - if ( theComponent instanceof AbstractButton ) - { - AbstractButton button = ( AbstractButton )theComponent; - String oldName = button.getName(); - button.setText( name ); - button.setName( name ); - button.setActionCommand( name ); - - // Replace the old name of the component with the new name - // in the ButtonPanel's list of components. - buttonList.setElementAt( name, buttonList.indexOf( oldName ) ); - } - - // TODO: If component is not a button (or doesn't define the getText() - // then what should be done. - } - else if ( propertyName.equals( "enabled" ) ) - { - Boolean enabled = ( Boolean )e.getNewValue(); - theComponent.setEnabled( ButtonPanel.this.isEnabled() ? enabled.booleanValue() : false ); - } - - // TODO: Icon? - } - } - - - // for testing - - public static void main( String[] argv ) - { - try - { - UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() ); - } - catch (Exception exc) - { - - } - - JFrame dialog = new JFrame(); - BorderLayout bl = new BorderLayout( 20, 20 ); - - ButtonPanel panel = new ButtonPanel(); + * ButtonPanel handles display and event broadcasting of standard buttons like + * OK/Cancel/Save/etc. The constructor takes a list or array of strings, each + * representing a button to appear on the panel from left to right. Any button + * click will send an action event to all listeners with the action command + * containing the corresponding string. Note action events are simply forwarded + * from the buttons themselves, so the source of the event will be the button, + * not the button panel. The button panel is the source of the STATE_CHANGED + * events that notify about changes to the panel itself.
+ *
+ * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ +public class ButtonPanel extends JPanel implements ActionListener, MouseMotionListener { + // TODO: Button text should be read from resources. + /** + * Specifies a "OK" button. This is also the action command sent by the OK + * button. + */ + public static final String OK = "OK"; + /** + * Specifies a "Save" button. This is also the action command sent by the Save + * button. + */ + public static final String SAVE = "Save"; + /** + * Specifies a "Refresh" button. This is also the action command sent by the + * Refresh button. + */ + public static final String REFRESH = "Refresh"; + /** + * Specifies a "Clear All" button. This is also the action command sent by the + * Clear All button. + */ + public static final String CLEAR_ALL = "Clear All"; + /** + * Specifies a "Refresh" button. This is also the action command sent by the + * Cancel button. + */ + public static final String CANCEL = "Cancel"; + /** + * Specifies a "Yes" button. This is also the action command sent by the Yes + * button. + */ + public static final String YES = "Yes"; + /** + * Specifies a "No" button. This is also the action command sent by the No + * button. + */ + public static final String NO = "No"; + /** + * Specifies an "Add" button. This is also the action command sent by the Add + * button. + */ + public static final String ADD = "Add"; + /** + * Specifies a "Remove" button. This is also the action command sent by the + * Remove button. + */ + public static final String REMOVE = "Remove"; + /** + * This is the action command to all listeners when the button state is changed. + */ + public static final String STATE_CHANGED = "STATE_CHANGED"; + + /** + * This is the container to which buttons are added. + */ + protected Container buttonContainer = null; // useful for subclasses + /** + * This is the list of all buttons on the panel. + */ + protected Vector buttonList = null; + /** + * The insets for this panel, so they can be modified. + */ + protected Insets insets = new Insets(5, 5, 5, 5); + + /** + * This is the layout manager - which must be a FlowLayout or subclass. + */ + protected FlowLayout buttonPanelLayout = null; + + // for action multicasting + protected ActionListener actionListener = null; + + /** + * Constructs a ButtonPanel. Three buttons are created so the panel is filled + * when used in a GUI-builder environment. + */ + public ButtonPanel() { + buttonList = new Vector(); + initLayout(); + + // default labels for bean layout + setLabels(new String[] { "One", "Two", "Three" }); + } + + /** + * This method is responsible for the initial layout of the panel. Subclasses + * can implement different layouts, but this method is responsible for + * initializing buttonContainer and buttonPanelLayout and setting the container + * to use the layout. + */ + protected void initLayout() { + this.setInsets(super.getInsets()); + buttonContainer = this; + buttonPanelLayout = new BetterFlowLayout(BetterFlowLayout.RIGHT); + buttonContainer.setLayout(buttonPanelLayout); + ((BetterFlowLayout) buttonPanelLayout).setWidthUniform(true); + + // setBackground( Color.blue ); // useful for debugging + } + + /** + * Constructs a ButtonPanel using specified buttons. + * + * @param buttonList An array containing the strings to be used in labeling the + * buttons. + */ + public ButtonPanel(String[] buttonList) { + this(); + setLabels(buttonList); + } + + /** + * Constructs a ButtonPane using specified actions. For each action, a button is + * created, that when pressed the corresponding action is activated. The "name" + * of the action is used as the title of the button. + * + * @param actionList An array of actions to be used to create buttons with. + */ + public ButtonPanel(Action[] actionList) { + this(); + setLabels(actionList); + } + + /** + * Creates the buttons to appear on the panel. Any existing buttons are + * replaced. The labels are used as names and action commands in addition to + * labels. + * + * @param labels An array of strings to be used in labeling the buttons. If + * null, all buttons will be removed. + */ + public void setLabels(String[] labels) { + if (labels == null) { + labels = new String[] {}; + } + + buttonContainer.removeAll(); + this.buttonList = new Vector(labels.length); + + String item = null; + Component button; + for (int i = 0; i < labels.length; i++) { + item = labels[i]; + if (item != null) { + button = createComponentWithLabel(item.toString()); + this.buttonList.addElement(item); + addComponentToPanel(button); + button.setEnabled(this.isEnabled()); + /* + * if ( i == 0 ) { JRootPane root = SwingUtilities.getRootPane( button ); if ( + * root != null ) root.setDefaultButton( button ); } + */ + } else { + throw new IllegalArgumentException("ButtonPanel.setButtons: nulls are not allowed."); + } + } + + this.revalidate(); + this.repaint(); + broadcastEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, STATE_CHANGED)); + } + + /** + * + */ + public void setLabels(Action[] actions) { + if (actions == null) { + actions = new Action[] {}; + } + + buttonContainer.removeAll(); + this.buttonList = new Vector(actions.length); + + Action action = null; + Component button; + for (int i = 0; i < actions.length; i++) { + action = actions[i]; + if (action != null) { + String name = (String) action.getValue(Action.NAME); + button = createComponentWithLabel(name); + this.buttonList.addElement(name); + addComponentToPanel(button); + button.setEnabled(this.isEnabled() ? action.isEnabled() : false); + + // Add the action to the "button" if it knows about action listeners. + try { + Method addActionListenerMethod = button.getClass().getMethod("addActionListener", + new Class[] { ActionListener.class }); + addActionListenerMethod.invoke(button, new Object[] { action }); + } catch (NoSuchMethodException e) { + /* Do Nothing */ } catch (IllegalAccessException e) { + e.printStackTrace(); + /* TODO: Do Something? */ } catch (InvocationTargetException e) { + e.printStackTrace(); + /* TODO: Do Something? */ } + + // Create a new listener for property change events and have + // the action broadcast to that listener. + PropertyChangeListener pcListener = new ActionChangeListener(button); + action.addPropertyChangeListener(pcListener); + } else { + throw new IllegalArgumentException("ButtonPanel.setButtons: nulls are not allowed."); + } + } + + this.revalidate(); + this.repaint(); + broadcastEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, STATE_CHANGED)); + } + + /** + * Gets the labels of the buttons that appear on the panel, ordered from left to + * right. + * + * @return A new list containing strings used in labeling the buttons. + */ + public String[] getLabels() { + String[] labels = new String[buttonList.size()]; + int i = 0; + for (Enumeration it = buttonList.elements(); it.hasMoreElements();) { + labels[i++] = it.nextElement().toString(); + } + return labels; + } + + /** + * Gets the first component having the specified name. + * + * @return A component with the specified name, or null if none match. + */ + public Component getButton(String aLabel) { + if (aLabel == null) + return null; + + Component c = null; + int count = buttonContainer.getComponentCount(); + for (int i = 0; i < count; i++) { + c = buttonContainer.getComponent(i); + if (aLabel.equals(c.getName())) { + return c; + } + } + return null; + } + + /** + * Creates a new component with the specified label. The label is also used for + * the component's name and action command, if any. (This implementation returns + * a JButton.) + * + * @param aLabel The label for the component that will be created. + * @return The newly created component. + */ + protected Component createComponentWithLabel(String aLabel) { + String buttonLabel = aLabel; // TODO: get string from resource + JButton newButton = new JButton(); // might allow other types in future + newButton.setName(aLabel); + newButton.setText(buttonLabel); + newButton.setActionCommand(aLabel); + newButton.addActionListener(this); + return newButton; + } + + /** + * Adds a component to the right-most side of the layout. + * + * @param aComponent The component to be added to the layout. + */ + protected void addComponentToPanel(Component aComponent) { + buttonContainer.add(aComponent); + } + + /** + * Changes the alignment of the buttons in the panel. Defaults to + * right-justified. + * + * @param alignment A valid alignment code, per BetterFlowLayout implementation. + * @see BetterFlowLayout + */ + public void setAlignment(int alignment) { + buttonPanelLayout.setAlignment(alignment); + buttonContainer.doLayout(); + } + + /** + * Gets the alignment of the buttons in the panel. + * + * @return An alignment code, per FlowLayout implementation. + * @see FlowLayout + */ + public int getAlignment() { + return buttonPanelLayout.getAlignment(); + } + + /** + * Changes the horizontal spacing between components in the panel. + * + * @param newHgap the new spacing, in pixels. May not be negative. + */ + public void setHgap(int newHgap) { + if (newHgap < 0) + return; // may not be negative + buttonPanelLayout.setHgap(newHgap); + } + + /** + * Gets the current horizontal spacing between components. + * + * @return the current horizontal spacing, in pixels. + */ + public int getHgap() { + return buttonPanelLayout.getHgap(); + } + + /** + * Changes the vertical spacing between components in the panel. + * + * @param newVgap the new spacing, in pixels. May not be negative. + */ + public void setVgap(int newVgap) { + if (newVgap < 0) + return; // may not be negative + buttonPanelLayout.setVgap(newVgap); + } + + /** + * Gets the current vertical spacing between components. + * + * @return the current vertical spacing, in pixels. + */ + public int getVgap() { + return buttonPanelLayout.getVgap(); + } + + /** + * Changes the insets for this panel. + * + * @param newInsets the new insets. + */ + public void setInsets(Insets newInsets) { + insets = newInsets; + } + + /** + * Overridden to return the user-specified insets for this panel. + * + * @return the current insets for this panel. + */ + public Insets getInsets() { + return insets; + } + + /** + * Overridden to call setEnabled on all components on panel. + * + * @param isEnabled whether to enable the panel and all components on it. + */ + public void setEnabled(boolean isEnabled) { + super.setEnabled(isEnabled); + int count = buttonContainer.getComponentCount(); + for (int i = 0; i < count; i++) { + buttonContainer.getComponent(i).setEnabled(isEnabled); + } + } + + // Action Multicast methods + + /** + * Adds an action listener to the list that will be notified by button events + * and changes in button state. + * + * @param l An action listener to be notified. + */ + public void addActionListener(ActionListener l) { + actionListener = AWTEventMulticaster.add(actionListener, l); + } + + /** + * Removes an action listener from the list that will be notified by button + * events and changes in button state. + * + * @param l An action listener to be removed. + */ + public void removeActionListener(ActionListener l) { + actionListener = AWTEventMulticaster.remove(actionListener, l); + } + + /** + * Notifies all registered action listeners of a pending Action Event. + * + * @param e An action event to be broadcast. + */ + protected void broadcastEvent(ActionEvent e) { + if (actionListener != null) { + actionListener.actionPerformed(e); + } + } + + // interface ActionListener + + /** + * Called by buttons on panel and by other components that might be set to + * broadcast events to this listener. + * + * @param e An action event to be received. + */ + public void actionPerformed(ActionEvent e) { + broadcastEvent(e); + } + + /** + * A property change listener that listens specifically for property changes + * from action objects. This is the class that ties in the action to the button. + * This class is added to an action as a property change listener. The + * corresponding component is referenced by this class toe easily handle updates + * to the component caused by changes to the action. + */ + public class ActionChangeListener implements PropertyChangeListener { + /** The UI component that is affected by the action's changes. */ + Component theComponent; + + /** + * Constructs an ActionChangeListener with the given component being the + * recipient of the action's changes. + * + * @param The component to bind with the action. + */ + public ActionChangeListener(Component aComponent) { + super(); + theComponent = aComponent; + } + + /** + * Called whenever a property changes on the action object. + * + * @pram e The property change event generated by the action. + */ + public void propertyChange(PropertyChangeEvent e) { + String propertyName = e.getPropertyName(); + if (propertyName.equals(Action.NAME)) { + String name = (String) e.getNewValue(); + if (theComponent instanceof AbstractButton) { + AbstractButton button = (AbstractButton) theComponent; + String oldName = button.getName(); + button.setText(name); + button.setName(name); + button.setActionCommand(name); + + // Replace the old name of the component with the new name + // in the ButtonPanel's list of components. + buttonList.setElementAt(name, buttonList.indexOf(oldName)); + } + + // TODO: If component is not a button (or doesn't define the getText() + // then what should be done. + } else if (propertyName.equals("enabled")) { + Boolean enabled = (Boolean) e.getNewValue(); + theComponent.setEnabled(ButtonPanel.this.isEnabled() ? enabled.booleanValue() : false); + } + + // TODO: Icon? + } + } + + // for testing + + public static void main(String[] argv) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception exc) { + + } + + JFrame dialog = new JFrame(); + BorderLayout bl = new BorderLayout(20, 20); + + ButtonPanel panel = new ButtonPanel(); // ButtonPanel panel = new ButtonPanel( new String[] { "OkayOkay", "CancelCancel" } ); - dialog.getContentPane().setLayout( bl ); - dialog.getContentPane().add( panel, BorderLayout.CENTER ); - dialog.setLocation( 50, 50 ); - // dialog.setSize( 450, 150 ); - - panel.setAlignment( BetterFlowLayout.CENTER_VERTICAL ); - panel.getButton( "One" ).setEnabled( false ); - - dialog.pack(); - dialog.setVisible( true ); - - try - { - BeanInfo info = Introspector.getBeanInfo( ButtonPanel.class ); - PropertyDescriptor[] props = info.getPropertyDescriptors(); - for ( int i = 0; i < props.length; i++ ) - { - System.out.println( props[i].getName() ); - } - } - catch (Exception exc) - { - System.out.println( exc ); - } + dialog.getContentPane().setLayout(bl); + dialog.getContentPane().add(panel, BorderLayout.CENTER); + dialog.setLocation(50, 50); + // dialog.setSize( 450, 150 ); + panel.setAlignment(BetterFlowLayout.CENTER_VERTICAL); + panel.getButton("One").setEnabled(false); + dialog.pack(); + dialog.setVisible(true); + try { + BeanInfo info = Introspector.getBeanInfo(ButtonPanel.class); + PropertyDescriptor[] props = info.getPropertyDescriptors(); + for (int i = 0; i < props.length; i++) { + System.out.println(props[i].getName()); + } + } catch (Exception exc) { + System.out.println(exc); + } - } - - public void mouseDragged(MouseEvent e) - { - } - - public void mouseMoved(MouseEvent e) - { - } + } + public void mouseDragged(MouseEvent e) { + } + public void mouseMoved(MouseEvent e) { + } } - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/CheckButtonPanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/CheckButtonPanel.java index 5e847ae..8116678 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/CheckButtonPanel.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/CheckButtonPanel.java @@ -27,246 +27,223 @@ import javax.swing.JCheckBox; import javax.swing.border.EmptyBorder; /** -* CheckButtonPanel is a simple extension of ButtonPanel. -* Differences are that it uses JCheckBoxes and the -* default alignment is vertical. The panel defaults to having -* no buttons selected. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ -public class CheckButtonPanel extends ButtonPanel -{ -/** -* Constructs a CheckButtonPanel. Three buttons are created -* so the panel is filled when used in a GUI-builder environment. -*/ - public CheckButtonPanel() - { - super(); - } - -/** -* Constructs a ButtonPanel using specified buttons. -* @param buttonList An array containing the strings to be used in labeling the buttons. -*/ - public CheckButtonPanel( String[] buttonList ) - { - super( buttonList ); - } - -/** -* Overridden to set vertical-center alignment and zero vgap. -*/ - protected void initLayout() - { - super.initLayout(); - buttonPanelLayout.setAlignment( BetterFlowLayout.CENTER_VERTICAL ); - buttonPanelLayout.setVgap( 0 ); - } - -/** -* Overridden to return a JRadioButton. -* @param aLabel The label for the component that will be created. -* @return The newly created component. -*/ - protected Component createComponentWithLabel( String aLabel ) - { - String buttonLabel = aLabel; - JCheckBox newButton = new JCheckBox(); - newButton.setName( aLabel ); - newButton.setText( buttonLabel ); - newButton.setActionCommand( aLabel ); - newButton.addActionListener( this ); - - // reduce insets per java l&f guidelines (was 4 on each side) - newButton.setBorder( new EmptyBorder( 1, 4, 1, 4 ) ); - - return newButton; - } - -/** -* Sets the value of the button whose name matches the given text value. -* @param aName A String matching the name of one of the buttons. -* If null, empty, or not matching, nothing happens. -* @param aValue A value to set the button. -*/ - public void setValue(String aName, boolean aValue) - { - if ( aName != null ) - { - Component c = null; - int count = buttonContainer.getComponentCount(); - for ( int i = 0; i < count; i++ ) - { - c = buttonContainer.getComponent( i ); - if ( c instanceof AbstractButton ) - { - if ( c.getName().equals( aName ) ) - { - ((AbstractButton)c).setSelected( aValue ); - c.repaint(); - return; - } - } - } - } - // null, empty, or not matching - exit. - System.out.println( "CheckButtonPanel.setValue: not found: " + aName ); - } - -/** -* Sets the state of the specified buttons to the specified value. -* @param aLabelArray An Array of Strings listing the buttons to be set. -* @param aValue The value to which the specified buttons will be set. -*/ - public void setValues(String[] aLabelArray, boolean aValue) - { - if ( aLabelArray != null ) - { - for ( int i = 0; i < aLabelArray.length; i++ ) - { - setValue( aLabelArray[i], aValue ); - } - } - } - -/** -* Convenience method to set all checkboxes on the panel. -* @param aValue The value to which all checkboxes on the panel will be set. -*/ - public void setAllValues(boolean aValue) - { - setValues( getLabels(), aValue ); - } - -/** -* Convenience method to check all boxes on the panel. -*/ - public void checkAll() - { - setAllValues( true ); - } - -/** -* Convenience method to clear all boxes on the panel. -*/ - public void clearAll() - { - setAllValues( false ); - } - -/** -* A convenience method to set only those buttons on the entire -* panel that should be checked. Buttons not in the list are unchecked. -* @param aLabelArray An Array of Strings listing the buttons to be set. -*/ - public void setCheckedValues(String[] aLabelArray) - { - setAllValues( false ); - setValues( aLabelArray, true ); - } - -/** -* Gets the labels of all checkboxes that are checked. -* @return A List of Strings containing the labels of the boxes that are checked. -*/ - public List getCheckedValueList() - { - Vector v = new Vector(); - Component c = null; - int count = buttonContainer.getComponentCount(); - for ( int i = 0; i < count; i++ ) - { - c = buttonContainer.getComponent( i ); - if ( c instanceof AbstractButton ) - { - if ( ((AbstractButton)c).isSelected() ) - { - v.addElement(c.getName()); - } - } - } - return v; - } - -/** -* Gets the labels of all checkboxes that are checked. -* @return A String Array containing the labels of the boxes that are checked. -*/ - public String[] getCheckedValues() - { - List v = getCheckedValueList(); - String[] result = new String[ v.size() ]; - for ( int i = 0; i < v.size(); i++ ) - { - result[i] = (String) v.get(i); - } - return result; - } - -/** -* Gets the value of the specified button. -* @param aName A String matching the name of one of the buttons. -* @return True if the button is checked, False if it is not checked. -* NOTE: If the button is not found in the list, False is returned. -*/ - public boolean getValue( String aName ) - { - Component c = null; - int count = buttonContainer.getComponentCount(); - for ( int i = 0; i < count; i++ ) - { - c = buttonContainer.getComponent( i ); - if ( ( c instanceof AbstractButton ) && ( ((AbstractButton)c).isSelected() ) ) - { - if ( ((AbstractButton)c).getText().equals( aName ) ) - { - return ((AbstractButton)c).isSelected(); - } - } - } - return false; - } - - // for testing - - public static void main( String[] argv ) - { - try - { - javax.swing.UIManager.setLookAndFeel( javax.swing.UIManager.getSystemLookAndFeelClassName() ); - } - catch (Exception exc) - { - - } - - javax.swing.JFrame dialog = new javax.swing.JFrame(); - java.awt.BorderLayout bl = new java.awt.BorderLayout( 20, 20 ); - - CheckButtonPanel panel = new CheckButtonPanel( new String[] { "One", "Two", "Three" } ); - - dialog.getContentPane().setLayout( bl ); - dialog.getContentPane().add( panel, java.awt.BorderLayout.CENTER ); - dialog.setLocation( 50, 50 ); - // dialog.setSize( 450, 150 ); - - panel.setAlignment( BetterFlowLayout.CENTER_VERTICAL ); - panel.getButton( "One" ).setEnabled( false ); - panel.setValues( new String[] { "One" }, true ); - panel.setValue( "Three", true ); + * CheckButtonPanel is a simple extension of ButtonPanel. Differences are that + * it uses JCheckBoxes and the default alignment is vertical. The panel defaults + * to having no buttons selected. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ +public class CheckButtonPanel extends ButtonPanel { + /** + * Constructs a CheckButtonPanel. Three buttons are created so the panel is + * filled when used in a GUI-builder environment. + */ + public CheckButtonPanel() { + super(); + } + + /** + * Constructs a ButtonPanel using specified buttons. + * + * @param buttonList An array containing the strings to be used in labeling the + * buttons. + */ + public CheckButtonPanel(String[] buttonList) { + super(buttonList); + } + + /** + * Overridden to set vertical-center alignment and zero vgap. + */ + protected void initLayout() { + super.initLayout(); + buttonPanelLayout.setAlignment(BetterFlowLayout.CENTER_VERTICAL); + buttonPanelLayout.setVgap(0); + } + + /** + * Overridden to return a JRadioButton. + * + * @param aLabel The label for the component that will be created. + * @return The newly created component. + */ + protected Component createComponentWithLabel(String aLabel) { + String buttonLabel = aLabel; + JCheckBox newButton = new JCheckBox(); + newButton.setName(aLabel); + newButton.setText(buttonLabel); + newButton.setActionCommand(aLabel); + newButton.addActionListener(this); + + // reduce insets per java l&f guidelines (was 4 on each side) + newButton.setBorder(new EmptyBorder(1, 4, 1, 4)); + + return newButton; + } + + /** + * Sets the value of the button whose name matches the given text value. + * + * @param aName A String matching the name of one of the buttons. If null, + * empty, or not matching, nothing happens. + * @param aValue A value to set the button. + */ + public void setValue(String aName, boolean aValue) { + if (aName != null) { + Component c = null; + int count = buttonContainer.getComponentCount(); + for (int i = 0; i < count; i++) { + c = buttonContainer.getComponent(i); + if (c instanceof AbstractButton) { + if (c.getName().equals(aName)) { + ((AbstractButton) c).setSelected(aValue); + c.repaint(); + return; + } + } + } + } + // null, empty, or not matching - exit. + System.out.println("CheckButtonPanel.setValue: not found: " + aName); + } + + /** + * Sets the state of the specified buttons to the specified value. + * + * @param aLabelArray An Array of Strings listing the buttons to be set. + * @param aValue The value to which the specified buttons will be set. + */ + public void setValues(String[] aLabelArray, boolean aValue) { + if (aLabelArray != null) { + for (int i = 0; i < aLabelArray.length; i++) { + setValue(aLabelArray[i], aValue); + } + } + } + + /** + * Convenience method to set all checkboxes on the panel. + * + * @param aValue The value to which all checkboxes on the panel will be set. + */ + public void setAllValues(boolean aValue) { + setValues(getLabels(), aValue); + } + + /** + * Convenience method to check all boxes on the panel. + */ + public void checkAll() { + setAllValues(true); + } + + /** + * Convenience method to clear all boxes on the panel. + */ + public void clearAll() { + setAllValues(false); + } + + /** + * A convenience method to set only those buttons on the entire panel that + * should be checked. Buttons not in the list are unchecked. + * + * @param aLabelArray An Array of Strings listing the buttons to be set. + */ + public void setCheckedValues(String[] aLabelArray) { + setAllValues(false); + setValues(aLabelArray, true); + } + + /** + * Gets the labels of all checkboxes that are checked. + * + * @return A List of Strings containing the labels of the boxes that are + * checked. + */ + public List getCheckedValueList() { + Vector v = new Vector(); + Component c = null; + int count = buttonContainer.getComponentCount(); + for (int i = 0; i < count; i++) { + c = buttonContainer.getComponent(i); + if (c instanceof AbstractButton) { + if (((AbstractButton) c).isSelected()) { + v.addElement(c.getName()); + } + } + } + return v; + } + + /** + * Gets the labels of all checkboxes that are checked. + * + * @return A String Array containing the labels of the boxes that are checked. + */ + public String[] getCheckedValues() { + List v = getCheckedValueList(); + String[] result = new String[v.size()]; + for (int i = 0; i < v.size(); i++) { + result[i] = (String) v.get(i); + } + return result; + } + + /** + * Gets the value of the specified button. + * + * @param aName A String matching the name of one of the buttons. + * @return True if the button is checked, False if it is not checked. NOTE: If + * the button is not found in the list, False is returned. + */ + public boolean getValue(String aName) { + Component c = null; + int count = buttonContainer.getComponentCount(); + for (int i = 0; i < count; i++) { + c = buttonContainer.getComponent(i); + if ((c instanceof AbstractButton) && (((AbstractButton) c).isSelected())) { + if (((AbstractButton) c).getText().equals(aName)) { + return ((AbstractButton) c).isSelected(); + } + } + } + return false; + } + + // for testing + + public static void main(String[] argv) { + try { + javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName()); + } catch (Exception exc) { + + } + + javax.swing.JFrame dialog = new javax.swing.JFrame(); + java.awt.BorderLayout bl = new java.awt.BorderLayout(20, 20); + + CheckButtonPanel panel = new CheckButtonPanel(new String[] { "One", "Two", "Three" }); + + dialog.getContentPane().setLayout(bl); + dialog.getContentPane().add(panel, java.awt.BorderLayout.CENTER); + dialog.setLocation(50, 50); + // dialog.setSize( 450, 150 ); + + panel.setAlignment(BetterFlowLayout.CENTER_VERTICAL); + panel.getButton("One").setEnabled(false); + panel.setValues(new String[] { "One" }, true); + panel.setValue("Three", true); // panel.setCheckedValues( new String[] { "Two" } ); - String[] values = panel.getCheckedValues(); - for ( int i = 0; i < values.length; i++ ) - { - System.out.println( values[i] ); - } + String[] values = panel.getCheckedValues(); + for (int i = 0; i < values.length; i++) { + System.out.println(values[i]); + } - dialog.pack(); - dialog.setVisible( true ); + dialog.pack(); + dialog.setVisible(true); - } + } } - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellEditor.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellEditor.java index a0a14ac..10feef7 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellEditor.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellEditor.java @@ -29,56 +29,42 @@ import javax.swing.JCheckBox; import javax.swing.JTable; /** -* A TableCellEditor that edits colors - it launches a color dialog when clicked. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ + * A TableCellEditor that edits colors - it launches a color dialog when + * clicked. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ class ColorCellEditor extends DefaultCellEditor { - Color currentColor = null; - - public ColorCellEditor(JButton b) - { - super(new JCheckBox()); // unfortunately, the constructor - // expects a check box, combo box, - // or text field. - editorComponent = b; - setClickCountToStart(1); // this is usually 1 or 2. - - // must do this so that editing stops when appropriate. - b.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - fireEditingStopped(); - } - } - ); - } - - protected void fireEditingStopped() - { - super.fireEditingStopped(); - } - - public Object getCellEditorValue() - { - return currentColor; - } - - public Component getTableCellEditorComponent(JTable table, - Object value, - boolean isSelected, - int row, - int column) - { - ((JButton)editorComponent).setText(value.toString()); - currentColor = (Color)value; - return editorComponent; - } + Color currentColor = null; + + public ColorCellEditor(JButton b) { + super(new JCheckBox()); // unfortunately, the constructor + // expects a check box, combo box, + // or text field. + editorComponent = b; + setClickCountToStart(1); // this is usually 1 or 2. + + // must do this so that editing stops when appropriate. + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + fireEditingStopped(); + } + }); + } + + protected void fireEditingStopped() { + super.fireEditingStopped(); + } + + public Object getCellEditorValue() { + return currentColor; + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + ((JButton) editorComponent).setText(value.toString()); + currentColor = (Color) value; + return editorComponent; + } } - - - - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellRenderer.java index 0552183..2fc97bb 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellRenderer.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellRenderer.java @@ -28,54 +28,39 @@ import javax.swing.border.Border; import javax.swing.table.TableCellRenderer; /** -* A TableCellRenderer that renders colors. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ + * A TableCellRenderer that renders colors. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ public class ColorCellRenderer extends JLabel implements TableCellRenderer { - Border unselectedBorder = null; - Border selectedBorder = null; - boolean isBordered = true; + Border unselectedBorder = null; + Border selectedBorder = null; + boolean isBordered = true; - public ColorCellRenderer(boolean isBordered) - { - super(); - this.isBordered = isBordered; - setOpaque(true); // must do this for background to show up. - } + public ColorCellRenderer(boolean isBordered) { + super(); + this.isBordered = isBordered; + setOpaque(true); // must do this for background to show up. + } - public Component getTableCellRendererComponent( - JTable table, Object color, - boolean isSelected, boolean hasFocus, - int row, int column) - { - setBackground((Color)color); - if (isBordered) - { - if (isSelected) - { - if (selectedBorder == null) - { - selectedBorder = BorderFactory.createMatteBorder(2,5,2,5, - table.getSelectionBackground()); - } - setBorder(selectedBorder); - } - else - { - if (unselectedBorder == null) - { - unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5, - table.getBackground()); - } - setBorder(unselectedBorder); - } - } - return this; - } + public Component getTableCellRendererComponent(JTable table, Object color, boolean isSelected, boolean hasFocus, + int row, int column) { + setBackground((Color) color); + if (isBordered) { + if (isSelected) { + if (selectedBorder == null) { + selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getSelectionBackground()); + } + setBorder(selectedBorder); + } else { + if (unselectedBorder == null) { + unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getBackground()); + } + setBorder(unselectedBorder); + } + } + return this; + } } - - - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ComboBoxCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ComboBoxCellRenderer.java index 2bf8dd6..67cb726 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ComboBoxCellRenderer.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ComboBoxCellRenderer.java @@ -26,32 +26,24 @@ import javax.swing.JTable; import javax.swing.table.TableCellRenderer; /** -* A TableCellRenderer that paints a JComboBox. Useful if -* you want to visibly display the JComboBox before the -* user clicks on the cell. -* -* @author bsafa@intersectsoft.com -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ + * A TableCellRenderer that paints a JComboBox. Useful if you want to visibly + * display the JComboBox before the user clicks on the cell. + * + * @author bsafa@intersectsoft.com + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ public class ComboBoxCellRenderer extends JComboBox implements TableCellRenderer { - public ComboBoxCellRenderer() - { - super(); - setOpaque(true); - } - - public Component getTableCellRendererComponent( - JTable table, Object value, - boolean isSelected, boolean hasFocus, - int row, int column) - { - setBackground(Color.white); - setSelectedItem(value); - return this; - } + public ComboBoxCellRenderer() { + super(); + setOpaque(true); + } + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { + setBackground(Color.white); + setSelectedItem(value); + return this; + } } - - - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/DateTextField.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/DateTextField.java index 18ed035..8a3b08c 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/DateTextField.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/DateTextField.java @@ -30,601 +30,507 @@ import java.util.Vector; import javax.swing.JOptionPane; import javax.swing.JTextField; - -/** -* DateTextField is a "smart" text field that restricts the user's input. The -* input is restructed to a string representing a date format. -* -* @author rob@straylight.princeton.com -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ -public class DateTextField extends JTextField -{ - -/******************************* -* CONSTANTS -*******************************/ - -/** -* Use the current date for this text field. -*/ - public static final int CURRENT_DATE = 0; - /** -* Use blanks for this text field. -*/ - public static final int BLANKS = 1; - -/** -* Use underscores for this text field. -*/ - public static final int UNDERSCORES = 2; - -/** -* Use just a 4-digit year for this text field. -*/ - public static final int YEAR = 3; - - private static final int BACKSPACE = 8; - private static final int DELETE = 127; - private static final int PASTE = 22; // Ctl-V - private static final int CUT = 24; // Ctl-X - - -/******************************* -* DATA MEMEBERS -*******************************/ - private int defaultType = CURRENT_DATE; - - private boolean warningMessageActive = false; - - -/******************************* -* PUBLIC METHODS -*******************************/ - -/** -* Default Constructor. -*/ - public DateTextField() - { - this(1, 1, 1999, 0); - - Calendar rightNow = Calendar.getInstance(); - - super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1, - rightNow.get(Calendar.DATE), - rightNow.get(Calendar.YEAR))); - } - -/** -* Constructor. -* @param month Number of the month, January being 1. -* @param data The day of the month. -* @param year The year. -*/ - public DateTextField(int month, int date, int year) - { - this(month, date, year, 0); - } - -/** -* Constructor. -* @param columns Width of the text field (in characters). -*/ - public DateTextField(int columns) - { - this(1, 1, 1998, columns); - - Calendar rightNow = Calendar.getInstance(); - - super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1, - rightNow.get(Calendar.DATE), - rightNow.get(Calendar.YEAR))); - } - -/** -* Constructor. -* @param month Number of the month, January being 1. -* @param data The day of the month. -* @param year The year. -* @param columns Width of the text field (in characters). -*/ - public DateTextField(int month, int date, int year, int columns) - { - super("", columns); - - super.setText(createDateString(month, date, year)); - - this.addFocusListener(new FocusAdapter() - { - public void focusLost(FocusEvent e) - { - if (!(e.isTemporary())) - { - validateDateString(e); - } - } - }); - } - -/** -* Sets the date type to display when the user has not entered any date yet. -* Default is the current date. -* @see #CURRENT_DATE -* @see #BLANKS -* @see #UNDERSCORES -* @param newDefaultType The type of date to display when there is no date data. -*/ - public void setDefaultType(int newDefaultType) - { - if (newDefaultType == BLANKS) - { - defaultType = BLANKS; - super.setText(" / / "); - } - else if (newDefaultType == UNDERSCORES) - { - defaultType = UNDERSCORES; - super.setText("__/__/____"); - } - else if (newDefaultType == YEAR) - { - defaultType = YEAR; - super.setText("0000"); - } - else - { - defaultType = CURRENT_DATE; - - Calendar rightNow = Calendar.getInstance(); - - super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1, - rightNow.get(Calendar.DATE), - rightNow.get(Calendar.YEAR))); - } - } - -/** -* Returns the type of date to display when there is no user input. -* @see #CURRENT_DATE -* @see #BLANKS -* @see #UNDERSCORES -* @return The type of date to display when there is no date to display. -*/ - public int getDefaultType() - { - return defaultType; - } - -/** -* Sets the text field to the string representation of the specified date. -* @param aDate The date to set the text field to. -*/ - public void setDate(Date aDate) - { - Calendar aCalendar = Calendar.getInstance(); - - aCalendar.setTime(aDate); - - super.setText(createDateString(aCalendar.get(Calendar.MONTH) + 1, - aCalendar.get(Calendar.DATE), - aCalendar.get(Calendar.YEAR))); - } - -/** -* Sets the text field directly from a Date object. -* @param aDate The date to set the text field to. -*/ - public void setText( Date aDate ) - { - setDate( aDate ); - } - -/** -* Sets the text field to the date specified in the string. This is overridden -* from the parent class to insure a valid date is inputted. The format of the -* date expected is the type of date format this text field is currently set to. -* @param aString A string representing a date in this text field current format. -*/ - public void setText( String aString ) - { - Date testDate = null; - - if ( aString != null ) - { - ParsePosition position = new ParsePosition( 0 ); - - if ( defaultType == YEAR ) - { - SimpleDateFormat yearFormatter = new SimpleDateFormat( "yyyy" ); - testDate = yearFormatter.parse( aString, position ); - } - else - { - SimpleDateFormat fullDateFormatter = new SimpleDateFormat( "MM/dd/yyyy" ); - testDate = fullDateFormatter.parse( aString, position ); - } - } - - // The string is not a valid date, use default value for date then. - if ( testDate == null ) - { - Calendar aCalendar = Calendar.getInstance(); - - testDate = aCalendar.getTime(); - } - - setDate( testDate ); - } - -/** -* Returns the date as represented by the date string in the text field. -* @return The date in the text field. -*/ - public Date getDate() throws NumberFormatException - { - Calendar aCalendar = Calendar.getInstance(); - int year = 1980; - int month = 0; - int date = 1; - int[] tempArray = {1,3,5,7,8,10,12}; - Vector monthsWith31Days = new Vector(7); - - for (int i = 0; i < tempArray.length; ++i) - { - monthsWith31Days.addElement(new Integer(tempArray[i])); - } - - aCalendar.set(year, month, date, 12, 0, 0); - - try - { - String dateString = getText(); - NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + dateString)); - - if (defaultType == YEAR) - { - year = Integer.parseInt(dateString); - - aCalendar.set(year, 0, 1, 12, 0, 0); - - return aCalendar.getTime(); - } - - month = Integer.parseInt(dateString.substring(0, 2).trim()); - date = Integer.parseInt(dateString.substring(3, 5).trim()); - year = Integer.parseInt(dateString.substring(6).trim()); - - if ((month < 1) || (month > 12)) - { - throw nfException; - } - - if ((date < 1) || (date > 31)) - { - throw nfException; - } - - if ((date == 31) && (!(monthsWith31Days.contains(new Integer(month))))) - { - throw nfException; - } - - if ((date == 30) && (month == 2)) - { - throw nfException; - } - - if ((date == 29) && (month == 2)) - { - if ((year % 100) == 0) - { - if ((year % 400) != 0) - { - throw nfException; - } - } - else - { - if ((year % 4) != 0) - { - throw nfException; - } - } - } - } - catch (IndexOutOfBoundsException ioobe) - { - NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + getText())); - throw nfException; - } - catch (NumberFormatException nfe) - { - NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + getText())); - throw nfException; - } - - aCalendar.set(year, (month - 1), date, 12, 0, 0); - - return aCalendar.getTime(); - } - - public void processKeyEvent(KeyEvent e) - { - String currentString = ""; - String testString = ""; - char newChar = e.getKeyChar(); - int currentLength = 0; - int currentCaretPosition = 0; - int selectionStart = 0; - int selectionEnd = 0; - int modifierPosition = 0; - int modifierDirection = 1; - char modifierCharacter; - boolean backspace = false; - boolean delete = false; - boolean paste = false; - boolean cut = false; - boolean keyPressed = false; - - backspace = (newChar == BACKSPACE); - delete = (newChar == DELETE); - paste = (newChar == PASTE); - cut = (newChar == CUT); - - keyPressed = (e.paramString().startsWith("KEY_PRESSED")); - - if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste) || (cut))) // A "key-typed" event - { - if (isValidCharacter(newChar)) - { - if ((isPrintableCharacter(newChar)) || (backspace) || (delete)) - { - // Both the key "pressed" and key "released" events get passed - // in here for the delete and backspace key. Only processes - // these keys if the event is key "pressed". - if (((backspace) || (delete)) && (!(keyPressed))) - { - // Don't do anything, pass through to consumption. - } - else - { - // Analyze the current contents of the field - currentString = getText(); - currentLength = currentString.length(); - - char[] tempText = new char[currentLength]; - - currentCaretPosition = getCaretPosition(); - - selectionStart = getSelectionStart(); - selectionEnd = getSelectionEnd(); - - // if a range is selected, then get rid of it and place the caret - // at the begginning of the range and continue processing. - if (selectionStart != selectionEnd) - { - selectionEnd = selectionStart; - setSelectionEnd(selectionEnd); - - currentCaretPosition = selectionStart; - setCaretPosition(currentCaretPosition); - } - - if (currentCaretPosition <= currentLength) - { - // a number of delete or backspace was pressed, delete and - // backspace deletes a number and places a "space" there - - // if caret at start of string and the backspace pressed OR - // caret at end of string and delete or number pressed THEN - // don't do anything, otherwise process key stroke - if (((currentCaretPosition == 0) && (backspace)) || - ((currentCaretPosition == currentLength) && (!(backspace)))) - { - // Don't do any processing. - } - else - { - modifierPosition = currentCaretPosition; - if (backspace) - { - modifierDirection = -1; - modifierPosition += modifierDirection; - } - - // Overwrite the current position with the new character - // inputted or overwrite using a space or underscore if - // the backspace or delete key was pressed. - if (defaultType != YEAR) - { - modifierCharacter = - ((delete)||(backspace)) ? - ((defaultType == UNDERSCORES) ? '_' : ' ') : - newChar; - } - else - { - // We are dealing with a 4-digit year. Overwrite - // with new character or "0" if delete or backspace - // was pressed. - modifierCharacter = ((delete)||(backspace)) ? ('0') : newChar; - } - - if (currentString.charAt(modifierPosition) == '/') - { - modifierPosition += modifierDirection; - } - - for (int i = 0; i < currentLength; ++i) - { - if (i == modifierPosition) - { - tempText[i] = modifierCharacter; - } - else - { - tempText[i] = currentString.charAt(i); - } - } - - testString = new String(tempText); - if (isValidString(testString)) - { - super.setText(testString); - if (backspace) - { - setCaretPosition(modifierPosition); - } - else - { - setCaretPosition(modifierPosition + 1); - } - } - } - } - } - - e.consume(); - } - else if ((cut) || (paste)) - { - e.consume(); - } - // else its a non-printable character, let it pass through - } - else - { - e.consume(); - } - } - - super.processKeyEvent(e); - } - - private boolean isValidCharacter(char aChar) - { - if (((aChar >= '!') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~'))) - { - return false; - } - return true; - } - - private boolean isPrintableCharacter(char inputChar) - { - if ((inputChar >= ' ') && (inputChar <= '~')) - { - return true; - } - return false; - } - - private boolean isValidDate(int month, int date, int year) - { - if ((month < 1) || (month > 12)) - { - return false; - } - - if ((date < 1) || (date > 31)) - { - return false; - } - - if ((year < 0) || (year > 9999)) - { - return false; - } - - return true; - } - - private boolean isValidString(String aString) - { - return true; - } - - private String createDateString(int month, int date, int year) - { - String dateString = ""; - - if (isValidDate(month, date, year)) - { - if (defaultType != YEAR) - { - if (month < 10) - { - dateString = "0"; - } - - dateString += String.valueOf(month); - dateString += "/"; - - if (date < 10) - { - dateString += "0"; - } - - dateString += String.valueOf(date); - dateString += "/"; - } - - if (year < 1000) - { - dateString += "0"; - if (year < 100) - { - dateString += "0"; - if (year < 10) - { - dateString += "0"; - } - } - } - - dateString += String.valueOf(year); - } - else - { - if (defaultType == YEAR) - { - dateString = "1999"; - } - else - { - dateString = "01/01/1999"; - } - } - - return dateString; - } - - private void validateDateString(FocusEvent e) - { - if (!(warningMessageActive)) - { - try - { - getDate(); - } - catch (NumberFormatException nfe) - { - System.out.println("Invalid Date String!!!"); - warningMessageActive = true; - JOptionPane.showMessageDialog(this, "Invald Date: " + getText(), "Warning", JOptionPane.WARNING_MESSAGE); - warningMessageActive = false; - if (defaultType == YEAR) - { - super.setText("1999"); - } - else - { - super.setText("01/01/1999"); - } - } - } - } + * DateTextField is a "smart" text field that restricts the user's input. The + * input is restructed to a string representing a date format. + * + * @author rob@straylight.princeton.com + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ +public class DateTextField extends JTextField { + + /******************************* + * CONSTANTS + *******************************/ + + /** + * Use the current date for this text field. + */ + public static final int CURRENT_DATE = 0; + + /** + * Use blanks for this text field. + */ + public static final int BLANKS = 1; + + /** + * Use underscores for this text field. + */ + public static final int UNDERSCORES = 2; + + /** + * Use just a 4-digit year for this text field. + */ + public static final int YEAR = 3; + + private static final int BACKSPACE = 8; + private static final int DELETE = 127; + private static final int PASTE = 22; // Ctl-V + private static final int CUT = 24; // Ctl-X + + /******************************* + * DATA MEMEBERS + *******************************/ + private int defaultType = CURRENT_DATE; + + private boolean warningMessageActive = false; + + /******************************* + * PUBLIC METHODS + *******************************/ + + /** + * Default Constructor. + */ + public DateTextField() { + this(1, 1, 1999, 0); + + Calendar rightNow = Calendar.getInstance(); + + super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1, rightNow.get(Calendar.DATE), + rightNow.get(Calendar.YEAR))); + } + + /** + * Constructor. + * + * @param month Number of the month, January being 1. + * @param data The day of the month. + * @param year The year. + */ + public DateTextField(int month, int date, int year) { + this(month, date, year, 0); + } + + /** + * Constructor. + * + * @param columns Width of the text field (in characters). + */ + public DateTextField(int columns) { + this(1, 1, 1998, columns); + + Calendar rightNow = Calendar.getInstance(); + + super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1, rightNow.get(Calendar.DATE), + rightNow.get(Calendar.YEAR))); + } + + /** + * Constructor. + * + * @param month Number of the month, January being 1. + * @param data The day of the month. + * @param year The year. + * @param columns Width of the text field (in characters). + */ + public DateTextField(int month, int date, int year, int columns) { + super("", columns); + + super.setText(createDateString(month, date, year)); + + this.addFocusListener(new FocusAdapter() { + public void focusLost(FocusEvent e) { + if (!(e.isTemporary())) { + validateDateString(e); + } + } + }); + } + + /** + * Sets the date type to display when the user has not entered any date yet. + * Default is the current date. + * + * @see #CURRENT_DATE + * @see #BLANKS + * @see #UNDERSCORES + * @param newDefaultType The type of date to display when there is no date data. + */ + public void setDefaultType(int newDefaultType) { + if (newDefaultType == BLANKS) { + defaultType = BLANKS; + super.setText(" / / "); + } else if (newDefaultType == UNDERSCORES) { + defaultType = UNDERSCORES; + super.setText("__/__/____"); + } else if (newDefaultType == YEAR) { + defaultType = YEAR; + super.setText("0000"); + } else { + defaultType = CURRENT_DATE; + + Calendar rightNow = Calendar.getInstance(); + + super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1, rightNow.get(Calendar.DATE), + rightNow.get(Calendar.YEAR))); + } + } + + /** + * Returns the type of date to display when there is no user input. + * + * @see #CURRENT_DATE + * @see #BLANKS + * @see #UNDERSCORES + * @return The type of date to display when there is no date to display. + */ + public int getDefaultType() { + return defaultType; + } + + /** + * Sets the text field to the string representation of the specified date. + * + * @param aDate The date to set the text field to. + */ + public void setDate(Date aDate) { + Calendar aCalendar = Calendar.getInstance(); + + aCalendar.setTime(aDate); + + super.setText(createDateString(aCalendar.get(Calendar.MONTH) + 1, aCalendar.get(Calendar.DATE), + aCalendar.get(Calendar.YEAR))); + } + + /** + * Sets the text field directly from a Date object. + * + * @param aDate The date to set the text field to. + */ + public void setText(Date aDate) { + setDate(aDate); + } + + /** + * Sets the text field to the date specified in the string. This is overridden + * from the parent class to insure a valid date is inputted. The format of the + * date expected is the type of date format this text field is currently set to. + * + * @param aString A string representing a date in this text field current + * format. + */ + public void setText(String aString) { + Date testDate = null; + + if (aString != null) { + ParsePosition position = new ParsePosition(0); + + if (defaultType == YEAR) { + SimpleDateFormat yearFormatter = new SimpleDateFormat("yyyy"); + testDate = yearFormatter.parse(aString, position); + } else { + SimpleDateFormat fullDateFormatter = new SimpleDateFormat("MM/dd/yyyy"); + testDate = fullDateFormatter.parse(aString, position); + } + } + + // The string is not a valid date, use default value for date then. + if (testDate == null) { + Calendar aCalendar = Calendar.getInstance(); + + testDate = aCalendar.getTime(); + } + + setDate(testDate); + } + + /** + * Returns the date as represented by the date string in the text field. + * + * @return The date in the text field. + */ + public Date getDate() throws NumberFormatException { + Calendar aCalendar = Calendar.getInstance(); + int year = 1980; + int month = 0; + int date = 1; + int[] tempArray = { 1, 3, 5, 7, 8, 10, 12 }; + Vector monthsWith31Days = new Vector(7); + + for (int i = 0; i < tempArray.length; ++i) { + monthsWith31Days.addElement(new Integer(tempArray[i])); + } + + aCalendar.set(year, month, date, 12, 0, 0); + + try { + String dateString = getText(); + NumberFormatException nfException = new NumberFormatException( + new String("Invalid Date String: " + dateString)); + + if (defaultType == YEAR) { + year = Integer.parseInt(dateString); + + aCalendar.set(year, 0, 1, 12, 0, 0); + + return aCalendar.getTime(); + } + + month = Integer.parseInt(dateString.substring(0, 2).trim()); + date = Integer.parseInt(dateString.substring(3, 5).trim()); + year = Integer.parseInt(dateString.substring(6).trim()); + + if ((month < 1) || (month > 12)) { + throw nfException; + } + + if ((date < 1) || (date > 31)) { + throw nfException; + } + + if ((date == 31) && (!(monthsWith31Days.contains(new Integer(month))))) { + throw nfException; + } + + if ((date == 30) && (month == 2)) { + throw nfException; + } + + if ((date == 29) && (month == 2)) { + if ((year % 100) == 0) { + if ((year % 400) != 0) { + throw nfException; + } + } else { + if ((year % 4) != 0) { + throw nfException; + } + } + } + } catch (IndexOutOfBoundsException ioobe) { + NumberFormatException nfException = new NumberFormatException( + new String("Invalid Date String: " + getText())); + throw nfException; + } catch (NumberFormatException nfe) { + NumberFormatException nfException = new NumberFormatException( + new String("Invalid Date String: " + getText())); + throw nfException; + } + + aCalendar.set(year, (month - 1), date, 12, 0, 0); + + return aCalendar.getTime(); + } + + public void processKeyEvent(KeyEvent e) { + String currentString = ""; + String testString = ""; + char newChar = e.getKeyChar(); + int currentLength = 0; + int currentCaretPosition = 0; + int selectionStart = 0; + int selectionEnd = 0; + int modifierPosition = 0; + int modifierDirection = 1; + char modifierCharacter; + boolean backspace = false; + boolean delete = false; + boolean paste = false; + boolean cut = false; + boolean keyPressed = false; + + backspace = (newChar == BACKSPACE); + delete = (newChar == DELETE); + paste = (newChar == PASTE); + cut = (newChar == CUT); + + keyPressed = (e.paramString().startsWith("KEY_PRESSED")); + + if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste) || (cut))) // A "key-typed" + // event + { + if (isValidCharacter(newChar)) { + if ((isPrintableCharacter(newChar)) || (backspace) || (delete)) { + // Both the key "pressed" and key "released" events get passed + // in here for the delete and backspace key. Only processes + // these keys if the event is key "pressed". + if (((backspace) || (delete)) && (!(keyPressed))) { + // Don't do anything, pass through to consumption. + } else { + // Analyze the current contents of the field + currentString = getText(); + currentLength = currentString.length(); + + char[] tempText = new char[currentLength]; + + currentCaretPosition = getCaretPosition(); + + selectionStart = getSelectionStart(); + selectionEnd = getSelectionEnd(); + + // if a range is selected, then get rid of it and place the caret + // at the begginning of the range and continue processing. + if (selectionStart != selectionEnd) { + selectionEnd = selectionStart; + setSelectionEnd(selectionEnd); + + currentCaretPosition = selectionStart; + setCaretPosition(currentCaretPosition); + } + + if (currentCaretPosition <= currentLength) { + // a number of delete or backspace was pressed, delete and + // backspace deletes a number and places a "space" there + + // if caret at start of string and the backspace pressed OR + // caret at end of string and delete or number pressed THEN + // don't do anything, otherwise process key stroke + if (((currentCaretPosition == 0) && (backspace)) + || ((currentCaretPosition == currentLength) && (!(backspace)))) { + // Don't do any processing. + } else { + modifierPosition = currentCaretPosition; + if (backspace) { + modifierDirection = -1; + modifierPosition += modifierDirection; + } + + // Overwrite the current position with the new character + // inputted or overwrite using a space or underscore if + // the backspace or delete key was pressed. + if (defaultType != YEAR) { + modifierCharacter = ((delete) || (backspace)) + ? ((defaultType == UNDERSCORES) ? '_' : ' ') + : newChar; + } else { + // We are dealing with a 4-digit year. Overwrite + // with new character or "0" if delete or backspace + // was pressed. + modifierCharacter = ((delete) || (backspace)) ? ('0') : newChar; + } + + if (currentString.charAt(modifierPosition) == '/') { + modifierPosition += modifierDirection; + } + + for (int i = 0; i < currentLength; ++i) { + if (i == modifierPosition) { + tempText[i] = modifierCharacter; + } else { + tempText[i] = currentString.charAt(i); + } + } + + testString = new String(tempText); + if (isValidString(testString)) { + super.setText(testString); + if (backspace) { + setCaretPosition(modifierPosition); + } else { + setCaretPosition(modifierPosition + 1); + } + } + } + } + } + + e.consume(); + } else if ((cut) || (paste)) { + e.consume(); + } + // else its a non-printable character, let it pass through + } else { + e.consume(); + } + } + + super.processKeyEvent(e); + } + + private boolean isValidCharacter(char aChar) { + if (((aChar >= '!') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~'))) { + return false; + } + return true; + } + + private boolean isPrintableCharacter(char inputChar) { + if ((inputChar >= ' ') && (inputChar <= '~')) { + return true; + } + return false; + } + + private boolean isValidDate(int month, int date, int year) { + if ((month < 1) || (month > 12)) { + return false; + } + + if ((date < 1) || (date > 31)) { + return false; + } + + if ((year < 0) || (year > 9999)) { + return false; + } + + return true; + } + + private boolean isValidString(String aString) { + return true; + } + + private String createDateString(int month, int date, int year) { + String dateString = ""; + + if (isValidDate(month, date, year)) { + if (defaultType != YEAR) { + if (month < 10) { + dateString = "0"; + } + + dateString += String.valueOf(month); + dateString += "/"; + + if (date < 10) { + dateString += "0"; + } + + dateString += String.valueOf(date); + dateString += "/"; + } + + if (year < 1000) { + dateString += "0"; + if (year < 100) { + dateString += "0"; + if (year < 10) { + dateString += "0"; + } + } + } + + dateString += String.valueOf(year); + } else { + if (defaultType == YEAR) { + dateString = "1999"; + } else { + dateString = "01/01/1999"; + } + } + + return dateString; + } + + private void validateDateString(FocusEvent e) { + if (!(warningMessageActive)) { + try { + getDate(); + } catch (NumberFormatException nfe) { + System.out.println("Invalid Date String!!!"); + warningMessageActive = true; + JOptionPane.showMessageDialog(this, "Invald Date: " + getText(), "Warning", + JOptionPane.WARNING_MESSAGE); + warningMessageActive = false; + if (defaultType == YEAR) { + super.setText("1999"); + } else { + super.setText("01/01/1999"); + } + } + } + } } diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/FormattedCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/FormattedCellRenderer.java index b3e2a76..10b1b89 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/FormattedCellRenderer.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/FormattedCellRenderer.java @@ -27,101 +27,92 @@ import javax.swing.JTable; import javax.swing.table.DefaultTableCellRenderer; /** -* A cell renderer for dealing with formatted content. -* Subclasses can specify formats or colors or styles for specific values -* or locations in the table by overridding getFormatForContext(), -* getForegroundForContext() and/or getBackgroundForContext(). -* -* @author michael@mpowers.net -* @version $Revision: 904 $ -* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ -*/ -public class FormattedCellRenderer extends DefaultTableCellRenderer -{ - protected Format currentFormat, defaultFormat; - protected Color defaultForeground, defaultBackground; - protected Font defaultFont; - -/** -* Default constructor with no specified format. -*/ - public FormattedCellRenderer() - { - this( (Format) null ); - } - -/** -* Constructor specifying a format for renderered content. -*/ - public FormattedCellRenderer( Format aFormat ) - { - currentFormat = null; - defaultFormat = aFormat; - defaultForeground = super.getForeground(); - defaultBackground = super.getForeground(); - } - -/** -* Returns the format currently in use to format cell content. -* @return The Format that is currently being used. -*/ - public Format getFormat() - { - return defaultFormat; - } - -/** -* Sets the format to be used to format cell content. -*/ - public void setFormat( Format aFormat ) - { - defaultFormat = aFormat; - } - -/** -* Overrides to retain the default foreground color, -* much the same as the DefaultCellRenderer does. -* We have to do this because DefaultCellRenderer's -* ivars are private. -*/ - public void setForeground(Color c) { - super.setForeground(c); - defaultForeground = c; - } - -/** -* Overrides to retain the default background color, -* much the same as the DefaultCellRenderer does. -* We have to do this because DefaultCellRenderer's -* ivars are private. -*/ - public void setBackground(Color c) { - super.setBackground(c); - defaultBackground = c; - } - -/** -* Overrides to retain the default font, -* much the same as the DefaultCellRenderer does. -* We have to do this because DefaultCellRenderer's -* ivars are private. -*/ - public void setFont(Font f) { - super.setFont(f); - defaultFont = f; - } - -/** -* Overridden to format the value with the appropriate Format. If the -* value cannot be formatted with the Format, the superclass method is called. -* @param value An Object to be formatted. -*/ - protected void setValue(Object value) - { - if ( currentFormat != null ) - { - try - { + * A cell renderer for dealing with formatted content. Subclasses can specify + * formats or colors or styles for specific values or locations in the table by + * overridding getFormatForContext(), getForegroundForContext() and/or + * getBackgroundForContext(). + * + * @author michael@mpowers.net + * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) + * $ + */ +public class FormattedCellRenderer extends DefaultTableCellRenderer { + protected Format currentFormat, defaultFormat; + protected Color defaultForeground, defaultBackground; + protected Font defaultFont; + + /** + * Default constructor with no specified format. + */ + public FormattedCellRenderer() { + this((Format) null); + } + + /** + * Constructor specifying a format for renderered content. + */ + public FormattedCellRenderer(Format aFormat) { + currentFormat = null; + defaultFormat = aFormat; + defaultForeground = super.getForeground(); + defaultBackground = super.getForeground(); + } + + /** + * Returns the format currently in use to format cell content. + * + * @return The Format that is currently being used. + */ + public Format getFormat() { + return defaultFormat; + } + + /** + * Sets the format to be used to format cell content. + */ + public void setFormat(Format aFormat) { + defaultFormat = aFormat; + } + + /** + * Overrides to retain the default foreground color, much the same as the + * DefaultCellRenderer does. We have to do this because DefaultCellRenderer's + * ivars are private. + */ + public void setForeground(Color c) { + super.setForeground(c); + defaultForeground = c; + } + + /** + * Overrides to retain the default background color, much the same as the + * DefaultCellRenderer does. We have to do this because DefaultCellRenderer's + * ivars are private. + */ + public void setBackground(Color c) { + super.setBackground(c); + defaultBackground = c; + } + + /** + * Overrides to retain the default font, much the same as the + * DefaultCellRenderer does. We have to do this because DefaultCellRenderer's + * ivars are private. + */ + public void setFont(Font f) { + super.setFont(f); + defaultFont = f; + } + + /** + * Overridden to format the value with the appropriate Format. If the value + * cannot be formatted with the Format, the superclass method is called. + * + * @param value An Object to be formatted. + */ + protected void setValue(Object value) { + if (currentFormat != null) { + try { // if ( ( value instanceof Number ) && ( value.toString().indexOf( "E" ) != -1 ) ) // { @@ -132,153 +123,140 @@ public class FormattedCellRenderer extends DefaultTableCellRenderer // System.out.println( "FormattedCellRenderer.setValue: converted = '" + currentFormat.format( value ) + "'" ); // } - // WORKAROUND: This works around what may be a rounding bug in DecimalFormat. (PR 256/297) - currentFormat.format( ZERO ); - - // DEBUG: code to test for weird one/zero problem (PR 256/297) - String result = currentFormat.format( value ); -/* above workaround seems to be working - if ( result.equals( "1" ) ) - { - System.out.println( "FormattedCellRenderer.setValue: Could be the ONE/ZERO problem!" ); - System.out.println( "FormattedCellRenderer.setValue: format = '" + currentFormat.getClass() + "'" ); - System.out.println( "FormattedCellRenderer.setValue: original value = '" + value + "'" ); - System.out.println( "FormattedCellRenderer.setValue: result = '" + result + "'" ); - } -*/ - setText( result ); - + // WORKAROUND: This works around what may be a rounding bug in DecimalFormat. + // (PR 256/297) + currentFormat.format(ZERO); + + // DEBUG: code to test for weird one/zero problem (PR 256/297) + String result = currentFormat.format(value); + /* + * above workaround seems to be working if ( result.equals( "1" ) ) { + * System.out.println( + * "FormattedCellRenderer.setValue: Could be the ONE/ZERO problem!" ); + * System.out.println( "FormattedCellRenderer.setValue: format = '" + + * currentFormat.getClass() + "'" ); System.out.println( + * "FormattedCellRenderer.setValue: original value = '" + value + "'" ); + * System.out.println( "FormattedCellRenderer.setValue: result = '" + result + + * "'" ); } + */ + setText(result); // setText( currentFormat.format( value ) ); - return; - } - catch ( IllegalArgumentException exc ) - { - // fall back on superclass implementation - } - } - super.setValue( value ); - } - - // FIXME: remove this when possible - private static Double ZERO = new Double( 0.0 ); - -/** -* Overridden to call context delegate methods. -*/ - public Component getTableCellRendererComponent(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) - { - Format format; - - // allow for context-sensitve formatting - format = getFormatForContext( table, value, isSelected, hasFocus, row, column ); - if ( format != null ) - { - currentFormat = format; - } - else - { - currentFormat = defaultFormat; - } - - Color color; - - // allow for context-sensitve foreground color - color = getForegroundForContext( table, value, isSelected, hasFocus, row, column ); - if ( color != null ) - { - super.setForeground( color ); - } - else - { - super.setForeground( defaultForeground ); - } - - // allow for context-sensitve background color - color = getBackgroundForContext( table, value, isSelected, hasFocus, row, column ); - if ( color != null ) - { - super.setBackground( color ); - } - else - { - super.setBackground( defaultBackground ); - } - - // have to call this here because super defaults to table's font - Component result = - super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column ); - // NOTE: DefaultTableCellRenderer returns itself. - - // allow for context-sensitve font - Font font = getFontForContext( table, value, isSelected, hasFocus, row, column ); - if ( font != null ) - { - result.setFont( font ); - } - else - { - result.setFont( defaultFont ); - } - - return result; - - } - -/** -* Override this method to provide a specific format for the -* specific cell to be rendered by this component. Any format -* returned by this method will take precedence of the format -* specified by setFormat().

-* This default implementation returns null. -* @return A Format for this cell, or null to rely on the the -* format specified by setFormat(). -*/ - public Format getFormatForContext(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) - { - return null; - } - -/** -* Override this method to provide a foreground color for the renderer. -* Because the table specifies colors for selected cells, -* these colors will only be used when renderering unselected cells.

-* This default implementation returns null. -* @return A Color for the foreground of the cell, or null to rely on -* the table's default color scheme. -*/ - public Color getForegroundForContext(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) - { - return null; - } - -/** -* Override this method to provide a background color for the renderer. -* Because the table specifies colors for selected cells, -* these colors will only be used when renderering unselected cells.

-* This default implementation returns null. -* @return A Color for the background of the cell, or null to rely on -* the table's default color scheme. -*/ - public Color getBackgroundForContext(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) - { - return null; - } - -/** -* Override this method to provide a font for the renderer.

-* This default implementation returns null. -* @return A Font for the cell, or null to rely on the table's default font. -*/ - public Font getFontForContext(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) - { - return null; - } + return; + } catch (IllegalArgumentException exc) { + // fall back on superclass implementation + } + } + super.setValue(value); + } + + // FIXME: remove this when possible + private static Double ZERO = new Double(0.0); + + /** + * Overridden to call context delegate methods. + */ + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { + Format format; + + // allow for context-sensitve formatting + format = getFormatForContext(table, value, isSelected, hasFocus, row, column); + if (format != null) { + currentFormat = format; + } else { + currentFormat = defaultFormat; + } + + Color color; + + // allow for context-sensitve foreground color + color = getForegroundForContext(table, value, isSelected, hasFocus, row, column); + if (color != null) { + super.setForeground(color); + } else { + super.setForeground(defaultForeground); + } + + // allow for context-sensitve background color + color = getBackgroundForContext(table, value, isSelected, hasFocus, row, column); + if (color != null) { + super.setBackground(color); + } else { + super.setBackground(defaultBackground); + } + + // have to call this here because super defaults to table's font + Component result = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + // NOTE: DefaultTableCellRenderer returns itself. + + // allow for context-sensitve font + Font font = getFontForContext(table, value, isSelected, hasFocus, row, column); + if (font != null) { + result.setFont(font); + } else { + result.setFont(defaultFont); + } + + return result; + + } + + /** + * Override this method to provide a specific format for the specific cell to be + * rendered by this component. Any format returned by this method will take + * precedence of the format specified by setFormat().
+ *
+ * This default implementation returns null. + * + * @return A Format for this cell, or null to rely on the the format specified + * by setFormat(). + */ + public Format getFormatForContext(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, + int column) { + return null; + } + + /** + * Override this method to provide a foreground color for the renderer. Because + * the table specifies colors for selected cells, these colors will only be used + * when renderering unselected cells.
+ *
+ * This default implementation returns null. + * + * @return A Color for the foreground of the cell, or null to rely on the + * table's default color scheme. + */ + public Color getForegroundForContext(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, + int column) { + return null; + } + + /** + * Override this method to provide a background color for the renderer. Because + * the table specifies colors for selected cells, these colors will only be used + * when renderering unselected cells.
+ *
+ * This default implementation returns null. + * + * @return A Color for the background of the cell, or null to rely on the + * table's default color scheme. + */ + public Color getBackgroundForContext(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, + int column) { + return null; + } + + /** + * Override this method to provide a font for the renderer.
+ *
+ * This default implementation returns null. + * + * @return A Font for the cell, or null to rely on the table's default font. + */ + public Font getFontForContext(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, + int column) { + return null; + } } - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/IconCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/IconCellRenderer.java index 8320d08..2d9531d 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/IconCellRenderer.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/IconCellRenderer.java @@ -54,792 +54,654 @@ import javax.swing.tree.TreeCellEditor; import javax.swing.tree.TreeCellRenderer; /** -* A cell renderer that displays icons in addition to text, -* and additionally is an editor in case you want to click -* the icon to trigger some kind of action. -* You probably should override both getStringForContext and -* getIconForContext to achieve your desired results. -* To receive mouse clicks, set the same instance of the -* renderer as the editor for the same component.

-* -* One notable addition is that this class is an action event -* broadcaster. ActionEvents are broadcast when the mouse is -* clicked on the button with an action event containing a -* user-configurable string that defaults to CLICKED.

-* -* The renderer itself can be used as a JComponent if -* you need something like a JLabel that allows you to click -* on the icon. You will want to call setIcon and setText -* to configure the component since the renderer method would -* not be called. (If you add an instance of the renderer -* to a container, you cannnot use the same instance as an -* editor in a table, tree, or list.) -* -* @author michael@mpowers.net -* @version $Revision: 904 $ -* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ -*/ -public class IconCellRenderer extends JPanel - implements TableCellRenderer, TableCellEditor, - TreeCellRenderer, TreeCellEditor, ListCellRenderer, - Runnable, ActionListener, MouseListener -{ + * A cell renderer that displays icons in addition to text, and additionally is + * an editor in case you want to click the icon to trigger some kind of action. + * You probably should override both getStringForContext and getIconForContext + * to achieve your desired results. To receive mouse clicks, set the same + * instance of the renderer as the editor for the same component.
+ *
+ * + * One notable addition is that this class is an action event broadcaster. + * ActionEvents are broadcast when the mouse is clicked on the button with an + * action event containing a user-configurable string that defaults to CLICKED. + *
+ *
+ * + * The renderer itself can be used as a JComponent if you need something like a + * JLabel that allows you to click on the icon. You will want to call setIcon + * and setText to configure the component since the renderer method would not be + * called. (If you add an instance of the renderer to a container, you cannnot + * use the same instance as an editor in a table, tree, or list.) + * + * @author michael@mpowers.net + * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) + * $ + */ +public class IconCellRenderer extends JPanel implements TableCellRenderer, TableCellEditor, TreeCellRenderer, + TreeCellEditor, ListCellRenderer, Runnable, ActionListener, MouseListener { public static final String CLICKED = "CLICKED"; - + /** - * The panel that is re-used to render everything. - * This is returned by getRendererForContext. - */ - protected JPanel rendererPanel; + * The panel that is re-used to render everything. This is returned by + * getRendererForContext. + */ + protected JPanel rendererPanel; protected JLabel rendererLabel; - protected JButton rendererButton; + protected JButton rendererButton; /** - * The panel that is used to receive mouse clicks. - * It must be a different component from rendererPanel. - * This is returned by getEditorForContext. - */ - protected JPanel editorPanel; + * The panel that is used to receive mouse clicks. It must be a different + * component from rendererPanel. This is returned by getEditorForContext. + */ + protected JPanel editorPanel; protected JLabel editorLabel; - protected JButton editorButton; - + protected JButton editorButton; + private Object lastKnownValue; - private JComponent lastKnownComponent; - + private JComponent lastKnownComponent; + // do as DefaultTableCellRenderer does private Border noFocusBorder; private Border treeFocusBorder; private Color unselectedForeground; private Color unselectedBackground; - + private Vector actionListeners; private String actionCommand; private Vector cellEditorListeners; - - private boolean editable; - private boolean clickable; - - /** - * Default constructor. - */ - public IconCellRenderer() - { - editable = true; - clickable = true; - - noFocusBorder = new EmptyBorder(1, 1, 1, 1); - treeFocusBorder = new LineBorder( - UIManager.getColor("Tree.selectionBorderColor") ); - setActionCommand( CLICKED ); - - rendererPanel = new JPanel(); - rendererPanel.setLayout( new GridBagLayout() ); - - editorPanel = this; - editorPanel.setLayout( new GridBagLayout() ); - - // set up constraints - GridBagConstraints imageConstraints = new GridBagConstraints(); - imageConstraints.gridx = 0; - GridBagConstraints labelConstraints = new GridBagConstraints(); - labelConstraints.fill = GridBagConstraints.HORIZONTAL; - labelConstraints.gridx = 1; - labelConstraints.weightx = 1.0; - labelConstraints.ipadx = 1; - labelConstraints.insets = new Insets( 0, 1, 0, 0 ); // sweat the pixel - - // make the editor panel go away when not in use - // and pass through all mouse events to container - - //this is not very useful since editorLabel and editorButton - //get all of the events - editorPanel.addMouseListener( this ); - + + private boolean editable; + private boolean clickable; + + /** + * Default constructor. + */ + public IconCellRenderer() { + editable = true; + clickable = true; + + noFocusBorder = new EmptyBorder(1, 1, 1, 1); + treeFocusBorder = new LineBorder(UIManager.getColor("Tree.selectionBorderColor")); + setActionCommand(CLICKED); + + rendererPanel = new JPanel(); + rendererPanel.setLayout(new GridBagLayout()); + + editorPanel = this; + editorPanel.setLayout(new GridBagLayout()); + + // set up constraints + GridBagConstraints imageConstraints = new GridBagConstraints(); + imageConstraints.gridx = 0; + GridBagConstraints labelConstraints = new GridBagConstraints(); + labelConstraints.fill = GridBagConstraints.HORIZONTAL; + labelConstraints.gridx = 1; + labelConstraints.weightx = 1.0; + labelConstraints.ipadx = 1; + labelConstraints.insets = new Insets(0, 1, 0, 0); // sweat the pixel + + // make the editor panel go away when not in use + // and pass through all mouse events to container + + // this is not very useful since editorLabel and editorButton + // get all of the events + editorPanel.addMouseListener(this); + rendererLabel = new JLabel(); - rendererLabel.setOpaque( false ); - rendererPanel.add( rendererLabel, labelConstraints ); + rendererLabel.setOpaque(false); + rendererPanel.add(rendererLabel, labelConstraints); editorLabel = new JLabel(); - editorLabel.setText( "" ); // default state - editorLabel.setOpaque( false ); - editorPanel.add( editorLabel, labelConstraints ); + editorLabel.setText(""); // default state + editorLabel.setOpaque(false); + editorPanel.add(editorLabel, labelConstraints); unselectedForeground = rendererLabel.getForeground(); unselectedBackground = rendererLabel.getBackground(); - - rendererButton = new JButton(); - rendererButton.setBorder( null ); - rendererButton.setBorderPainted( false ); - rendererButton.setContentAreaFilled( false ); - rendererButton.setFocusPainted( false ); - rendererButton.setMargin( new Insets( 0, 0, 0, 0 ) ); - rendererPanel.add( rendererButton, imageConstraints ); - - editorButton = new JButton(); - editorButton.setEnabled( clickable ); // default state - editorButton.setIcon( null ); // default state - editorButton.setBorder( null ); - editorButton.setBorderPainted( false ); - editorButton.setContentAreaFilled( false ); - editorButton.setFocusPainted( false ); - editorButton.setMargin( new Insets( 0, 0, 0, 0 ) ); - editorPanel.add( editorButton, imageConstraints ); - - editorButton.addActionListener( this ); - - //add these in order to dispatch the MouseEvents - //to the lastKnownComponent, and proper management of - //DnD operations - editorLabel.addMouseListener( this ); - editorButton.addMouseListener( this ); - } - -/** -* Returns the text string currently displayed in the editor component. -*/ - public String getText() - { - return editorLabel.getText(); - } - -/** -* Sets the text string displayed in the editor component. -* Default is an empty string. -*/ - public void setText( String aString ) - { - editorLabel.setText( aString ); - } -/** -* Returns the icon currently displayed in the editor component. -*/ - public Icon getIcon() - { - return editorButton.getIcon(); - } - -/** -* Sets the icon currently displayed in the editor component. -* Default is null. -*/ - public void setIcon( Icon anIcon ) - { - editorButton.setIcon( anIcon ); - if ( !isClickable() ) - { - editorButton.setDisabledIcon( anIcon ); - } - } + rendererButton = new JButton(); + rendererButton.setBorder(null); + rendererButton.setBorderPainted(false); + rendererButton.setContentAreaFilled(false); + rendererButton.setFocusPainted(false); + rendererButton.setMargin(new Insets(0, 0, 0, 0)); + rendererPanel.add(rendererButton, imageConstraints); + + editorButton = new JButton(); + editorButton.setEnabled(clickable); // default state + editorButton.setIcon(null); // default state + editorButton.setBorder(null); + editorButton.setBorderPainted(false); + editorButton.setContentAreaFilled(false); + editorButton.setFocusPainted(false); + editorButton.setMargin(new Insets(0, 0, 0, 0)); + editorPanel.add(editorButton, imageConstraints); + + editorButton.addActionListener(this); + + // add these in order to dispatch the MouseEvents + // to the lastKnownComponent, and proper management of + // DnD operations + editorLabel.addMouseListener(this); + editorButton.addMouseListener(this); + } -/** -* Returns whether the editor component's label text is editable. -*/ - public boolean isEditable() - { - return editable; - } - -/** -* Sets whether the editor component's label text is editable. -* Default is true. Editable text is not yet implemented. -*/ - public void setEditable( boolean isEditable ) - { - editable = isEditable; - } + /** + * Returns the text string currently displayed in the editor component. + */ + public String getText() { + return editorLabel.getText(); + } -/** -* Returns whether the editor component's icon is clickable. -*/ - public boolean isClickable() - { - return clickable; - } - -/** -* Sets whether the editor component's icon is clickable. -* Default is true. -*/ - public void setClickable( boolean isClickable ) - { - clickable = isClickable; - editorButton.setEnabled( clickable ); - } + /** + * Sets the text string displayed in the editor component. Default is an empty + * string. + */ + public void setText(String aString) { + editorLabel.setText(aString); + } -/** -* Returns the component from getRendererForContext. -*/ - public Component getListCellRendererComponent(JList list, - Object value, - int index, - boolean isSelected, - boolean cellHasFocus) - { - lastKnownComponent = list; - return getRendererForContext( - list, value, index, 0, isSelected, cellHasFocus, false, true ); - } + /** + * Returns the icon currently displayed in the editor component. + */ + public Icon getIcon() { + return editorButton.getIcon(); + } -/** -* Returns the component from getRendererForContext. -*/ - public Component getTableCellRendererComponent(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) - { - lastKnownComponent = table; - return getRendererForContext( - table, value, row, column, isSelected, hasFocus, false, true ); - } + /** + * Sets the icon currently displayed in the editor component. Default is null. + */ + public void setIcon(Icon anIcon) { + editorButton.setIcon(anIcon); + if (!isClickable()) { + editorButton.setDisabledIcon(anIcon); + } + } -/** -* Returns the component from getRendererForContext. -*/ - public Component getTreeCellRendererComponent(JTree tree, - Object value, - boolean selected, - boolean expanded, - boolean leaf, - int row, - boolean hasFocus) - { - lastKnownComponent = tree; - return getRendererForContext( - tree, value, row, 0, selected, hasFocus, expanded, leaf ); - } - -/** -* Returns getEditorForContext with the same parameters with hasFocus true. -*/ - public Component getTableCellEditorComponent(JTable table, - Object value, boolean isSelected, int row, int column) - { + /** + * Returns whether the editor component's label text is editable. + */ + public boolean isEditable() { + return editable; + } + + /** + * Sets whether the editor component's label text is editable. Default is true. + * Editable text is not yet implemented. + */ + public void setEditable(boolean isEditable) { + editable = isEditable; + } + + /** + * Returns whether the editor component's icon is clickable. + */ + public boolean isClickable() { + return clickable; + } + + /** + * Sets whether the editor component's icon is clickable. Default is true. + */ + public void setClickable(boolean isClickable) { + clickable = isClickable; + editorButton.setEnabled(clickable); + } + + /** + * Returns the component from getRendererForContext. + */ + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, + boolean cellHasFocus) { + lastKnownComponent = list; + return getRendererForContext(list, value, index, 0, isSelected, cellHasFocus, false, true); + } + + /** + * Returns the component from getRendererForContext. + */ + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { + lastKnownComponent = table; + return getRendererForContext(table, value, row, column, isSelected, hasFocus, false, true); + } + + /** + * Returns the component from getRendererForContext. + */ + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, + boolean leaf, int row, boolean hasFocus) { + lastKnownComponent = tree; + return getRendererForContext(tree, value, row, 0, selected, hasFocus, expanded, leaf); + } + + /** + * Returns getEditorForContext with the same parameters with hasFocus true. + */ + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { lastKnownValue = value; - lastKnownComponent = table; - return getEditorForContext( - table, value, row, column, isSelected, true, false, true ); + lastKnownComponent = table; + return getEditorForContext(table, value, row, column, isSelected, true, false, true); } -/** -* Returns the component from getEditorForContext with hasFocus true. -*/ - public Component getTreeCellEditorComponent(JTree tree, - Object value, - boolean isSelected, - boolean expanded, - boolean leaf, - int row) - { - - - lastKnownValue = value; - lastKnownComponent = tree; - - return getEditorForContext( - tree, value, row, 0, isSelected, true, expanded, leaf ); - } - -/** -* This default implementation returns a JPanel that is configured by -* calling configureComponentForContext. -* @return An component that is used to render content. -*/ - public Component getRendererForContext( - JComponent container, Object value, - int row, int column, - boolean isSelected, boolean hasFocus, - boolean isExpanded, boolean isLeaf ) - { - - - configureComponentForContext( rendererPanel, rendererButton, rendererLabel, - container, value, row, column, - isSelected, hasFocus, isExpanded, isLeaf ); + /** + * Returns the component from getEditorForContext with hasFocus true. + */ + public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, + boolean leaf, int row) { + + lastKnownValue = value; + lastKnownComponent = tree; + + return getEditorForContext(tree, value, row, 0, isSelected, true, expanded, leaf); + } + + /** + * This default implementation returns a JPanel that is configured by calling + * configureComponentForContext. + * + * @return An component that is used to render content. + */ + public Component getRendererForContext(JComponent container, Object value, int row, int column, boolean isSelected, + boolean hasFocus, boolean isExpanded, boolean isLeaf) { + + configureComponentForContext(rendererPanel, rendererButton, rendererLabel, container, value, row, column, + isSelected, hasFocus, isExpanded, isLeaf); return rendererPanel; - } + } -/** -* This method returns a separate component that should be visually -* identical to the renderer component. We can't simply reuse the -* renderer component because the renderer is still used to paint -* the table while the editor component is displayed. Clicks are -* received on this component. -* This default implementation returns a JPanel that is configured by -* calling configureComponentForContext. -* @return A component used to receive clicks on the cell. -*/ - public Component getEditorForContext( - JComponent container, Object value, - int row, int column, - boolean isSelected, boolean hasFocus, - boolean isExpanded, boolean isLeaf ) - { - configureComponentForContext( editorPanel, editorButton, editorLabel, - container, value, row, column, - true, hasFocus, isExpanded, isLeaf ); // editor should always be selected + /** + * This method returns a separate component that should be visually identical to + * the renderer component. We can't simply reuse the renderer component because + * the renderer is still used to paint the table while the editor component is + * displayed. Clicks are received on this component. This default implementation + * returns a JPanel that is configured by calling configureComponentForContext. + * + * @return A component used to receive clicks on the cell. + */ + public Component getEditorForContext(JComponent container, Object value, int row, int column, boolean isSelected, + boolean hasFocus, boolean isExpanded, boolean isLeaf) { + configureComponentForContext(editorPanel, editorButton, editorLabel, container, value, row, column, true, + hasFocus, isExpanded, isLeaf); // editor should always be selected return editorPanel; - } + } -/** -* Called to configure components -*/ - protected void configureComponentForContext( - JPanel component, JButton iconButton, JLabel label, - JComponent container, Object value, - int row, int column, - boolean isSelected, boolean hasFocus, - boolean isExpanded, boolean isLeaf ) - { - if (hasFocus) - { - if ( container instanceof JTable ) - { - component.setBorder( - UIManager.getBorder("Table.focusCellHighlightBorder") ); - } - else - { - component.setBorder( noFocusBorder ); - } - - if ( container instanceof JTree ) // was: (false) - { - label.setBorder( treeFocusBorder ); - } - else - { - label.setBorder( noFocusBorder ); - } - } - else - { - label.setBorder(noFocusBorder); - component.setBorder(noFocusBorder); - } - - if (isSelected) - { - if ( container instanceof JTree ) - { - label.setOpaque( true ); - label.setForeground(UIManager.getColor("Tree.selectionForeground")); - label.setBackground(UIManager.getColor("Tree.selectionBackground")); - component.setBackground(container.getBackground()); - } - else if ( container instanceof JTable ) - { - label.setOpaque( false ); - label.setForeground( ((JTable)container).getSelectionForeground() ); - component.setBackground(((JTable)container).getSelectionBackground()); - } - else - { - label.setOpaque( false ); - label.setForeground(UIManager.getColor("Table.selectionForeground")); - component.setBackground(UIManager.getColor("Table.selectionBackground")); - } - } - else - { - label.setOpaque( false ); - label.setForeground(container.getForeground()); - component.setBackground(container.getBackground()); - } - - label.setFont(container.getFont()); - - Icon icon = getIconForContext( - container, value, row, column, isSelected, hasFocus, isExpanded, isLeaf ); - iconButton.setIcon( icon ); - if ( !isClickable() ) - { - iconButton.setDisabledIcon( icon ); - } - - String text = getStringForContext( - container, value, row, column, isSelected, hasFocus, isExpanded, isLeaf ); - - if ( ( text == null ) || ( "".equals( text ) ) ) - { - if ( ! label.getText().equals( "" ) ) - label.setText( "" ); + /** + * Called to configure components + */ + protected void configureComponentForContext(JPanel component, JButton iconButton, JLabel label, + JComponent container, Object value, int row, int column, boolean isSelected, boolean hasFocus, + boolean isExpanded, boolean isLeaf) { + if (hasFocus) { + if (container instanceof JTable) { + component.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); + } else { + component.setBorder(noFocusBorder); + } + + if (container instanceof JTree) // was: (false) + { + label.setBorder(treeFocusBorder); + } else { + label.setBorder(noFocusBorder); + } + } else { + label.setBorder(noFocusBorder); + component.setBorder(noFocusBorder); } - else - { - if ( ! label.getText().equals( text ) ) - label.setText( text ); + + if (isSelected) { + if (container instanceof JTree) { + label.setOpaque(true); + label.setForeground(UIManager.getColor("Tree.selectionForeground")); + label.setBackground(UIManager.getColor("Tree.selectionBackground")); + component.setBackground(container.getBackground()); + } else if (container instanceof JTable) { + label.setOpaque(false); + label.setForeground(((JTable) container).getSelectionForeground()); + component.setBackground(((JTable) container).getSelectionBackground()); + } else { + label.setOpaque(false); + label.setForeground(UIManager.getColor("Table.selectionForeground")); + component.setBackground(UIManager.getColor("Table.selectionBackground")); + } + } else { + label.setOpaque(false); + label.setForeground(container.getForeground()); + component.setBackground(container.getBackground()); + } + + label.setFont(container.getFont()); + + Icon icon = getIconForContext(container, value, row, column, isSelected, hasFocus, isExpanded, isLeaf); + iconButton.setIcon(icon); + if (!isClickable()) { + iconButton.setDisabledIcon(icon); + } + + String text = getStringForContext(container, value, row, column, isSelected, hasFocus, isExpanded, isLeaf); + + if ((text == null) || ("".equals(text))) { + if (!label.getText().equals("")) + label.setText(""); + } else { + if (!label.getText().equals(text)) + label.setText(text); } } - -/** -* Override this method to provide an icon for the renderer. -* This default implementation returns null. -* @return An icon to be displayed in the cell, or null to omit the -* icon from the cell. -*/ - public Icon getIconForContext( - JComponent container, Object value, - int row, int column, - boolean isSelected, boolean hasFocus, - boolean isExpanded, boolean isLeaf ) - { - return null; - } -/** -* Override this method to provide a string for the renderer. -* This default implementation returns toString on the value parameter, -* or null if the value is null. -* @return A string to be displayed in the cell. -*/ - public String getStringForContext( - JComponent container, Object value, - int row, int column, - boolean isSelected, boolean hasFocus, - boolean isExpanded, boolean isLeaf ) - { - if ( value == null ) return null; - return value.toString(); - } - - /** - * Adds the specified listener to the list of listeners - * to be notified when the button receives a click. - */ - public void addActionListener( ActionListener aListener ) - { - if ( actionListeners == null ) - { - actionListeners = new Vector( 2 ); + /** + * Override this method to provide an icon for the renderer. This default + * implementation returns null. + * + * @return An icon to be displayed in the cell, or null to omit the icon from + * the cell. + */ + public Icon getIconForContext(JComponent container, Object value, int row, int column, boolean isSelected, + boolean hasFocus, boolean isExpanded, boolean isLeaf) { + return null; + } + + /** + * Override this method to provide a string for the renderer. This default + * implementation returns toString on the value parameter, or null if the value + * is null. + * + * @return A string to be displayed in the cell. + */ + public String getStringForContext(JComponent container, Object value, int row, int column, boolean isSelected, + boolean hasFocus, boolean isExpanded, boolean isLeaf) { + if (value == null) + return null; + return value.toString(); + } + + /** + * Adds the specified listener to the list of listeners to be notified when the + * button receives a click. + */ + public void addActionListener(ActionListener aListener) { + if (actionListeners == null) { + actionListeners = new Vector(2); } - actionListeners.add( aListener ); + actionListeners.add(aListener); } - + /** - * Removes the specified listener from the list of listeners - * to be notified when the button receives a click. - */ - public void removeActionListener( ActionListener aListener ) - { - actionListeners.remove( aListener ); + * Removes the specified listener from the list of listeners to be notified when + * the button receives a click. + */ + public void removeActionListener(ActionListener aListener) { + actionListeners.remove(aListener); } - + /** - * Broadcasts the specified action event to all listeners. - */ - protected void fireActionEvent( ActionEvent anActionEvent ) - { - if ( actionListeners == null ) return; + * Broadcasts the specified action event to all listeners. + */ + protected void fireActionEvent(ActionEvent anActionEvent) { + if (actionListeners == null) + return; // vector's enumeration is not fail-fast Enumeration e = actionListeners.elements(); - while ( e.hasMoreElements() ) - { - ((ActionListener)e.nextElement()).actionPerformed( anActionEvent ); + while (e.hasMoreElements()) { + ((ActionListener) e.nextElement()).actionPerformed(anActionEvent); } } - + /** - * Returns the action command broadcast when this icon - * receives a click. Defaults to CLICKED. - */ - public String getActionCommand() - { + * Returns the action command broadcast when this icon receives a click. + * Defaults to CLICKED. + */ + public String getActionCommand() { return actionCommand; } /** - * Sets the action command broadcast when this table - * receives a double click. - */ - public void setActionCommand( String anActionCommand ) - { - actionCommand = anActionCommand; + * Sets the action command broadcast when this table receives a double click. + */ + public void setActionCommand(String anActionCommand) { + actionCommand = anActionCommand; } - + // interface CellEditor /** - * Returns lastKnownValue, although this should not be called. - */ - public Object getCellEditorValue() - { + * Returns lastKnownValue, although this should not be called. + */ + public Object getCellEditorValue() { return lastKnownValue; } - + /** - * Returns true. - */ - public boolean isCellEditable(EventObject anEvent) - { - return true; + * Returns true. + */ + public boolean isCellEditable(EventObject anEvent) { + return true; } - + /** - * Returns true. - */ - public boolean shouldSelectCell(EventObject anEvent) - { + * Returns true. + */ + public boolean shouldSelectCell(EventObject anEvent) { return true; } - + /** - * Fires an editing stopped event and returns true. - */ - public boolean stopCellEditing() - { - ChangeEvent event = new ChangeEvent( this ); - if ( cellEditorListeners != null ) - { + * Fires an editing stopped event and returns true. + */ + public boolean stopCellEditing() { + ChangeEvent event = new ChangeEvent(this); + if (cellEditorListeners != null) { // vector's enumeration is not fail-fast Enumeration e = cellEditorListeners.elements(); - while ( e.hasMoreElements() ) - { - // broadcast editing cancelled since no value is edited - ((CellEditorListener)e.nextElement()).editingCanceled( event ); + while (e.hasMoreElements()) { + // broadcast editing cancelled since no value is edited + ((CellEditorListener) e.nextElement()).editingCanceled(event); } } - lastKnownComponent = null; + lastKnownComponent = null; return true; } - - /** - * Fires an editing cancelled event and returns true. - */ - public void cancelCellEditing() - { - //HACK: cancelCellEditing() causes for the dragGesture - //to be NOT recognized AT ALL since on the next MOUSE_PRESSED - //the cell editor first needs to startEditing() [if in the tree - //the CellEditorListener is a BasicTreeUI class] - //(before the drag gesture event can be recognized). - //Also the lastKnownComponent should not be set to null, - //none of the mouse events won't dispathced to the lastKnownComponent - //in that case. - - //Not calling it at all does seem to fix it, but what are the - //consequences??? - //Trying to workaround this might solve it, but it introduces having - //an extra listener (a MouseMotionListnener), which might be wasteful - //(i.e. only if a Mouse_dragged event has been initiated, but DragGesture - //hasn't been recognized, postpone calling this till finish the DnD event) - //But what if do DnD and not exited ??? The mouseExited() is not called - //anyway until the DnD event is done. - - ChangeEvent event = new ChangeEvent( this ); - if ( cellEditorListeners == null ) return; + + /** + * Fires an editing cancelled event and returns true. + */ + public void cancelCellEditing() { + // HACK: cancelCellEditing() causes for the dragGesture + // to be NOT recognized AT ALL since on the next MOUSE_PRESSED + // the cell editor first needs to startEditing() [if in the tree + // the CellEditorListener is a BasicTreeUI class] + // (before the drag gesture event can be recognized). + // Also the lastKnownComponent should not be set to null, + // none of the mouse events won't dispathced to the lastKnownComponent + // in that case. + + // Not calling it at all does seem to fix it, but what are the + // consequences??? + // Trying to workaround this might solve it, but it introduces having + // an extra listener (a MouseMotionListnener), which might be wasteful + // (i.e. only if a Mouse_dragged event has been initiated, but DragGesture + // hasn't been recognized, postpone calling this till finish the DnD event) + // But what if do DnD and not exited ??? The mouseExited() is not called + // anyway until the DnD event is done. + + ChangeEvent event = new ChangeEvent(this); + if (cellEditorListeners == null) + return; // vector's enumeration is not fail-fast Enumeration e = cellEditorListeners.elements(); - - while ( e.hasMoreElements() ) - { - ((CellEditorListener)e.nextElement()).editingCanceled( event ); - } - - //DO not nullify this - lastKnownComponent = null; - } - - /** - * Adds the specified listener to the list of listeners - * to be notified when the table receives a double click. - */ - public void addCellEditorListener( CellEditorListener aListener ) - { - if ( cellEditorListeners == null ) - { - cellEditorListeners = new Vector( 2 ); + + while (e.hasMoreElements()) { + ((CellEditorListener) e.nextElement()).editingCanceled(event); + } + + // DO not nullify this + lastKnownComponent = null; + } + + /** + * Adds the specified listener to the list of listeners to be notified when the + * table receives a double click. + */ + public void addCellEditorListener(CellEditorListener aListener) { + if (cellEditorListeners == null) { + cellEditorListeners = new Vector(2); } - cellEditorListeners.add( aListener ); + cellEditorListeners.add(aListener); } - + /** - * Removes the specified listener from the list of listeners - * to be notified when the table receives a double click. - */ - public void removeCellEditorListener( CellEditorListener aListener ) - { - cellEditorListeners.remove( aListener ); + * Removes the specified listener from the list of listeners to be notified when + * the table receives a double click. + */ + public void removeCellEditorListener(CellEditorListener aListener) { + cellEditorListeners.remove(aListener); } - + // interface ActionListener - - /** - * Puts ourself on the end of the event queue for - * firing our action event to all listeners. - */ - public void actionPerformed( ActionEvent evt ) - { - //commented out in order NOT to set lastKnownComponent to null, since - //if this object is inside a table or tree, relying on getCellEditorValue() - //to return the currently edited object - //cancelCellEditing(); - - SwingUtilities.invokeLater( this ); - } - + + /** + * Puts ourself on the end of the event queue for firing our action event to all + * listeners. + */ + public void actionPerformed(ActionEvent evt) { + // commented out in order NOT to set lastKnownComponent to null, since + // if this object is inside a table or tree, relying on getCellEditorValue() + // to return the currently edited object + // cancelCellEditing(); + + SwingUtilities.invokeLater(this); + } + // interface Runnable - + /** - * Fires the action event to all listeners. - * This is triggered by a click on the icon. - */ - public void run() - { - fireActionEvent( new ActionEvent( this, 0, getActionCommand() ) ); + * Fires the action event to all listeners. This is triggered by a click on the + * icon. + */ + public void run() { + fireActionEvent(new ActionEvent(this, 0, getActionCommand())); } // interface MouseListener - - /** - * Passes through editor mouse clicks to last known component. - * (left click only) - */ - public void mouseClicked(MouseEvent e) - { - if(lastKnownComponent != null){ - Object source = e.getSource(); - if(source != null) - { - if(source == editorPanel) - { - lastKnownComponent.dispatchEvent( - SwingUtilities.convertMouseEvent( - editorPanel, e, lastKnownComponent ) ); - - } - else if(source == editorLabel) - { - lastKnownComponent.dispatchEvent( - SwingUtilities.convertMouseEvent( - editorLabel, e, lastKnownComponent ) ); - } - - else if(source == editorButton) - { - lastKnownComponent.dispatchEvent( - SwingUtilities.convertMouseEvent( - editorButton, e, lastKnownComponent ) ); - } - } - } - } - - /** - * Passes through editor right-mouse (popup trigger) mouse events to last known component. - * Needed for possible displaying of popup menus on right click - */ - public void mousePressed(MouseEvent e) - { - if ( e.isPopupTrigger() ) - { - if(lastKnownComponent != null) - { - Object source = e.getSource(); - if(source != null) - { - if(source == editorPanel) - { - lastKnownComponent.dispatchEvent( - SwingUtilities.convertMouseEvent( - editorPanel, e, lastKnownComponent ) ); - } - else if(source == editorLabel) - { - lastKnownComponent.dispatchEvent( - SwingUtilities.convertMouseEvent( - editorLabel, e, lastKnownComponent ) ); - } - - else if(source == editorButton) - { - lastKnownComponent.dispatchEvent( - SwingUtilities.convertMouseEvent( - editorButton, e, lastKnownComponent ) ); - } - } - } - } - } - - /** - * Does nothing. - */ - public void mouseReleased(MouseEvent e) - { - if ( e.isPopupTrigger() ) - { - if(lastKnownComponent != null){ - - Object source = e.getSource(); - if(source != null) - { - if(source == editorPanel) - { - lastKnownComponent.dispatchEvent( - SwingUtilities.convertMouseEvent( - editorPanel, e, lastKnownComponent ) ); - } - - else if(source == editorLabel) - { - lastKnownComponent.dispatchEvent( - SwingUtilities.convertMouseEvent( - editorLabel, e, lastKnownComponent ) ); - } - - else if(source == editorButton) - { - lastKnownComponent.dispatchEvent( - SwingUtilities.convertMouseEvent( - editorButton, e, lastKnownComponent ) ); - } - } - } - } - } - - /** - * Does nothing. - */ - public void mouseEntered(MouseEvent e) - { - } - - /** - * Cancels cell editing. - */ - public void mouseExited(MouseEvent e) - { - Object source = e.getSource(); - if(source != null && source instanceof JComponent){ - //need to convert the Point from the source's coordinate system to editorPanel's coordinate system. - //(note that simple editorPanel.contains(e.getPoint()) fails if source is editorButton) - - Point convertedPoint = SwingUtilities.convertPoint((JComponent) source, e.getPoint(), editorPanel); - - //check if exited from editorButton, but still inside the editorPanel (works for editorLabel as well) - if(!editorPanel.contains(convertedPoint)){ - - //This was getting called before, but it interfers with the DnD operation - cancelCellEditing(); - } - } - } - - /* This might be redundant - public void cleanUp(){ - - //since cancelCellEditing() was never called call it now - cancelCellEditing(); - stopCellEditing(); - - editorButton.removeActionListener( this ); - editorPanel.removeMouseListener( this ); - editorLabel.removeMouseListener( this ); - editorButton.removeMouseListener( this ); - lastKnownComponent = null; - lastKnownValue = null; - } - */ + + /** + * Passes through editor mouse clicks to last known component. (left click only) + */ + public void mouseClicked(MouseEvent e) { + if (lastKnownComponent != null) { + Object source = e.getSource(); + if (source != null) { + if (source == editorPanel) { + lastKnownComponent + .dispatchEvent(SwingUtilities.convertMouseEvent(editorPanel, e, lastKnownComponent)); + + } else if (source == editorLabel) { + lastKnownComponent + .dispatchEvent(SwingUtilities.convertMouseEvent(editorLabel, e, lastKnownComponent)); + } + + else if (source == editorButton) { + lastKnownComponent + .dispatchEvent(SwingUtilities.convertMouseEvent(editorButton, e, lastKnownComponent)); + } + } + } + } + + /** + * Passes through editor right-mouse (popup trigger) mouse events to last known + * component. Needed for possible displaying of popup menus on right click + */ + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + if (lastKnownComponent != null) { + Object source = e.getSource(); + if (source != null) { + if (source == editorPanel) { + lastKnownComponent + .dispatchEvent(SwingUtilities.convertMouseEvent(editorPanel, e, lastKnownComponent)); + } else if (source == editorLabel) { + lastKnownComponent + .dispatchEvent(SwingUtilities.convertMouseEvent(editorLabel, e, lastKnownComponent)); + } + + else if (source == editorButton) { + lastKnownComponent + .dispatchEvent(SwingUtilities.convertMouseEvent(editorButton, e, lastKnownComponent)); + } + } + } + } + } + + /** + * Does nothing. + */ + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + if (lastKnownComponent != null) { + + Object source = e.getSource(); + if (source != null) { + if (source == editorPanel) { + lastKnownComponent + .dispatchEvent(SwingUtilities.convertMouseEvent(editorPanel, e, lastKnownComponent)); + } + + else if (source == editorLabel) { + lastKnownComponent + .dispatchEvent(SwingUtilities.convertMouseEvent(editorLabel, e, lastKnownComponent)); + } + + else if (source == editorButton) { + lastKnownComponent + .dispatchEvent(SwingUtilities.convertMouseEvent(editorButton, e, lastKnownComponent)); + } + } + } + } + } + + /** + * Does nothing. + */ + public void mouseEntered(MouseEvent e) { + } + + /** + * Cancels cell editing. + */ + public void mouseExited(MouseEvent e) { + Object source = e.getSource(); + if (source != null && source instanceof JComponent) { + // need to convert the Point from the source's coordinate system to + // editorPanel's coordinate system. + // (note that simple editorPanel.contains(e.getPoint()) fails if source is + // editorButton) + + Point convertedPoint = SwingUtilities.convertPoint((JComponent) source, e.getPoint(), editorPanel); + + // check if exited from editorButton, but still inside the editorPanel (works + // for editorLabel as well) + if (!editorPanel.contains(convertedPoint)) { + + // This was getting called before, but it interfers with the DnD operation + cancelCellEditing(); + } + } + } + + /* + * This might be redundant public void cleanUp(){ + * + * //since cancelCellEditing() was never called call it now cancelCellEditing(); + * stopCellEditing(); + * + * editorButton.removeActionListener( this ); editorPanel.removeMouseListener( + * this ); editorLabel.removeMouseListener( this ); + * editorButton.removeMouseListener( this ); lastKnownComponent = null; + * lastKnownValue = null; } + */ } diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ImagePanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ImagePanel.java index cdaa218..4fa8e04 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ImagePanel.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ImagePanel.java @@ -26,79 +26,58 @@ import java.awt.image.ImageObserver; import javax.swing.JPanel; /** -* A JPanel that renders an image, tiling as necessary to -* fill the panel. -* The preferred size of the panel is the size of the image -* and will change until the image is fully loaded, so using -* a media tracker is recommended. -* -* @author michael@mpowers.net -* @version $Revision: 904 $ -*/ -public class ImagePanel extends JPanel implements ImageObserver -{ + * A JPanel that renders an image, tiling as necessary to fill the panel. The + * preferred size of the panel is the size of the image and will change until + * the image is fully loaded, so using a media tracker is recommended. + * + * @author michael@mpowers.net + * @version $Revision: 904 $ + */ +public class ImagePanel extends JPanel implements ImageObserver { protected Image image; protected int imageWidth, imageHeight; - - public ImagePanel() - { - this( null ); - } - - public ImagePanel( Image anImage ) - { - image = anImage; - if ( anImage != null ) - { - prepareImage( image, this ); + + public ImagePanel() { + this(null); + } + + public ImagePanel(Image anImage) { + image = anImage; + if (anImage != null) { + prepareImage(image, this); // these may return -1 - imageWidth = image.getWidth( this ); - imageHeight = image.getHeight( this ); - } - else - { + imageWidth = image.getWidth(this); + imageHeight = image.getHeight(this); + } else { imageWidth = 0; imageHeight = 0; } } - - protected void paintComponent(Graphics g) - { - if ( ( image != null ) && ( imageWidth > 0 ) && ( imageHeight > 0 ) ) - { + + protected void paintComponent(Graphics g) { + if ((image != null) && (imageWidth > 0) && (imageHeight > 0)) { int width = getWidth(); int height = getHeight(); - - for ( int x = 0; x < width; x += imageWidth ) - { - for ( int y = 0; y < height; y += imageHeight ) - { - g.drawImage( image, x, y, - imageWidth, imageHeight, - getBackground(), this ); + + for (int x = 0; x < width; x += imageWidth) { + for (int y = 0; y < height; y += imageHeight) { + g.drawImage(image, x, y, imageWidth, imageHeight, getBackground(), this); } } } } - - public boolean imageUpdate(Image img, - int infoflags, - int x, - int y, - int width, - int height) - { + + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { imageWidth = width; imageHeight = height; - setPreferredSize( new Dimension( width, height ) ); - revalidate(); + setPreferredSize(new Dimension(width, height)); + revalidate(); repaint(); - - if ( ( infoflags & ImageObserver.ALLBITS ) == ImageObserver.ALLBITS ) - { - return false; + + if ((infoflags & ImageObserver.ALLBITS) == ImageObserver.ALLBITS) { + return false; } return true; } - + } diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/InfoPanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/InfoPanel.java index 55c1e36..a2c0182 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/InfoPanel.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/InfoPanel.java @@ -49,1645 +49,1521 @@ import javax.swing.JTextField; import javax.swing.SwingConstants; /** -* InfoPanel uses labels and textfields (or any other component - see below) -* to display a list of keys and values in a well-aligned and consistent manner, -* conforming to alignment and pixel spacing in the java look and feel -* design guidelines. -*

-* -* Each key is displayed in a label to the left of the component that contains -* the corresponding value. Each row is displayed starting at the top of the -* component's available area. Each row's height is the maximum preferred -* height of its components and the field itself gets as much of the width as -* it can, dependent on the length of the longest label.

-* -* The values in the fields can be editable, and the -* current value can be retrieved using the key - for this reason, unique keys -* are recommended.

-* -* As a convenience, push buttons may be placed across the -* bottom of the panel in a manner similar to ButtonPanel.

-* -* The panel forwards any ActionEvents generated by the components and -* buttons on it to all registered listeners.

-* -* Optionally, any component can be used instead of a textfield. -* However, get/setValueForKey() and get/setEditable() -* may not work for those components. Use getComponentForKey() to -* access them instead. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ -*/ -public class InfoPanel extends JPanel implements ActionListener -{ -/** -* Special label for an empty pair - a label and component -* that take up space but are hidden from view. This might -* be useful for achieving certain layouts. -*/ - public static final String HIDDEN = "(hidden)"; - - /** Cache for the introspectComponent method */ - private static Map _method_cache = - Collections.synchronizedMap( new HashMap(30) ); - - protected Container listContainer = null; - protected int hgap; // set in constructor - protected int vgap; // set in constructor - protected int margin; // set in constructor - protected int columns; // set in constructor - protected List fields = null; - protected List labels = null; - protected List fieldSpacers = null; - protected ButtonPanel buttonPanel = null; - protected boolean isEditable = true; - protected String prefix; - protected String postfix; + * InfoPanel uses labels and textfields (or any other component - see below) to + * display a list of keys and values in a well-aligned and consistent manner, + * conforming to alignment and pixel spacing in the java look and feel + * design + * guidelines.
+ *
+ * + * Each key is displayed in a label to the left of the component that contains + * the corresponding value. Each row is displayed starting at the top of the + * component's available area. Each row's height is the maximum preferred height + * of its components and the field itself gets as much of the width as it can, + * dependent on the length of the longest label.
+ *
+ * + * The values in the fields can be editable, and the current value can be + * retrieved using the key - for this reason, unique keys are recommended.
+ *
+ * + * As a convenience, push buttons may be placed across the bottom of the panel + * in a manner similar to ButtonPanel.
+ *
+ * + * The panel forwards any ActionEvents generated by the components and buttons + * on it to all registered listeners.
+ *
+ * + * Optionally, any component can be used instead of a textfield. However, + * get/setValueForKey() and get/setEditable() may not + * work for those components. Use getComponentForKey() to access + * them instead. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) + * $ + */ +public class InfoPanel extends JPanel implements ActionListener { + /** + * Special label for an empty pair - a label and component that take up space + * but are hidden from view. This might be useful for achieving certain layouts. + */ + public static final String HIDDEN = "(hidden)"; + + /** Cache for the introspectComponent method */ + private static Map _method_cache = Collections.synchronizedMap(new HashMap(30)); + + protected Container listContainer = null; + protected int hgap; // set in constructor + protected int vgap; // set in constructor + protected int margin; // set in constructor + protected int columns; // set in constructor + protected List fields = null; + protected List labels = null; + protected List fieldSpacers = null; + protected ButtonPanel buttonPanel = null; + protected boolean isEditable = true; + protected String prefix; + protected String postfix; protected int labelAnchor; protected int labelAlign; // protected Component marginStrut = null; - // for action multicasting - protected ActionListener actionListener = null; - -/** -* Constructs an empty InfoPanel. -*/ - public InfoPanel() - { - hgap = 12; // per java l&f guidelines - vgap = 6; // java l&f says 11 - columns = 1; // default columns - margin = 0; // default margin: none - prefix = ""; // default prefix: none - postfix = ":"; // per java l&f guidelines - fields = new ArrayList(); - labels = new ArrayList(); - labelAnchor = GridBagConstraints.NORTHWEST; - // per java l&f guidelines (CENTER is nicer) - labelAlign = SwingConstants.LEFT; - // per java l&f guidelines - - doInitialLayout(); - } + // for action multicasting + protected ActionListener actionListener = null; + + /** + * Constructs an empty InfoPanel. + */ + public InfoPanel() { + hgap = 12; // per java l&f guidelines + vgap = 6; // java l&f says 11 + columns = 1; // default columns + margin = 0; // default margin: none + prefix = ""; // default prefix: none + postfix = ":"; // per java l&f guidelines + fields = new ArrayList(); + labels = new ArrayList(); + labelAnchor = GridBagConstraints.NORTHWEST; + // per java l&f guidelines (CENTER is nicer) + labelAlign = SwingConstants.LEFT; + // per java l&f guidelines + + doInitialLayout(); + } -/** -* Constructs an InfoPanel with the specified labels -* each paired with a blank textfield. -* @param labelArray An Array containing the labels in the -* order in which they should appear from top to bottom. -* A null value produces an empty panel. -*/ - public InfoPanel( String[] labelArray ) - { - this(); - setLabels( labelArray ); - } + /** + * Constructs an InfoPanel with the specified labels each paired with a blank + * textfield. + * + * @param labelArray An Array containing the labels in the order in which they + * should appear from top to bottom. A null value produces an + * empty panel. + */ + public InfoPanel(String[] labelArray) { + this(); + setLabels(labelArray); + } -/** -* Creates a set of labels and empty textfields after first -* clearing all existing components on the panel. -* @param labelArray An Array containing the labels in the order -* in which they should appear from top to bottom. A null -* value will clear the panel. -*/ - public void setLabels( String[] labelArray ) - { - removeAll(); - if ( labelArray == null ) return; // null clears panel - for ( int i = 0; i < labelArray.length; i++ ) - { - addPair( labelArray[i], new JTextField() ); - } - } + /** + * Creates a set of labels and empty textfields after first clearing all + * existing components on the panel. + * + * @param labelArray An Array containing the labels in the order in which they + * should appear from top to bottom. A null value will clear + * the panel. + */ + public void setLabels(String[] labelArray) { + removeAll(); + if (labelArray == null) + return; // null clears panel + for (int i = 0; i < labelArray.length; i++) { + addPair(labelArray[i], new JTextField()); + } + } -/** -* Retrieves the labls for the components on the panel -* in the order in which they are displayed from top WIDTH bottom. -* These are the keys used to reference values or to reference -* the components directly. -* @return An Array of Strings containing the labels. -*/ - public String[] getLabels() - { - int length = fields.size(); - String[] labelArray = new String[ length ]; - for ( int i = 0; i < length; i++ ) - { - labelArray[i] = ((Component)fields.get(i)).getName(); - } - return labelArray; - } - -/** -* Retrieves the constant used to anchor the labels in place. -* The default value is GridBagConstraints.NORTHWEST. -*/ - public int getLabelAnchor() - { + /** + * Retrieves the labls for the components on the panel in the order in which + * they are displayed from top WIDTH bottom. These are the keys used to + * reference values or to reference the components directly. + * + * @return An Array of Strings containing the labels. + */ + public String[] getLabels() { + int length = fields.size(); + String[] labelArray = new String[length]; + for (int i = 0; i < length; i++) { + labelArray[i] = ((Component) fields.get(i)).getName(); + } + return labelArray; + } + + /** + * Retrieves the constant used to anchor the labels in place. The default value + * is GridBagConstraints.NORTHWEST. + */ + public int getLabelAnchor() { return labelAnchor; } -/** -* Sets the constant used to anchor the labels in place -* and reflows the layout. -* @param anAnchorConstant An anchor constant from -* GridBagConstraints. -*/ - public void setLabelAnchor( int anAnchorConstant ) - { - labelAnchor = anAnchorConstant; + /** + * Sets the constant used to anchor the labels in place and reflows the layout. + * + * @param anAnchorConstant An anchor constant from GridBagConstraints. + */ + public void setLabelAnchor(int anAnchorConstant) { + labelAnchor = anAnchorConstant; updateLabels(); } -/** -* Retrieves the constant used to align the labels in place. -* The default value is GridBagConstraints.CENTER. -*/ - public int getLabelAlignment() - { + /** + * Retrieves the constant used to align the labels in place. The default value + * is GridBagConstraints.CENTER. + */ + public int getLabelAlignment() { return labelAlign; } -/** -* Sets the constant used to align the labels in place -* and reflows the layout. -* @param anAlignmentConstant LEFT, CENTER, or RIGHT constants -* from SwingUtilities. -*/ - public void setLabelAlignment( int anAlignmentConstant ) - { - labelAlign = anAlignmentConstant; + /** + * Sets the constant used to align the labels in place and reflows the layout. + * + * @param anAlignmentConstant LEFT, CENTER, or RIGHT constants from + * SwingUtilities. + */ + public void setLabelAlignment(int anAlignmentConstant) { + labelAlign = anAlignmentConstant; updateLabels(); } - -/** -* Factory method for creating panel spacers. -* This implementation returns a JPanel with -* opaque set to false. Override to customize. -*/ - public JPanel createPanel() - { - JPanel result = new JPanel(); - result.setOpaque( false ); - return result; - } -/** -* This method is responsible for the initial layout of the panel. -* All labels and textfields will later added to listContainer. -* This method is responsible for initializing listContainer. -*/ - protected void doInitialLayout() - { - listContainer = createPanel(); - listContainer.setLayout( new BetterGridBagLayout() ); - this.setLayout( new BorderLayout() ); - this.add( listContainer, BorderLayout.NORTH ); + /** + * Factory method for creating panel spacers. This implementation returns a + * JPanel with opaque set to false. Override to customize. + */ + public JPanel createPanel() { + JPanel result = new JPanel(); + result.setOpaque(false); + return result; + } - //listContainer.setBackground( Color.blue ); // useful for testing - //this.setBackground( Color.red ); - } + /** + * This method is responsible for the initial layout of the panel. All labels + * and textfields will later added to listContainer. This method is responsible + * for initializing listContainer. + */ + protected void doInitialLayout() { + listContainer = createPanel(); + listContainer.setLayout(new BetterGridBagLayout()); + this.setLayout(new BorderLayout()); + this.add(listContainer, BorderLayout.NORTH); + + // listContainer.setBackground( Color.blue ); // useful for testing + // this.setBackground( Color.red ); + } -/** -* Changes the horizontal spacing between the label and the components in the panel. -* Note: Assumes listContainer uses a GridBagLayout. -* @param newHgap the new spacing, in pixels. May not be negative. -*/ - public void setHgap( int newHgap ) - { - if ( newHgap < 0 ) return; // may not be negative - this.hgap = newHgap; - updateGaps(); - this.revalidate(); - this.repaint(); + /** + * Changes the horizontal spacing between the label and the components in the + * panel. Note: Assumes listContainer uses a GridBagLayout. + * + * @param newHgap the new spacing, in pixels. May not be negative. + */ + public void setHgap(int newHgap) { + if (newHgap < 0) + return; // may not be negative + this.hgap = newHgap; + updateGaps(); + this.revalidate(); + this.repaint(); - } + } -/** -* Gets the current horizontal spacing between components. -* @return the current horizontal spacing, in pixels. -*/ - public int getHgap() - { - return this.hgap; - } + /** + * Gets the current horizontal spacing between components. + * + * @return the current horizontal spacing, in pixels. + */ + public int getHgap() { + return this.hgap; + } -/** -* Changes the vertical spacing between components in the panel. -* Note: Assumes listContainer uses a GridBagLayout. -* @param newVgap the new spacing, in pixels. May not be negative. -*/ - public void setVgap( int newVgap ) - { - if ( newVgap < 0 ) return; // may not be negative - this.vgap = newVgap; - updateGaps(); - this.revalidate(); - this.repaint(); + /** + * Changes the vertical spacing between components in the panel. Note: Assumes + * listContainer uses a GridBagLayout. + * + * @param newVgap the new spacing, in pixels. May not be negative. + */ + public void setVgap(int newVgap) { + if (newVgap < 0) + return; // may not be negative + this.vgap = newVgap; + updateGaps(); + this.revalidate(); + this.repaint(); - } + } -/** -* Gets the current vertical spacing between components. -* @return the current vertical spacing, in pixels. -*/ - public int getVgap() - { - return this.vgap; - } + /** + * Gets the current vertical spacing between components. + * + * @return the current vertical spacing, in pixels. + */ + public int getVgap() { + return this.vgap; + } -/** -* Sets the minimum width for the labels column. -* This left margin will grow if one of the labels -* is wider than this value. -* Note: assumes GridBagLayout. -* @param newMargin the new minimum margin in pixels. May not be negative. -*/ - public void setMargin( int newMargin ) - { - if ( newMargin < 0 ) return; // may not be negative - this.margin = newMargin; - - if ( listContainer.getLayout() instanceof GridBagLayout ) - { - GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout(); - GridBagConstraints constraints = null; - Component c = null; - int count = listContainer.getComponentCount(); - for ( int i = 0; i < count; i++ ) - { - c = listContainer.getComponent( i ); - constraints = gridBag.getConstraints( c ); - if ( constraints.gridy == 0 && constraints.gridx % 2 == 0 ) - { // if this is a label spacer - // replace it with an appropriately sized box - listContainer.remove( c ); - listContainer.add( Box.createHorizontalStrut( this.margin ), constraints ); - } - } - } - - this.revalidate(); - this.repaint(); - - } + /** + * Sets the minimum width for the labels column. This left margin will grow if + * one of the labels is wider than this value. Note: assumes GridBagLayout. + * + * @param newMargin the new minimum margin in pixels. May not be negative. + */ + public void setMargin(int newMargin) { + if (newMargin < 0) + return; // may not be negative + this.margin = newMargin; + + if (listContainer.getLayout() instanceof GridBagLayout) { + GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout(); + GridBagConstraints constraints = null; + Component c = null; + int count = listContainer.getComponentCount(); + for (int i = 0; i < count; i++) { + c = listContainer.getComponent(i); + constraints = gridBag.getConstraints(c); + if (constraints.gridy == 0 && constraints.gridx % 2 == 0) { // if this is a label spacer + // replace it with an appropriately sized + // box + listContainer.remove(c); + listContainer.add(Box.createHorizontalStrut(this.margin), constraints); + } + } + } -/** -* Gets the current minimum margin for the labels column. -* @return the current minimum margin in pixels. -*/ - public int getMargin() - { - return this.margin; - } + this.revalidate(); + this.repaint(); -/** -* Sets the number of columns for the panel. -* Label/Component pairs will start from the top left -* and fill in to the right before wrapping to the -* next row. The default number of columns is one. -* Note: assumes GridBagLayout. -* @param newColumns the new number of columns. May not be less than one. -*/ - public void setColumns( int newColumns ) - { - if ( newColumns < 1 ) return; // may not be less than one. - int oldColumns = this.columns; - this.columns = newColumns; - - if ( listContainer.getLayout() instanceof GridBagLayout ) - { - GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout(); - int count = listContainer.getComponentCount(); - Component[] components = listContainer.getComponents(); - GridBagConstraints[] constraints = new GridBagConstraints[ components.length ]; - for ( int i = 0; i < components.length; i++ ) - { - constraints[i] = gridBag.getConstraints( components[i] ); - } - listContainer.removeAll(); - for ( int i = 0; i < components.length; i++ ) - { - if ( constraints[i].gridy != 0 ) - { // ignore first row which is reserved for spacers. - - // translate component to new position - // (columns*2 accounts for two grid columns for one "actual" column) - int index = ( constraints[i].gridy - 1 ) * oldColumns*2 + constraints[i].gridx; - constraints[i].gridy = ( index / (newColumns*2) ) + 1; - constraints[i].gridx = index % (newColumns*2) ; - listContainer.add( components[i], constraints[i] ); - } - } - createSpacers(); // replace the spacers - updateGaps(); - } - - this.revalidate(); - this.repaint(); - - } + } -/** -* Sets the vertical weight used for determining how to distribute additional -* vertical space in the component. -* @param aComponent Key that exists in the layout. -* @return weighty The weight of the component, or -1.0 if not found. -*/ - public double getVerticalWeightForKey( String key ) - { - Container c = getCompositeComponentForKey( key ); - if ( c == null ) return -1.0; - if ( ! ( listContainer.getLayout() instanceof GridBagLayout ) ) return -1.0; - GridBagLayout layout = (GridBagLayout) listContainer.getLayout(); - GridBagConstraints gbc = layout.getConstraints( c ); - return gbc.weighty; - } - -/** -* Sets the vertical weight used for determining how to distribute additional -* vertical space in the component. By default, all weights are zero, so each -* component gets its preferred height. If any weights are specified, then -* additional space is allocated to those components proportionately. -* @param aComponent Key that exists in the layout. -* @param weighty The new weight. -*/ - public void setVerticalWeightForKey( String key, double weighty ) - { - Container c = getCompositeComponentForKey( key ); - if ( c == null ) return; - if ( ! ( listContainer.getLayout() instanceof GridBagLayout ) ) return; - GridBagLayout layout = (GridBagLayout) listContainer.getLayout(); - GridBagConstraints gbc = layout.getConstraints( c ); - gbc.weighty = weighty; - layout.setConstraints( c, gbc ); - // handle adding on-the-fly - updateGaps(); - this.revalidate(); - this.repaint(); - } - -/** -* Gets the current number of columns. -* @return the current number of columns. -*/ - public int getColumns() - { - return this.columns; - } + /** + * Gets the current minimum margin for the labels column. + * + * @return the current minimum margin in pixels. + */ + public int getMargin() { + return this.margin; + } + + /** + * Sets the number of columns for the panel. Label/Component pairs will start + * from the top left and fill in to the right before wrapping to the next row. + * The default number of columns is one. Note: assumes GridBagLayout. + * + * @param newColumns the new number of columns. May not be less than one. + */ + public void setColumns(int newColumns) { + if (newColumns < 1) + return; // may not be less than one. + int oldColumns = this.columns; + this.columns = newColumns; + + if (listContainer.getLayout() instanceof GridBagLayout) { + GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout(); + int count = listContainer.getComponentCount(); + Component[] components = listContainer.getComponents(); + GridBagConstraints[] constraints = new GridBagConstraints[components.length]; + for (int i = 0; i < components.length; i++) { + constraints[i] = gridBag.getConstraints(components[i]); + } + listContainer.removeAll(); + for (int i = 0; i < components.length; i++) { + if (constraints[i].gridy != 0) { // ignore first row which is reserved for spacers. + + // translate component to new position + // (columns*2 accounts for two grid columns for one "actual" column) + int index = (constraints[i].gridy - 1) * oldColumns * 2 + constraints[i].gridx; + constraints[i].gridy = (index / (newColumns * 2)) + 1; + constraints[i].gridx = index % (newColumns * 2); + listContainer.add(components[i], constraints[i]); + } + } + createSpacers(); // replace the spacers + updateGaps(); + } + + this.revalidate(); + this.repaint(); -/** -* Appends a label containing a key and the specified component -* to the bottom of the panel. Any registered action listeners -* will receive action events from the component - the key corresponding -* to the component will be used as the action command. -* @param key A string that will be displayed in a label, preferrably unique. -* @param component A component that will be placed next to the label. -* If null, a blank JPanel will be used. -*/ - public void addPair( String key, Component component ) - { - addRow( key, new Component[] { component } ); } - -/** -* Appends a label containing a key and the specified component -* to the bottom of the panel. Any registered action listeners -* will receive action events from the component - the key corresponding -* to the component will be used as the action command. -* @param key A string that will be displayed in a label, preferrably unique. -* @param component A component that will be placed next to the label. -* If null, a blank JPanel will appear. -*/ - public void addRow( String key, Component component ) - { - addRow( key, new Component[] { component } ); + + /** + * Sets the vertical weight used for determining how to distribute additional + * vertical space in the component. + * + * @param aComponent Key that exists in the layout. + * @return weighty The weight of the component, or -1.0 if not found. + */ + public double getVerticalWeightForKey(String key) { + Container c = getCompositeComponentForKey(key); + if (c == null) + return -1.0; + if (!(listContainer.getLayout() instanceof GridBagLayout)) + return -1.0; + GridBagLayout layout = (GridBagLayout) listContainer.getLayout(); + GridBagConstraints gbc = layout.getConstraints(c); + return gbc.weighty; } - -/** -* Appends a label containing a key and the specified components -* to the bottom of the panel. Any registered action listeners -* will receive action events from the component - the key corresponding -* to the component will be used as the action command. -* @param key A string that will be displayed in a label, preferrably unique. -* @param components An array of components that will be placed next to the label. -* Any nulls in the list will be replaced with blank JPanels. -*/ - public void addRow( - String key, Component[] components ) - { - addCompositeComponent( key, makeCompositeComponent( key, components ) ); + + /** + * Sets the vertical weight used for determining how to distribute additional + * vertical space in the component. By default, all weights are zero, so each + * component gets its preferred height. If any weights are specified, then + * additional space is allocated to those components proportionately. + * + * @param aComponent Key that exists in the layout. + * @param weighty The new weight. + */ + public void setVerticalWeightForKey(String key, double weighty) { + Container c = getCompositeComponentForKey(key); + if (c == null) + return; + if (!(listContainer.getLayout() instanceof GridBagLayout)) + return; + GridBagLayout layout = (GridBagLayout) listContainer.getLayout(); + GridBagConstraints gbc = layout.getConstraints(c); + gbc.weighty = weighty; + layout.setConstraints(c, gbc); + // handle adding on-the-fly + updateGaps(); + this.revalidate(); + this.repaint(); } -/** -* Appends a label containing a key and the specified components -* to the bottom of the panel. Any registered action listeners -* will receive action events from the components - the key corresponding -* to the component will be used as the action command. -* @param key A string that will be displayed in a label, preferrably unique. -* @param west A component that will appear to the left of the other components, -* as wide as its preferred width and as tall as the tallest of the other components. -* A null will be replaced with a blank JPanel. -* @param center A component that will appear between the other components, -* taking up available space. -* A null will be replaced with a blank JPanel. -* @param east A component that will appear to the right of the other components, -* as wide as its preferred width and as tall as the tallest of the other components. -* A null will be replaced with a blank JPanel. -*/ - public void addRow( - String key, Component west, Component center, Component east ) - { - addCompositeComponent( key, - makeCompositeComponent( key, - west, center, east ) ); + /** + * Gets the current number of columns. + * + * @return the current number of columns. + */ + public int getColumns() { + return this.columns; } -/** -* Appends a label containing a key and the specified components -* to the bottom of the panel. Any registered action listeners -* will receive action events from the components - the key corresponding -* to the component will be used as the action command. -* @param key A string that will be displayed in a label, preferrably unique. -* @param west A component that will appear to the left of the other components, -* as wide as its preferred width and as tall as the tallest of the other components. -* A null will be replaced with a blank JPanel. -* @param north A component that will appear above all the other components, -* as tall as its preferred height and as wide as the info panel itself. -* @param center A component that will appear between the other components, -* taking up available space. A null will be replaced with a blank JPanel. -* @param south A component that will appear below all the other components, -* as tall as its preferred height and as wide as the info panel itself. -* @param east A component that will appear to the right of the other components, -* as wide as its preferred width and as tall as the tallest of the other components. -* A null will be replaced with a blank JPanel. -*/ - public void addRow( - String key, Component west, Component north, - Component center, Component south, Component east ) - { - addCompositeComponent( key, - makeCompositeComponent( key, - west, north, center, south, east ) ); + /** + * Appends a label containing a key and the specified component to the bottom of + * the panel. Any registered action listeners will receive action events from + * the component - the key corresponding to the component will be used as the + * action command. + * + * @param key A string that will be displayed in a label, preferrably + * unique. + * @param component A component that will be placed next to the label. If null, + * a blank JPanel will be used. + */ + public void addPair(String key, Component component) { + addRow(key, new Component[] { component }); } -/** -* Produces a container that contains the specified components, -* using GridLayout. Nulls are ignored. -* This implementation returns a JPanel. -*/ - protected Container makeCompositeComponent( - String key, Component[] components ) - { + /** + * Appends a label containing a key and the specified component to the bottom of + * the panel. Any registered action listeners will receive action events from + * the component - the key corresponding to the component will be used as the + * action command. + * + * @param key A string that will be displayed in a label, preferrably + * unique. + * @param component A component that will be placed next to the label. If null, + * a blank JPanel will appear. + */ + public void addRow(String key, Component component) { + addRow(key, new Component[] { component }); + } + + /** + * Appends a label containing a key and the specified components to the bottom + * of the panel. Any registered action listeners will receive action events from + * the component - the key corresponding to the component will be used as the + * action command. + * + * @param key A string that will be displayed in a label, preferrably + * unique. + * @param components An array of components that will be placed next to the + * label. Any nulls in the list will be replaced with blank + * JPanels. + */ + public void addRow(String key, Component[] components) { + addCompositeComponent(key, makeCompositeComponent(key, components)); + } + + /** + * Appends a label containing a key and the specified components to the bottom + * of the panel. Any registered action listeners will receive action events from + * the components - the key corresponding to the component will be used as the + * action command. + * + * @param key A string that will be displayed in a label, preferrably unique. + * @param west A component that will appear to the left of the other + * components, as wide as its preferred width and as tall as the + * tallest of the other components. A null will be replaced with a + * blank JPanel. + * @param center A component that will appear between the other components, + * taking up available space. A null will be replaced with a blank + * JPanel. + * @param east A component that will appear to the right of the other + * components, as wide as its preferred width and as tall as the + * tallest of the other components. A null will be replaced with a + * blank JPanel. + */ + public void addRow(String key, Component west, Component center, Component east) { + addCompositeComponent(key, makeCompositeComponent(key, west, center, east)); + } + + /** + * Appends a label containing a key and the specified components to the bottom + * of the panel. Any registered action listeners will receive action events from + * the components - the key corresponding to the component will be used as the + * action command. + * + * @param key A string that will be displayed in a label, preferrably unique. + * @param west A component that will appear to the left of the other + * components, as wide as its preferred width and as tall as the + * tallest of the other components. A null will be replaced with a + * blank JPanel. + * @param north A component that will appear above all the other components, as + * tall as its preferred height and as wide as the info panel + * itself. + * @param center A component that will appear between the other components, + * taking up available space. A null will be replaced with a blank + * JPanel. + * @param south A component that will appear below all the other components, as + * tall as its preferred height and as wide as the info panel + * itself. + * @param east A component that will appear to the right of the other + * components, as wide as its preferred width and as tall as the + * tallest of the other components. A null will be replaced with a + * blank JPanel. + */ + public void addRow(String key, Component west, Component north, Component center, Component south, Component east) { + addCompositeComponent(key, makeCompositeComponent(key, west, north, center, south, east)); + } + + /** + * Produces a container that contains the specified components, using + * GridLayout. Nulls are ignored. This implementation returns a JPanel. + */ + protected Container makeCompositeComponent(String key, Component[] components) { JPanel panel = createPanel(); - if ( components.length != 0 ) - { - panel.setLayout( new GridLayout( 1, components.length, hgap, vgap ) ); + if (components.length != 0) { + panel.setLayout(new GridLayout(1, components.length, hgap, vgap)); Component c; - for ( int i = 0; i < components.length; i++ ) - { + for (int i = 0; i < components.length; i++) { c = components[i]; - if ( c != null ) - { - introspectComponent( c, key ); - panel.add( c ); + if (c != null) { + introspectComponent(c, key); + panel.add(c); } } } return panel; } -/** -* Produces a container that contains the specified components, -* using BorderLayout. Nulls are ignored. -* This implementation returns a JPanel. -*/ - protected Container makeCompositeComponent( - String key, Component west, Component center, Component east ) - { + /** + * Produces a container that contains the specified components, using + * BorderLayout. Nulls are ignored. This implementation returns a JPanel. + */ + protected Container makeCompositeComponent(String key, Component west, Component center, Component east) { JPanel panel = createPanel(); - panel.setLayout( new BorderLayout( hgap, vgap ) ); + panel.setLayout(new BorderLayout(hgap, vgap)); - if ( west != null ) - { - introspectComponent( west, key ); - panel.add( west, BorderLayout.WEST ); + if (west != null) { + introspectComponent(west, key); + panel.add(west, BorderLayout.WEST); } - - if ( center != null ) - { - introspectComponent( center, key ); - panel.add( center, BorderLayout.CENTER ); + + if (center != null) { + introspectComponent(center, key); + panel.add(center, BorderLayout.CENTER); } - - if ( east != null ) - { - introspectComponent( east, key ); - panel.add( east, BorderLayout.EAST ); + + if (east != null) { + introspectComponent(east, key); + panel.add(east, BorderLayout.EAST); } - + return panel; } -/** -* Produces a container that contains the specified components, -* using BorderLayout. Nulls are ignored. -* This implementation returns a JPanel. -*/ - protected Container makeCompositeComponent( - String key, Component west, Component north, - Component center, Component south, Component east ) - { + /** + * Produces a container that contains the specified components, using + * BorderLayout. Nulls are ignored. This implementation returns a JPanel. + */ + protected Container makeCompositeComponent(String key, Component west, Component north, Component center, + Component south, Component east) { JPanel panel = createPanel(); - panel.setLayout( new BorderLayout( hgap, vgap ) ); + panel.setLayout(new BorderLayout(hgap, vgap)); - if ( west != null ) - { - introspectComponent( west, key ); - panel.add( west, BorderLayout.WEST ); + if (west != null) { + introspectComponent(west, key); + panel.add(west, BorderLayout.WEST); } - - if ( north != null ) - { - introspectComponent( north, key ); - panel.add( north, BorderLayout.WEST ); + + if (north != null) { + introspectComponent(north, key); + panel.add(north, BorderLayout.WEST); } - - if ( center != null ) - { - introspectComponent( center, key ); - panel.add( center, BorderLayout.CENTER ); + + if (center != null) { + introspectComponent(center, key); + panel.add(center, BorderLayout.CENTER); } - - if ( south != null ) - { - introspectComponent( south, key ); - panel.add( south, BorderLayout.CENTER ); + + if (south != null) { + introspectComponent(south, key); + panel.add(south, BorderLayout.CENTER); } - - if ( east != null ) - { - introspectComponent( east, key ); - panel.add( east, BorderLayout.EAST ); + + if (east != null) { + introspectComponent(east, key); + panel.add(east, BorderLayout.EAST); } - + return panel; } -/** -* Override to return a specific component to be used -* as a label. This implementation calls createLabel(). -*/ - protected Component createLabelForKey( String aKey ) - { - return createLabel(); + /** + * Override to return a specific component to be used as a label. This + * implementation calls createLabel(). + */ + protected Component createLabelForKey(String aKey) { + return createLabel(); } -/** -* Provided for backwards compatibility, and called by -* the default implementation of createLabelForKey. -* This implementation returns a JLabel. -*/ - protected JLabel createLabel() - { - return new JLabel(); + /** + * Provided for backwards compatibility, and called by the default + * implementation of createLabelForKey. This implementation returns a JLabel. + */ + protected JLabel createLabel() { + return new JLabel(); } -/** -* Appends a label containing a key and the specified component -* to the bottom of the panel. Any registered action listeners -* will receive action events from the component - the key corresponding -* to the component will be used as the action command. -* @param key A string that will be displayed in a label, preferrably unique. -* @param component A component that will be placed next to the label. -* If null, a stock JTextField will be used. -*/ - protected void addCompositeComponent( String key, Component component ) - { - if ( key == null ) - { - key = ""; - } - Component label = createLabelForKey( key ); - Component field = component; - if ( field == null ) - { - field = new JTextField( 15 ); // default to 15 columns - } - field.setName( key ); // for association and reference - label.setName( key ); // ditto - if ( label instanceof JLabel ) - { - ((JLabel)label).setHorizontalAlignment( labelAlign ); - ((JLabel)label).setLabelFor( field ); // for accessibility - } - if ( "".equals( key ) ) - { - setText( label, "" ); - } - else - { - setText( label, prefix + key + postfix ); - } - field.setEnabled( this.isEditable ); // was: setEditable - - GridBagConstraints gbc = new GridBagConstraints(); - - if ( listContainer.getComponentCount() == 0 ) - { // we've just initialized or called removeAll - createSpacers(); - } - - gbc.gridx = ( fields.size() % this.columns ) * 2; - gbc.gridy = ( fields.size() / this.columns ) + 1; // spacer is at index zero - gbc.weightx = 0.0; - gbc.weighty = 0.0; - gbc.anchor = this.labelAnchor; - gbc.fill = GridBagConstraints.HORIZONTAL; - listContainer.add( label, gbc ); + /** + * Appends a label containing a key and the specified component to the bottom of + * the panel. Any registered action listeners will receive action events from + * the component - the key corresponding to the component will be used as the + * action command. + * + * @param key A string that will be displayed in a label, preferrably + * unique. + * @param component A component that will be placed next to the label. If null, + * a stock JTextField will be used. + */ + protected void addCompositeComponent(String key, Component component) { + if (key == null) { + key = ""; + } + Component label = createLabelForKey(key); + Component field = component; + if (field == null) { + field = new JTextField(15); // default to 15 columns + } + field.setName(key); // for association and reference + label.setName(key); // ditto + if (label instanceof JLabel) { + ((JLabel) label).setHorizontalAlignment(labelAlign); + ((JLabel) label).setLabelFor(field); // for accessibility + } + if ("".equals(key)) { + setText(label, ""); + } else { + setText(label, prefix + key + postfix); + } + field.setEnabled(this.isEditable); // was: setEditable + + GridBagConstraints gbc = new GridBagConstraints(); + + if (listContainer.getComponentCount() == 0) { // we've just initialized or called removeAll + createSpacers(); + } + + gbc.gridx = (fields.size() % this.columns) * 2; + gbc.gridy = (fields.size() / this.columns) + 1; // spacer is at index zero + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.anchor = this.labelAnchor; + gbc.fill = GridBagConstraints.HORIZONTAL; + listContainer.add(label, gbc); gbc.fill = GridBagConstraints.BOTH; - gbc.gridx = gbc.gridx + 1; - //FIXME: components default to the labelAnchor - should be different? - gbc.weightx = 1.0; - gbc.weighty = 0.0; - - listContainer.add( field, gbc ); - - if ( key.equals( HIDDEN ) ) - { // these components are not to be shown - setText( label, " " ); - field.setVisible( false ); - } - - fields.add( field ); // using list not map to allow for duplicate keys - labels.add( label ); // ditto - - // handle adding on-the-fly - updateGaps(); - this.revalidate(); - this.repaint(); - } + gbc.gridx = gbc.gridx + 1; + // FIXME: components default to the labelAnchor - should be different? + gbc.weightx = 1.0; + gbc.weighty = 0.0; -/** -* Introspects a component to set the action command and to add the -* InfoPanel to its list of ActionListeners. -* @param aComponent The Component to be introspected. -* @param aKey The action command to be set. -*/ - protected void introspectComponent( Component aComponent, String aKey ) - { - // try to set properties of whatever component this might be - try { - Method [] methods = - (Method []) _method_cache.get( aComponent.getClass() ); - if (methods == null) { - Class componentClass = aComponent.getClass(); - BeanInfo info = - Introspector.getBeanInfo( componentClass ); - - MethodDescriptor[] descriptors = - info.getMethodDescriptors(); - Method setMethod = null; - Method addMethod = null; - for ( int i = 0; - ((setMethod == null || addMethod == null) && - i < descriptors.length); - i++ ) - { - Method m = descriptors[i].getMethod(); - String name = m.getName (); - if ( setMethod == null && - name.equals( "setActionCommand" ) ) - { - setMethod = m; - } - else if ( addMethod == null && - name.equals( "addActionListener" ) ) - { - addMethod = m; - } - } - - methods = new Method [] {setMethod, addMethod}; - _method_cache.put (componentClass, methods); - } - if (methods [0] != null) { - methods [0].invoke( aComponent, new Object[] { aKey } ); - } - if (methods [1] != null) { - methods [1].invoke( aComponent, new Object[] { this } ); - listenedToComponents.add( aComponent ); - } - } - catch ( Exception exc ) - { // error occured while introspecting... move along. - System.out.println( "InfoPanel.introspectComponent: " + exc ); - } - } - -/** -* Called to populate a label component with the specified text. -* This implementation attempts to call setText(String) on the component. -* Override to customize. -*/ - protected void setText( Component c, String text ) - { - try - { - Method m = c.getClass().getMethod( "setText", new Class[] { String.class } ); - if ( m != null ) - { - m.invoke( c, new Object[] { text } ); - } - } - catch ( Exception exc ) - { - // no such method: ignore - } - } + listContainer.add(field, gbc); -/** -* Creates spacer components on the reserved first grid row -* for each column of labels and fields. -* This allows us to set the margin for those label columns, -* and set the preferred width of the field columns. -* A list containing the field spacers should be assigned to -* the fieldSpacers instance variable. -*/ - private void createSpacers() - { - if ( listContainer.getLayout() instanceof GridBagLayout ) - { - // insert spacers for labels column - GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout(); - GridBagConstraints constraints = new GridBagConstraints(); - constraints.gridy = 0; - constraints.fill = GridBagConstraints.HORIZONTAL; - - fieldSpacers = new LinkedList(); - Component fieldSpacer; - for ( int i = 0; i < this.columns; i++ ) - { - constraints.gridx = i * 2; - listContainer.add( Box.createHorizontalStrut( this.margin ), constraints ); - - constraints.gridx = i * 2 + 1; - fieldSpacer = Box.createHorizontalStrut( 0 ); - fieldSpacers.add( fieldSpacer ); - listContainer.add( fieldSpacer, constraints ); - } - } - } + if (key.equals(HIDDEN)) { // these components are not to be shown + setText(label, " "); + field.setVisible(false); + } -/** -* Updates the insets for all components. -*/ - protected void updateGaps() - { - if ( listContainer.getLayout() instanceof GridBagLayout ) - { - GridBagLayout layout = (GridBagLayout) listContainer.getLayout(); - Component c = null; - GridBagConstraints gbc = null; - double totalWeightY = 0.0; - int count = listContainer.getComponentCount(); - int i; - for ( i = 0; i < count; i++ ) - { - c = listContainer.getComponent( i ); - gbc = layout.getConstraints( c ); - totalWeightY += gbc.weighty; - if ( (gbc.gridx + 1) % ( this.columns * 2 ) == 0 ) - { // if last component in row - gbc.insets = new Insets( 0, 0, this.vgap, 0 ); - } - else - { - if ( gbc.gridx % 2 == 0 ) - { // is a label column - NOTE: uses eleven pixels before component, per l&f guide - gbc.insets = new Insets( 0, 0, this.vgap, 11 ); - } - else - { // is a component column - if ( gbc.gridy != 0 ) - { - if ( c instanceof JPanel ) ((JPanel)c).setPreferredSize( null ); - gbc.insets = new Insets( 0, 0, this.vgap, this.hgap ); - } - } - } - layout.setConstraints( c, gbc ); - } - - //hack: gridbag clumps components in center if weighty is zero - // if sum of weighty is zero, top-justify the list container - this.remove( listContainer ); - if ( totalWeightY == 0.0 ) - { - this.add( listContainer, BorderLayout.NORTH ); - } - else // put list container in center so it will grow - { - this.add( listContainer, BorderLayout.CENTER ); - } - } - } + fields.add(field); // using list not map to allow for duplicate keys + labels.add(label); // ditto -/** -* Updates the label alignment. -*/ - protected void updateLabels() - { - if ( listContainer.getLayout() instanceof GridBagLayout ) - { - GridBagLayout layout = (GridBagLayout) listContainer.getLayout(); - Component c = null; - GridBagConstraints gbc = null; + // handle adding on-the-fly + updateGaps(); + this.revalidate(); + this.repaint(); + } + + /** + * Introspects a component to set the action command and to add the InfoPanel to + * its list of ActionListeners. + * + * @param aComponent The Component to be introspected. + * @param aKey The action command to be set. + */ + protected void introspectComponent(Component aComponent, String aKey) { + // try to set properties of whatever component this might be + try { + Method[] methods = (Method[]) _method_cache.get(aComponent.getClass()); + if (methods == null) { + Class componentClass = aComponent.getClass(); + BeanInfo info = Introspector.getBeanInfo(componentClass); + + MethodDescriptor[] descriptors = info.getMethodDescriptors(); + Method setMethod = null; + Method addMethod = null; + for (int i = 0; ((setMethod == null || addMethod == null) && i < descriptors.length); i++) { + Method m = descriptors[i].getMethod(); + String name = m.getName(); + if (setMethod == null && name.equals("setActionCommand")) { + setMethod = m; + } else if (addMethod == null && name.equals("addActionListener")) { + addMethod = m; + } + } + + methods = new Method[] { setMethod, addMethod }; + _method_cache.put(componentClass, methods); + } + if (methods[0] != null) { + methods[0].invoke(aComponent, new Object[] { aKey }); + } + if (methods[1] != null) { + methods[1].invoke(aComponent, new Object[] { this }); + listenedToComponents.add(aComponent); + } + } catch (Exception exc) { // error occured while introspecting... move along. + System.out.println("InfoPanel.introspectComponent: " + exc); + } + } + + /** + * Called to populate a label component with the specified text. This + * implementation attempts to call setText(String) on the component. Override to + * customize. + */ + protected void setText(Component c, String text) { + try { + Method m = c.getClass().getMethod("setText", new Class[] { String.class }); + if (m != null) { + m.invoke(c, new Object[] { text }); + } + } catch (Exception exc) { + // no such method: ignore + } + } + + /** + * Creates spacer components on the reserved first grid row for each column of + * labels and fields. This allows us to set the margin for those label columns, + * and set the preferred width of the field columns. A list containing the field + * spacers should be assigned to the fieldSpacers instance variable. + */ + private void createSpacers() { + if (listContainer.getLayout() instanceof GridBagLayout) { + // insert spacers for labels column + GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.gridy = 0; + constraints.fill = GridBagConstraints.HORIZONTAL; + + fieldSpacers = new LinkedList(); + Component fieldSpacer; + for (int i = 0; i < this.columns; i++) { + constraints.gridx = i * 2; + listContainer.add(Box.createHorizontalStrut(this.margin), constraints); + + constraints.gridx = i * 2 + 1; + fieldSpacer = Box.createHorizontalStrut(0); + fieldSpacers.add(fieldSpacer); + listContainer.add(fieldSpacer, constraints); + } + } + } + + /** + * Updates the insets for all components. + */ + protected void updateGaps() { + if (listContainer.getLayout() instanceof GridBagLayout) { + GridBagLayout layout = (GridBagLayout) listContainer.getLayout(); + Component c = null; + GridBagConstraints gbc = null; + double totalWeightY = 0.0; + int count = listContainer.getComponentCount(); + int i; + for (i = 0; i < count; i++) { + c = listContainer.getComponent(i); + gbc = layout.getConstraints(c); + totalWeightY += gbc.weighty; + if ((gbc.gridx + 1) % (this.columns * 2) == 0) { // if last component in row + gbc.insets = new Insets(0, 0, this.vgap, 0); + } else { + if (gbc.gridx % 2 == 0) { // is a label column - NOTE: uses eleven pixels before component, per l&f + // guide + gbc.insets = new Insets(0, 0, this.vgap, 11); + } else { // is a component column + if (gbc.gridy != 0) { + if (c instanceof JPanel) + ((JPanel) c).setPreferredSize(null); + gbc.insets = new Insets(0, 0, this.vgap, this.hgap); + } + } + } + layout.setConstraints(c, gbc); + } + + // hack: gridbag clumps components in center if weighty is zero + // if sum of weighty is zero, top-justify the list container + this.remove(listContainer); + if (totalWeightY == 0.0) { + this.add(listContainer, BorderLayout.NORTH); + } else // put list container in center so it will grow + { + this.add(listContainer, BorderLayout.CENTER); + } + } + } + + /** + * Updates the label alignment. + */ + protected void updateLabels() { + if (listContainer.getLayout() instanceof GridBagLayout) { + GridBagLayout layout = (GridBagLayout) listContainer.getLayout(); + Component c = null; + GridBagConstraints gbc = null; Iterator it = labels.iterator(); - while ( it.hasNext() ) - { - c = (Component) it.next(); - if ( c instanceof JLabel ) - { - ((JLabel)c).setHorizontalAlignment( labelAlign ); - } - gbc = layout.getConstraints( c ); - gbc.anchor = this.labelAnchor; - layout.setConstraints( c, gbc ); - } - } - } + while (it.hasNext()) { + c = (Component) it.next(); + if (c instanceof JLabel) { + ((JLabel) c).setHorizontalAlignment(labelAlign); + } + gbc = layout.getConstraints(c); + gbc.anchor = this.labelAnchor; + layout.setConstraints(c, gbc); + } + } + } -/** -* Convenience method that uses a stock JTextField. -* @param key A string that will be displayed in a label, preferrably unique. -* @param value A string that will be displayed in a textfield. -*/ - public void addPair( String key, String value ) - { - addPair( key, value, null ); - } + /** + * Convenience method that uses a stock JTextField. + * + * @param key A string that will be displayed in a label, preferrably unique. + * @param value A string that will be displayed in a textfield. + */ + public void addPair(String key, String value) { + addPair(key, value, null); + } -/** -* Convenience method that uses the specified JTextField or subclass -* and sets it to the specified value. -* @param key A string that will be displayed in a label, preferrably unique. -* @param value A string that will be displayed in a textfield. -* @param textField A JTextField or subclass that will be used to display the value. -* If null, a stock JTextField will be used. -*/ - public void addPair( String key, String value, JTextField textField ) - { - if ( value == null ) - { - value = ""; - } - JTextField field = textField; - if ( field == null ) - { - field = new JTextField( 15 ); // default to 15 columns - } - else - { - field = textField; - } - field.setText( value ); - - addPair( key, (Component) field ); - } + /** + * Convenience method that uses the specified JTextField or subclass and sets it + * to the specified value. + * + * @param key A string that will be displayed in a label, preferrably + * unique. + * @param value A string that will be displayed in a textfield. + * @param textField A JTextField or subclass that will be used to display the + * value. If null, a stock JTextField will be used. + */ + public void addPair(String key, String value, JTextField textField) { + if (value == null) { + value = ""; + } + JTextField field = textField; + if (field == null) { + field = new JTextField(15); // default to 15 columns + } else { + field = textField; + } + field.setText(value); -/** -* Removes all components from the list. Buttons, if any, -* will remain unchanged - use setButtons( null ) to remove -* them. NOTE: does not call super.removeAll(). -*/ - public void removeAll() - { - Object component; - Method method; - Class[] paramClasses = new Class[] { ActionListener.class }; - Object[] paramObjects = new Object[] { this }; - - Iterator iterator = listenedToComponents.iterator(); - while ( iterator.hasNext() ) - { - component = iterator.next(); - try - { - method = component.getClass().getMethod( "removeActionListener", paramClasses ); - if ( method != null ) - { - method.invoke( component, paramObjects ); - } - } - catch ( Exception exception ) - { - // No removeActionListener() method, move along. - } - } - - listenedToComponents.clear(); - - listContainer.removeAll(); - fields.clear(); - labels.clear(); - this.revalidate(); - this.repaint(); - - //FIXME: It is very confusing that this - // implementation does not call super.removeAll(). - } + addPair(key, (Component) field); + } -/** -* Adds one or buttons to the bottom of the panel with the specified labels -* from left to right. Any action listeners will receive action events -* from clicks on these buttons - the supplied label will be used as the action command. -* @param buttons A string array containing the strings to be used for the button labels -* and action commands. A null value will remove the button panel. -* @see ButtonPanel -*/ - public void setButtons( String[] buttons ) - { - if ( buttonPanel == null ) - { - buttonPanel = new ButtonPanel(); - buttonPanel.setInsets( new Insets( 6, 0, 0, 0 ) ); - // button panel has a 11-pixel top inset - // and java l&f guide says 17-pixels before command buttons - buttonPanel.addActionListener( this ); - this.add( buttonPanel, BorderLayout.SOUTH ); - } - if ( buttons == null ) - { - this.remove( buttonPanel ); - buttonPanel = null; - } - else - { - buttonPanel.setLabels( buttons ); - } - - this.revalidate(); - this.repaint(); - } - protected Collection listenedToComponents = new LinkedList(); + /** + * Removes all components from the list. Buttons, if any, will remain unchanged + * - use setButtons( null ) to remove them. NOTE: does not call + * super.removeAll(). + */ + public void removeAll() { + Object component; + Method method; + Class[] paramClasses = new Class[] { ActionListener.class }; + Object[] paramObjects = new Object[] { this }; + + Iterator iterator = listenedToComponents.iterator(); + while (iterator.hasNext()) { + component = iterator.next(); + try { + method = component.getClass().getMethod("removeActionListener", paramClasses); + if (method != null) { + method.invoke(component, paramObjects); + } + } catch (Exception exception) { + // No removeActionListener() method, move along. + } + } -/** -* Retrieves the names of the buttons that are displayed, if any. -* @return A string array containing the strings used for the button labels -* and action commands, or null if no buttons have been created. -* @see ButtonPanel -*/ - public String[] getButtons() - { - if ( buttonPanel == null ) - { - return null; // none created - } + listenedToComponents.clear(); - return buttonPanel.getLabels(); - } + listContainer.removeAll(); + fields.clear(); + labels.clear(); + this.revalidate(); + this.repaint(); -/** -* Retrieves the actual button panel, if any. -* @return A button panel, or null if none has been created. -* @see ButtonPanel -*/ - public ButtonPanel getButtonPanel() - { - return buttonPanel; - } + // FIXME: It is very confusing that this + // implementation does not call super.removeAll(). + } + /** + * Adds one or buttons to the bottom of the panel with the specified labels from + * left to right. Any action listeners will receive action events from clicks on + * these buttons - the supplied label will be used as the action command. + * + * @param buttons A string array containing the strings to be used for the + * button labels and action commands. A null value will remove + * the button panel. + * @see ButtonPanel + */ + public void setButtons(String[] buttons) { + if (buttonPanel == null) { + buttonPanel = new ButtonPanel(); + buttonPanel.setInsets(new Insets(6, 0, 0, 0)); + // button panel has a 11-pixel top inset + // and java l&f guide says 17-pixels before command buttons + buttonPanel.addActionListener(this); + this.add(buttonPanel, BorderLayout.SOUTH); + } + if (buttons == null) { + this.remove(buttonPanel); + buttonPanel = null; + } else { + buttonPanel.setLabels(buttons); + } -/** -* Sets whether the values displayed in the panel should be editable. Defaults to true. -* @param isEditable Whether the values should be editable. -*/ - public void setEditable( boolean isEditable ) - { - this.isEditable = isEditable; - Iterator enumeration = fields.iterator(); - while ( enumeration.hasNext() ) - { - ( (Component) enumeration.next() ).setEnabled( isEditable ); - } - } + this.revalidate(); + this.repaint(); + } -/** -* Gets whether the values displayed in the panel are editable. -* @return Whether the values should be editable. -*/ - public boolean isEditable() - { - return this.isEditable; - } + protected Collection listenedToComponents = new LinkedList(); + + /** + * Retrieves the names of the buttons that are displayed, if any. + * + * @return A string array containing the strings used for the button labels and + * action commands, or null if no buttons have been created. + * @see ButtonPanel + */ + public String[] getButtons() { + if (buttonPanel == null) { + return null; // none created + } -/** -* Sets the field associated with the key to the specified value. -* Note: If the component does not respond to setText() or setString() -* or setValue() the value will not be set. JTextFields and the like will work. -* @param key A string representing the key associated with the field. Nulls are converted to an empty string. -* @param value A object to be displayed in the specified field. Nulls are converted to an empty string. -*/ - public void setValueForKey( String key, Object value ) - { - setValueForKey( key, value, 0 ); + return buttonPanel.getLabels(); } - -/** -* Sets the field associated with the key to the specified value. -* Note: If the component does not respond to setText() or setString() -* or setValue() the value will not be set. JTextFields and the like will work. -* @param key A string representing the key associated with the field. Nulls are converted to an empty string. -* @param value A object to be displayed in the specified field. Nulls are converted to an empty string. -*/ - public void setValueForKey( String key, Object value, int index ) - { - if ( key == null ) - { - key = ""; - } - - Container field = null; - for ( int i = 0; i < fields.size(); i++ ) - { - field = (Container) fields.get(i); - if ( key.equals( field.getName() ) ) - { - setValueForIndex( index, i, value ); + + /** + * Retrieves the actual button panel, if any. + * + * @return A button panel, or null if none has been created. + * @see ButtonPanel + */ + public ButtonPanel getButtonPanel() { + return buttonPanel; + } + + /** + * Sets whether the values displayed in the panel should be editable. Defaults + * to true. + * + * @param isEditable Whether the values should be editable. + */ + public void setEditable(boolean isEditable) { + this.isEditable = isEditable; + Iterator enumeration = fields.iterator(); + while (enumeration.hasNext()) { + ((Component) enumeration.next()).setEnabled(isEditable); + } + } + + /** + * Gets whether the values displayed in the panel are editable. + * + * @return Whether the values should be editable. + */ + public boolean isEditable() { + return this.isEditable; + } + + /** + * Sets the field associated with the key to the specified value. Note: If the + * component does not respond to setText() or setString() or setValue() the + * value will not be set. JTextFields and the like will work. + * + * @param key A string representing the key associated with the field. Nulls + * are converted to an empty string. + * @param value A object to be displayed in the specified field. Nulls are + * converted to an empty string. + */ + public void setValueForKey(String key, Object value) { + setValueForKey(key, value, 0); + } + + /** + * Sets the field associated with the key to the specified value. Note: If the + * component does not respond to setText() or setString() or setValue() the + * value will not be set. JTextFields and the like will work. + * + * @param key A string representing the key associated with the field. Nulls + * are converted to an empty string. + * @param value A object to be displayed in the specified field. Nulls are + * converted to an empty string. + */ + public void setValueForKey(String key, Object value, int index) { + if (key == null) { + key = ""; + } + + Container field = null; + for (int i = 0; i < fields.size(); i++) { + field = (Container) fields.get(i); + if (key.equals(field.getName())) { + setValueForIndex(index, i, value); return; - } + } } - // else not found - ignore - } - -/** -* Sets the first field at the specified row index to the specified value. -* Note: If the component does not respond to setText() or setString() -* or setValue() the value will not be set. JTextFields and the like will work. -* @param row The row index of the component. -* @param value A object to be displayed in the specified field. -* Nulls are converted to an empty string. -*/ - public void setValueForIndex( int row, Object value ) - { - setValueForIndex( row, 0, value ); + // else not found - ignore } - -/** -* Sets the field at the specified row index and column index to the specified value. -* Note: If the component does not respond to setText() or setString() -* or setValue() the value will not be set. JTextFields and the like will work. -* @param row The row index of the component. -* @param index The column index of the component. -* @param value A object to be displayed in the specified field. -* Nulls are converted to an empty string. -*/ - public void setValueForIndex( int row, int col, Object value ) - { - Container field = (Container) fields.get( row ); - Component c = field.getComponent( col ); - setValueForComponent( c, value ); + + /** + * Sets the first field at the specified row index to the specified value. Note: + * If the component does not respond to setText() or setString() or setValue() + * the value will not be set. JTextFields and the like will work. + * + * @param row The row index of the component. + * @param value A object to be displayed in the specified field. Nulls are + * converted to an empty string. + */ + public void setValueForIndex(int row, Object value) { + setValueForIndex(row, 0, value); } - + /** + * Sets the field at the specified row index and column index to the specified + * value. Note: If the component does not respond to setText() or setString() or + * setValue() the value will not be set. JTextFields and the like will work. + * + * @param row The row index of the component. + * @param index The column index of the component. + * @param value A object to be displayed in the specified field. Nulls are + * converted to an empty string. + */ + public void setValueForIndex(int row, int col, Object value) { + Container field = (Container) fields.get(row); + Component c = field.getComponent(col); + setValueForComponent(c, value); + } -/** -* Sets the value in the field at the specified index. -* Note: If the component does not respond to setText() or setString() -* or setValue() this method will return null. JTextFields and the like will work. -* @param A valid index. -* @param value A object to be displayed in the specified field. -*/ - protected void setValueForComponent( Component aComponent, Object value ) - { - // try to set a text or string property - try { - BeanInfo info = Introspector.getBeanInfo( aComponent.getClass() ); - MethodDescriptor[] methods = info.getMethodDescriptors(); - for ( int i = 0; i < methods.length; i++ ) - { - Method m = methods[i].getMethod(); - Class[] paramTypes = m.getParameterTypes(); - if ( paramTypes.length == 1 ) - { - if ( m.getName().equals( "setText" ) ) - { - if ( paramTypes[0].getName().equals( String.class.getName() ) ) - { - m.invoke( aComponent, new Object[] { value } ); - } - } - if ( m.getName().equals( "setString" ) ) - { - if ( paramTypes[0].getName().equals( String.class.getName() ) ) - { - m.invoke( aComponent, new Object[] { value } ); - } - } - if ( m.getName().equals( "setValue" ) ) - { - if ( paramTypes[0].getName().equals( Object.class.getName() ) ) - { - m.invoke( aComponent, new Object[] { value } ); - } - } - } - } - } - catch ( Exception exc ) - { // error occured while introspecting... move along. - // FIXME: should log error in ErrorManager - System.out.println( "InfoPanel.setValueForComponent: " + exc ); - } - } + /** + * Sets the value in the field at the specified index. Note: If the component + * does not respond to setText() or setString() or setValue() this method will + * return null. JTextFields and the like will work. + * + * @param A valid index. + * @param value A object to be displayed in the specified field. + */ + protected void setValueForComponent(Component aComponent, Object value) { + // try to set a text or string property + try { + BeanInfo info = Introspector.getBeanInfo(aComponent.getClass()); + MethodDescriptor[] methods = info.getMethodDescriptors(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i].getMethod(); + Class[] paramTypes = m.getParameterTypes(); + if (paramTypes.length == 1) { + if (m.getName().equals("setText")) { + if (paramTypes[0].getName().equals(String.class.getName())) { + m.invoke(aComponent, new Object[] { value }); + } + } + if (m.getName().equals("setString")) { + if (paramTypes[0].getName().equals(String.class.getName())) { + m.invoke(aComponent, new Object[] { value }); + } + } + if (m.getName().equals("setValue")) { + if (paramTypes[0].getName().equals(Object.class.getName())) { + m.invoke(aComponent, new Object[] { value }); + } + } + } + } + } catch (Exception exc) { // error occured while introspecting... move along. + // FIXME: should log error in ErrorManager + System.out.println("InfoPanel.setValueForComponent: " + exc); + } + } -/** -* Gets the value in the field at the specified index. -* Note: If the component does not respond to getText() or getString() -* or getSelectedItem() this method will return null. JTextFields and the like will work. -* @param A valid index. -* @return An object representing the value in the field at the specified index, -* or null if the component does not have a text property or if the index is out of bounds. -*/ - public Object getValueForIndex( int anIndex ) - { - return getValueForIndex( anIndex, 0 ); - } + /** + * Gets the value in the field at the specified index. Note: If the component + * does not respond to getText() or getString() or getSelectedItem() this method + * will return null. JTextFields and the like will work. + * + * @param A valid index. + * @return An object representing the value in the field at the specified index, + * or null if the component does not have a text property or if the + * index is out of bounds. + */ + public Object getValueForIndex(int anIndex) { + return getValueForIndex(anIndex, 0); + } -/** -* Gets the value in the field at the specified row and column. -* Note: If the component does not respond to getText() or getString() -* or getSelectedItem() this method will return null. JTextFields and the like will work. -* @param A valid index. -* @return An object representing the value in the field at the specified index, -* or null if the component does not have a text property or if the index is out of bounds. -*/ - public Object getValueForIndex( int row, int col ) - { - if ( ( row >= fields.size() ) || ( row < 0 ) ) - { // out of bounds - return null; - } - - Container field = (Container) fields.get( row ); - Component c = field.getComponent( col ); - return getValueForComponent( c ); - } + /** + * Gets the value in the field at the specified row and column. Note: If the + * component does not respond to getText() or getString() or getSelectedItem() + * this method will return null. JTextFields and the like will work. + * + * @param A valid index. + * @return An object representing the value in the field at the specified index, + * or null if the component does not have a text property or if the + * index is out of bounds. + */ + public Object getValueForIndex(int row, int col) { + if ((row >= fields.size()) || (row < 0)) { // out of bounds + return null; + } -/** -* Gets the value in the field associated with the key. -* Note: If the component does not respond to getText() or getString() -* or getSelectedItem() this method will return null. JTextFields and the like will work. -* @param key An string representing the key associated with the field. Nulls are converted to an empty string. -* @return An object representing the value in the field associated with the key, -* or null if the key does not exist or if the component does not have a text property. -*/ - public Object getValueForKey( String key ) - { - return getValueForKey( key, 0 ); + Container field = (Container) fields.get(row); + Component c = field.getComponent(col); + return getValueForComponent(c); } -/** -* Gets the value in the field associated with the key. -* Note: If the component does not respond to getText() or getString() -* or getSelectedItem() this method will return null. JTextFields and the like will work. -* @param key An string representing the key associated with the field. Nulls are converted to an empty string. -* @return An object representing the value in the field associated with the key, -* or null if the key does not exist or if the component does not have a text property. -*/ - public Object getValueForKey( String key, int index ) - { - if ( key == null ) - { - key = ""; - } - - Container field = null; - Iterator enumeration = fields.iterator(); - while ( enumeration.hasNext() ) - { // finds first value in list with specified key - field = (Container) enumeration.next(); - if ( key.equals( field.getName() ) ) - { - Component c = field.getComponent( index ); - if ( c != null ) - { - return getValueForComponent( c ); + /** + * Gets the value in the field associated with the key. Note: If the component + * does not respond to getText() or getString() or getSelectedItem() this method + * will return null. JTextFields and the like will work. + * + * @param key An string representing the key associated with the field. Nulls + * are converted to an empty string. + * @return An object representing the value in the field associated with the + * key, or null if the key does not exist or if the component does not + * have a text property. + */ + public Object getValueForKey(String key) { + return getValueForKey(key, 0); + } + + /** + * Gets the value in the field associated with the key. Note: If the component + * does not respond to getText() or getString() or getSelectedItem() this method + * will return null. JTextFields and the like will work. + * + * @param key An string representing the key associated with the field. Nulls + * are converted to an empty string. + * @return An object representing the value in the field associated with the + * key, or null if the key does not exist or if the component does not + * have a text property. + */ + public Object getValueForKey(String key, int index) { + if (key == null) { + key = ""; + } + + Container field = null; + Iterator enumeration = fields.iterator(); + while (enumeration.hasNext()) { // finds first value in list with specified key + field = (Container) enumeration.next(); + if (key.equals(field.getName())) { + Component c = field.getComponent(index); + if (c != null) { + return getValueForComponent(c); } - } - } - // else not found - return null; - } + } + } + // else not found + return null; + } -/** -* Gets the value in the specified component. -* Note: If the component does not respond to getText() or getString() -* or getSelectedItem() this method will return null. JTextFields and the like will work. -* @param aComponent The specified component. -* @return An object representing the value in the component. -* or null if the component does not have a text property. -*/ - protected Object getValueForComponent( Component aComponent ) - { - // try to get a text or string property - try - { - BeanInfo info = Introspector.getBeanInfo( aComponent.getClass() ); - MethodDescriptor[] methods = info.getMethodDescriptors(); - for ( int i = 0; i < methods.length; i++ ) - { - Method m = methods[i].getMethod(); - Class[] paramTypes = m.getParameterTypes(); - if ( m.getName().equals( "getText" ) ) - { - if ( paramTypes.length == 0 ) - { - return m.invoke( aComponent, new Object[] {} ); - } - } - if ( m.getName().equals( "getString" ) ) - { - if ( paramTypes.length == 0 ) - { - return m.invoke( aComponent, new Object[] {} ); - } - } - if ( m.getName().equals( "getSelectedItem" ) ) - { - if ( paramTypes.length == 0 ) - { - return m.invoke( aComponent, new Object[] {} ); - } - } - // TODO: should also handle variants of setValue() - } - } - catch ( Exception exc ) - { // error occured while introspecting... move along. - System.out.println( "InfoPanel.getValueFromComponent: " + exc ); - } - - // not found - return null; - } + /** + * Gets the value in the specified component. Note: If the component does not + * respond to getText() or getString() or getSelectedItem() this method will + * return null. JTextFields and the like will work. + * + * @param aComponent The specified component. + * @return An object representing the value in the component. or null if the + * component does not have a text property. + */ + protected Object getValueForComponent(Component aComponent) { + // try to get a text or string property + try { + BeanInfo info = Introspector.getBeanInfo(aComponent.getClass()); + MethodDescriptor[] methods = info.getMethodDescriptors(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i].getMethod(); + Class[] paramTypes = m.getParameterTypes(); + if (m.getName().equals("getText")) { + if (paramTypes.length == 0) { + return m.invoke(aComponent, new Object[] {}); + } + } + if (m.getName().equals("getString")) { + if (paramTypes.length == 0) { + return m.invoke(aComponent, new Object[] {}); + } + } + if (m.getName().equals("getSelectedItem")) { + if (paramTypes.length == 0) { + return m.invoke(aComponent, new Object[] {}); + } + } + // TODO: should also handle variants of setValue() + } + } catch (Exception exc) { // error occured while introspecting... move along. + System.out.println("InfoPanel.getValueFromComponent: " + exc); + } -/** -* Gets the component associated with the key as a JTextField, for backwards compatibility. -* @param key A string representing the key associated with the component. Nulls are converted to an empty string. -* @return A JTextField that contains the value associated with the key, -* or null if the key does not exist or if the component is not a JTextField. -*/ - public JTextField getFieldForKey( String key ) - { - Component c = getComponentForKey( key ); - if ( c instanceof JTextField ) - { - return (JTextField) c; - } - return null; - } + // not found + return null; + } -/** -* Gets the component associated with the key. If more than one component is associated -* with the key, returns the first such component. -* @param key A string representing the key associated with the component. -* Nulls are converted to an empty string. -* @return A component that contains the value associated with the key, -* or null if the key does not exist. -*/ - public Component getComponentForKey( String key ) - { - return getComponentForKey( key, 0 ); + /** + * Gets the component associated with the key as a JTextField, for backwards + * compatibility. + * + * @param key A string representing the key associated with the component. Nulls + * are converted to an empty string. + * @return A JTextField that contains the value associated with the key, or null + * if the key does not exist or if the component is not a JTextField. + */ + public JTextField getFieldForKey(String key) { + Component c = getComponentForKey(key); + if (c instanceof JTextField) { + return (JTextField) c; + } + return null; } -/** -* Gets the component associated with the key and index. -* @param key A string representing the key associated with the component. -* Nulls are converted to an empty string. -* @return A component that contains the value associated with the key, -* or null if the key does not exist. -*/ - public Component getComponentForKey( String key, int index ) - { - Container c = getCompositeComponentForKey( key ); - if ( c == null ) return null; - return c.getComponent( index ); - } + /** + * Gets the component associated with the key. If more than one component is + * associated with the key, returns the first such component. + * + * @param key A string representing the key associated with the component. Nulls + * are converted to an empty string. + * @return A component that contains the value associated with the key, or null + * if the key does not exist. + */ + public Component getComponentForKey(String key) { + return getComponentForKey(key, 0); + } -/** -* Gets the component at the specified row. If more than one component exists -* on that row, returns the first such component. -* @return A component or null if the row does not exist. -*/ - public Object getComponentForIndex( int row ) - { - return getComponentForIndex( row, 0 ); - } + /** + * Gets the component associated with the key and index. + * + * @param key A string representing the key associated with the component. Nulls + * are converted to an empty string. + * @return A component that contains the value associated with the key, or null + * if the key does not exist. + */ + public Component getComponentForKey(String key, int index) { + Container c = getCompositeComponentForKey(key); + if (c == null) + return null; + return c.getComponent(index); + } -/** -* Gets the component at the specified row and column. -* @return A component or null if the index is out of bounds. -*/ - public Object getComponentForIndex( int row, int col ) - { - if ( ( row > fields.size() ) || ( row < 0 ) ) - { // out of bounds - return null; - } - - Container field = (Container) fields.get( row ); - return field.getComponent( col ); - } + /** + * Gets the component at the specified row. If more than one component exists on + * that row, returns the first such component. + * + * @return A component or null if the row does not exist. + */ + public Object getComponentForIndex(int row) { + return getComponentForIndex(row, 0); + } -/** -* Gets the container associated with the key. -* @param key A string representing the key associated with the component. -* Nulls are converted to an empty string. -* @return A component that contains the value associated with the key, -* or null if the key does not exist. -*/ - protected Container getCompositeComponentForKey( String key ) - { - if ( key == null ) - { - key = ""; - } - - JPanel field = null; - Iterator enumeration = fields.iterator(); - while ( enumeration.hasNext() ) - { // finds first value in list with specified key - field = (JPanel) enumeration.next(); - if ( key.equals( field.getName() ) ) - { - return field; - } - } - - // else not found - return null; - } + /** + * Gets the component at the specified row and column. + * + * @return A component or null if the index is out of bounds. + */ + public Object getComponentForIndex(int row, int col) { + if ((row > fields.size()) || (row < 0)) { // out of bounds + return null; + } -/** -* Provided for backwards compatibility: calls getLabelComponentForKey. -* @param key A string representing the key associated with the compoent. -* Nulls are converted to an empty string. -* @return Component label object associated with the key, or null if the key does not exist -* or if the label component is not an instance of JLabel. -*/ - public JLabel getLabelForKey( String key ) - { - Component result = getLabelComponentForKey( key ); - if ( result instanceof JLabel ) return (JLabel) result; - return null; - } - -/** -* Get the label component associated with the key. -* @param key A string representing the key associated with the compoent. -* Nulls are converted to an empty string. -* @return Component label object associated with the key, or null if the key does not exist. -*/ - public Component getLabelComponentForKey( String key ) - { - if ( key == null ) - { - key = ""; - } - - Component label = null; - Iterator enumeration = labels.iterator(); - while ( enumeration.hasNext() ) - { // finds first value in list with specified key - label = (Component) enumeration.next(); - if ( key.equals( label.getName() ) ) - { - return label; - } - } - - // else not found - return null; - } + Container field = (Container) fields.get(row); + return field.getComponent(col); + } -/** -* Replaces the first component associated with the key. Any value in the existing -* component will be copied to the new component. -* @param key A string representing the key to be associated with the component. -* Nulls are converted to an empty string. -* @param c A component to be placed next to the label corresponding to the key. -* Nulls are converted to a JTextField. -*/ - public void setComponentForKey( String key, Component c ) - { - setComponentForKey( key, c, 0 ); + /** + * Gets the container associated with the key. + * + * @param key A string representing the key associated with the component. Nulls + * are converted to an empty string. + * @return A component that contains the value associated with the key, or null + * if the key does not exist. + */ + protected Container getCompositeComponentForKey(String key) { + if (key == null) { + key = ""; + } + + JPanel field = null; + Iterator enumeration = fields.iterator(); + while (enumeration.hasNext()) { // finds first value in list with specified key + field = (JPanel) enumeration.next(); + if (key.equals(field.getName())) { + return field; + } + } + + // else not found + return null; } - -/** -* Replaces the component associated with the key. Any value in the existing -* component will be copied to the new component. -* @param key A string representing the key to be associated with the component. -* Nulls are converted to an empty string. -* @param c A component to be placed next to the label corresponding to the key. -* Nulls are converted to a JTextField. -*/ - public void setComponentForKey( String key, Component c, int index ) - { - if ( c == null ) - { - c = new JTextField( 15 ); - } - if ( key == null ) - { - key = ""; - } - - Container container = this.getCompositeComponentForKey( key ); - Component field = container.getComponent( index ); - Object value = this.getValueForKey( key, index ); - if ( field != null ) - { - container.remove( index ); - container.add( c, index ); - c.setEnabled( this.isEditable ); - introspectComponent( c, key ); - setValueForComponent( c, value ); - } - } -/** -* Replaces the first component in the specified row. Any value in the existing -* component will be copied to the new component. -* @param row A valid index. -* @param c A component to be placed next to the label corresponding to the key. -*/ - public void setComponentForIndex( int row, Component c ) - { - setComponentForIndex( row, 0, c ); + /** + * Provided for backwards compatibility: calls getLabelComponentForKey. + * + * @param key A string representing the key associated with the compoent. Nulls + * are converted to an empty string. + * @return Component label object associated with the key, or null if the key + * does not exist or if the label component is not an instance of + * JLabel. + */ + public JLabel getLabelForKey(String key) { + Component result = getLabelComponentForKey(key); + if (result instanceof JLabel) + return (JLabel) result; + return null; } - -/** -* Replaces the component associated with the key. Any value in the existing -* component will be copied to the new component. -* @param row A valid index. -* @param c A component to be placed next to the label corresponding to the key. -*/ - public void setComponentForIndex( int row, int col, Component c ) - { - setComponentForKey( getLabels()[row], c, col ); - } -/** -* Sets the string that appears before each label's text on the panel. -* @param aString A String to be used as the label prefix. -*/ - public void setLabelPrefix( String aString ) - { - prefix = aString; - setLabels( getLabels() ); // force refresh - } + /** + * Get the label component associated with the key. + * + * @param key A string representing the key associated with the compoent. Nulls + * are converted to an empty string. + * @return Component label object associated with the key, or null if the key + * does not exist. + */ + public Component getLabelComponentForKey(String key) { + if (key == null) { + key = ""; + } -/** -* Gets the string that appears before each label's text on the panel. -* Defaults to "", an empty string. -* @return A String that is currently used as the label prefix. -*/ - public String getLabelPrefix() - { - return prefix; - } + Component label = null; + Iterator enumeration = labels.iterator(); + while (enumeration.hasNext()) { // finds first value in list with specified key + label = (Component) enumeration.next(); + if (key.equals(label.getName())) { + return label; + } + } -/** -* Sets the string that appears after each label's text on the panel. -* Defaults to ": ", a colon followed by a space. -* @param aString A String to be used as the label postfix. -*/ - public void setLabelPostfix( String aString ) - { - postfix = aString; - setLabels( getLabels() ); // force refresh - } + // else not found + return null; + } -/** -* Gets the string that appears after each label's text on the panel. -* @return A String that is currently used as the label postfix. -*/ - public String getLabelPostfix() - { - return postfix; - } + /** + * Replaces the first component associated with the key. Any value in the + * existing component will be copied to the new component. + * + * @param key A string representing the key to be associated with the component. + * Nulls are converted to an empty string. + * @param c A component to be placed next to the label corresponding to the + * key. Nulls are converted to a JTextField. + */ + public void setComponentForKey(String key, Component c) { + setComponentForKey(key, c, 0); + } -/** -* Adds an action listener to the list that will be -* notified by events occurring in the panel. -* @param l An action listener to be notified. -*/ - public void addActionListener(ActionListener l) - { - actionListener = AWTEventMulticaster.add(actionListener, l); - } -/** -* Removes an action listener from the list that will be -* notified by events occurring in the panel. -* @param l An action listener to be removed. -*/ - public void removeActionListener(ActionListener l) - { - actionListener = AWTEventMulticaster.remove(actionListener, l); - } -/** -* Notifies all registered action listeners of a pending Action Event. -* @param e An action event to be broadcast. -*/ - protected void broadcastEvent(ActionEvent e) - { - if (actionListener != null) - { - actionListener.actionPerformed(e); - } - } + /** + * Replaces the component associated with the key. Any value in the existing + * component will be copied to the new component. + * + * @param key A string representing the key to be associated with the component. + * Nulls are converted to an empty string. + * @param c A component to be placed next to the label corresponding to the + * key. Nulls are converted to a JTextField. + */ + public void setComponentForKey(String key, Component c, int index) { + if (c == null) { + c = new JTextField(15); + } + if (key == null) { + key = ""; + } - // interface ActionListener + Container container = this.getCompositeComponentForKey(key); + Component field = container.getComponent(index); + Object value = this.getValueForKey(key, index); + if (field != null) { + container.remove(index); + container.add(c, index); + c.setEnabled(this.isEditable); + introspectComponent(c, key); + setValueForComponent(c, value); + } + } -/** -* Called by buttons on panel and by other components that -* might be set to broadcast events to this listener. -* Simply forwards the action event unchanged. -* @param e An action event to be received. -*/ - public void actionPerformed(ActionEvent e) - { + /** + * Replaces the first component in the specified row. Any value in the existing + * component will be copied to the new component. + * + * @param row A valid index. + * @param c A component to be placed next to the label corresponding to the + * key. + */ + public void setComponentForIndex(int row, Component c) { + setComponentForIndex(row, 0, c); + } + + /** + * Replaces the component associated with the key. Any value in the existing + * component will be copied to the new component. + * + * @param row A valid index. + * @param c A component to be placed next to the label corresponding to the + * key. + */ + public void setComponentForIndex(int row, int col, Component c) { + setComponentForKey(getLabels()[row], c, col); + } + + /** + * Sets the string that appears before each label's text on the panel. + * + * @param aString A String to be used as the label prefix. + */ + public void setLabelPrefix(String aString) { + prefix = aString; + setLabels(getLabels()); // force refresh + } + + /** + * Gets the string that appears before each label's text on the panel. Defaults + * to "", an empty string. + * + * @return A String that is currently used as the label prefix. + */ + public String getLabelPrefix() { + return prefix; + } + + /** + * Sets the string that appears after each label's text on the panel. Defaults + * to ": ", a colon followed by a space. + * + * @param aString A String to be used as the label postfix. + */ + public void setLabelPostfix(String aString) { + postfix = aString; + setLabels(getLabels()); // force refresh + } + + /** + * Gets the string that appears after each label's text on the panel. + * + * @return A String that is currently used as the label postfix. + */ + public String getLabelPostfix() { + return postfix; + } + + /** + * Adds an action listener to the list that will be notified by events occurring + * in the panel. + * + * @param l An action listener to be notified. + */ + public void addActionListener(ActionListener l) { + actionListener = AWTEventMulticaster.add(actionListener, l); + } + + /** + * Removes an action listener from the list that will be notified by events + * occurring in the panel. + * + * @param l An action listener to be removed. + */ + public void removeActionListener(ActionListener l) { + actionListener = AWTEventMulticaster.remove(actionListener, l); + } + + /** + * Notifies all registered action listeners of a pending Action Event. + * + * @param e An action event to be broadcast. + */ + protected void broadcastEvent(ActionEvent e) { + if (actionListener != null) { + actionListener.actionPerformed(e); + } + } + + // interface ActionListener + + /** + * Called by buttons on panel and by other components that might be set to + * broadcast events to this listener. Simply forwards the action event + * unchanged. + * + * @param e An action event to be received. + */ + public void actionPerformed(ActionEvent e) { // if ( e.getSource() instanceof AbstractButton ) // { - broadcastEvent(e); + broadcastEvent(e); // } - } - - /** - * GridBagLayout allocates weightx only after considering - * the preferred width of the components in a column. - * We'd prefer that preferred width wasn't considered, - * so that the layout worked more like a html-table. - * GridBagLayout is poorly factored for subclassing, - * so this code is going to get a little bit ugly. - * Really, what good is a protected method that returns - * a private class? Would have liked to just override - * getLayoutInfo and be done with it. - */ - private class BetterGridBagLayout extends GridBagLayout - { - public Dimension preferredLayoutSize(Container parent) - { - preprocess(); - return super.preferredLayoutSize( parent ); - } - - public Dimension minimumLayoutSize(Container parent) - { - preprocess(); - return super.minimumLayoutSize( parent ); - } - - - public void layoutContainer(Container parent) - { - preprocess(); - super.layoutContainer( parent ); - } - - protected void preprocess() - { - if ( fieldSpacers == null ) return; - Iterator i; - - // find the field with the widest preferred size - Component c; - int maxWidth = 0; - i = fields.iterator(); - while ( i.hasNext() ) - { - c = (Component) i.next(); - maxWidth = Math.max( maxWidth, - Math.max( c.getPreferredSize().width, c.getMinimumSize().width ) ); - } - - // set each column's spacers to that preferred size - Dimension min = new Dimension( 0, 0 ); - Dimension pref = new Dimension( maxWidth, 0 ); - i = fieldSpacers.iterator(); - while ( i.hasNext() ) - { - ((Box.Filler)i.next()).changeShape( min, pref, pref ); - } - } - } -} + } + + /** + * GridBagLayout allocates weightx only after considering the preferred width of + * the components in a column. We'd prefer that preferred width wasn't + * considered, so that the layout worked more like a html-table. GridBagLayout + * is poorly factored for subclassing, so this code is going to get a little bit + * ugly. Really, what good is a protected method that returns a private class? + * Would have liked to just override getLayoutInfo and be done with it. + */ + private class BetterGridBagLayout extends GridBagLayout { + public Dimension preferredLayoutSize(Container parent) { + preprocess(); + return super.preferredLayoutSize(parent); + } + + public Dimension minimumLayoutSize(Container parent) { + preprocess(); + return super.minimumLayoutSize(parent); + } + + public void layoutContainer(Container parent) { + preprocess(); + super.layoutContainer(parent); + } + + protected void preprocess() { + if (fieldSpacers == null) + return; + Iterator i; + + // find the field with the widest preferred size + Component c; + int maxWidth = 0; + i = fields.iterator(); + while (i.hasNext()) { + c = (Component) i.next(); + maxWidth = Math.max(maxWidth, Math.max(c.getPreferredSize().width, c.getMinimumSize().width)); + } + // set each column's spacers to that preferred size + Dimension min = new Dimension(0, 0); + Dimension pref = new Dimension(maxWidth, 0); + i = fieldSpacers.iterator(); + while (i.hasNext()) { + ((Box.Filler) i.next()).changeShape(min, pref, pref); + } + } + } +} diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyDelayTimer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyDelayTimer.java index b73c74d..31bdb70 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyDelayTimer.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyDelayTimer.java @@ -28,161 +28,150 @@ import java.awt.event.KeyListener; import javax.swing.Timer; /** -* KeyDelayTimer is a utility that listens for KeyEvents from one -* or more components. After receiving a KeyEvents the timer will -* broadcast an action event if a specified time interval passes without -* a subsequent KeyEvent.

-* -* This utility is useful for implementing any kind of auto-complete -* feature in a user interface. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ -*/ -public class KeyDelayTimer implements ActionListener, KeyListener -{ - // delay timer for keypress-sensitve events - protected Timer keyTimer = null; - protected Component lastFieldTouched = null; - protected long timeLastFieldTouched = 0; - protected int interval = 400; // adjust as needed - - // for action multicasting - protected ActionListener actionListener = null; - -/** -* Default constructor. -*/ - public KeyDelayTimer() - { - keyTimer = new Timer( interval, this ); - } - -/** -* Convenience constructor. -* @param listener An action listener to be notified of delay events. -*/ - public KeyDelayTimer( ActionListener listener ) - { - this(); - addActionListener( listener ); - } - -/** -* Returns the last component that generated a KeyEvent. -* @return The component that sent the most recent KeyEvent. -*/ - public Component getComponent() - { - return lastFieldTouched; - } - -/** -* Returns the number of milliseconds before an ActionEvent is generated. -* The default is 400. -* @return The current delay interval in milliseconds. -*/ - public int getInterval() - { - return interval; - } - -/** -* Sets the number of milliseconds before an ActionEvent will be generated -* after a KeyEvent is received. -* @param millis The new delay interval in milliseconds. -*/ - public void setInterval( int millis ) - { - interval = millis; - keyTimer.setDelay( interval / 2 ); - } - - // interface KeyListener - - public void keyTyped(KeyEvent e) - { - } - public void keyPressed(KeyEvent e) - { - } - -/** -* Receives key events from one or more components. -* Records the component and the time this event was received, -* then starts the timer. -* @param e The key event in question. -*/ - public void keyReleased(KeyEvent e) - { // handles keystrokes in the textfields (except ENTER and ESCAPE) - if ( ( Character.isLetterOrDigit( e.getKeyChar() ) ) - || ( e.getKeyCode() == KeyEvent.VK_SPACE ) - || ( e.getKeyCode() == KeyEvent.VK_DELETE ) - || ( e.getKeyCode() == KeyEvent.VK_BACK_SPACE ) ) - { - this.lastFieldTouched = e.getComponent(); - this.timeLastFieldTouched = System.currentTimeMillis(); - this.keyTimer.start(); - return; - } - } - - // interface ActionListener - -/** -* Receives ActionEvents from the internal timer. -* If the interval has passed without another KeyEvent, -* an ActionEvent is broadcast, with the name of this class -* as the ActionCommand, and the internal timer is stopped. -* @param e The action event in question. -*/ - public void actionPerformed(ActionEvent e) - { - if ( e.getSource() == keyTimer ) - { - if ( System.currentTimeMillis() - this.timeLastFieldTouched > interval ) - { - this.keyTimer.stop(); - broadcastEvent( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, this.getClass().getName() ) ); - } - return; - } - } - - // Action Multicast methods - -/** -* Adds an action listener to the list that will be -* notified by button events and changes in button state. -* @param l An action listener to be notified. -*/ - public void addActionListener(ActionListener l) - { - actionListener = AWTEventMulticaster.add(actionListener, l); - } -/** -* Removes an action listener from the list that will be -* notified by button events and changes in button state. -* @param l An action listener to be removed. -*/ - public void removeActionListener(ActionListener l) - { - actionListener = AWTEventMulticaster.remove(actionListener, l); - } -/** -* Notifies all registered action listeners of a pending Action Event. -* @param e An action event to be broadcast. -*/ - protected void broadcastEvent(ActionEvent e) - { - if (actionListener != null) - { - actionListener.actionPerformed(e); - } - } + * KeyDelayTimer is a utility that listens for KeyEvents from one or more + * components. After receiving a KeyEvents the timer will broadcast an action + * event if a specified time interval passes without a subsequent KeyEvent.
+ *
+ * + * This utility is useful for implementing any kind of auto-complete feature in + * a user interface. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) + * $ + */ +public class KeyDelayTimer implements ActionListener, KeyListener { + // delay timer for keypress-sensitve events + protected Timer keyTimer = null; + protected Component lastFieldTouched = null; + protected long timeLastFieldTouched = 0; + protected int interval = 400; // adjust as needed + + // for action multicasting + protected ActionListener actionListener = null; + + /** + * Default constructor. + */ + public KeyDelayTimer() { + keyTimer = new Timer(interval, this); + } + + /** + * Convenience constructor. + * + * @param listener An action listener to be notified of delay events. + */ + public KeyDelayTimer(ActionListener listener) { + this(); + addActionListener(listener); + } + + /** + * Returns the last component that generated a KeyEvent. + * + * @return The component that sent the most recent KeyEvent. + */ + public Component getComponent() { + return lastFieldTouched; + } + + /** + * Returns the number of milliseconds before an ActionEvent is generated. The + * default is 400. + * + * @return The current delay interval in milliseconds. + */ + public int getInterval() { + return interval; + } + + /** + * Sets the number of milliseconds before an ActionEvent will be generated after + * a KeyEvent is received. + * + * @param millis The new delay interval in milliseconds. + */ + public void setInterval(int millis) { + interval = millis; + keyTimer.setDelay(interval / 2); + } + + // interface KeyListener + + public void keyTyped(KeyEvent e) { + } + + public void keyPressed(KeyEvent e) { + } + + /** + * Receives key events from one or more components. Records the component and + * the time this event was received, then starts the timer. + * + * @param e The key event in question. + */ + public void keyReleased(KeyEvent e) { // handles keystrokes in the textfields (except ENTER and ESCAPE) + if ((Character.isLetterOrDigit(e.getKeyChar())) || (e.getKeyCode() == KeyEvent.VK_SPACE) + || (e.getKeyCode() == KeyEvent.VK_DELETE) || (e.getKeyCode() == KeyEvent.VK_BACK_SPACE)) { + this.lastFieldTouched = e.getComponent(); + this.timeLastFieldTouched = System.currentTimeMillis(); + this.keyTimer.start(); + return; + } + } + + // interface ActionListener + + /** + * Receives ActionEvents from the internal timer. If the interval has passed + * without another KeyEvent, an ActionEvent is broadcast, with the name of this + * class as the ActionCommand, and the internal timer is stopped. + * + * @param e The action event in question. + */ + public void actionPerformed(ActionEvent e) { + if (e.getSource() == keyTimer) { + if (System.currentTimeMillis() - this.timeLastFieldTouched > interval) { + this.keyTimer.stop(); + broadcastEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, this.getClass().getName())); + } + return; + } + } + + // Action Multicast methods + + /** + * Adds an action listener to the list that will be notified by button events + * and changes in button state. + * + * @param l An action listener to be notified. + */ + public void addActionListener(ActionListener l) { + actionListener = AWTEventMulticaster.add(actionListener, l); + } + + /** + * Removes an action listener from the list that will be notified by button + * events and changes in button state. + * + * @param l An action listener to be removed. + */ + public void removeActionListener(ActionListener l) { + actionListener = AWTEventMulticaster.remove(actionListener, l); + } + + /** + * Notifies all registered action listeners of a pending Action Event. + * + * @param e An action event to be broadcast. + */ + protected void broadcastEvent(ActionEvent e) { + if (actionListener != null) { + actionListener.actionPerformed(e); + } + } } - - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyableCellEditor.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyableCellEditor.java index 95b8a19..f64e607 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyableCellEditor.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyableCellEditor.java @@ -40,311 +40,265 @@ import javax.swing.event.ChangeEvent; import javax.swing.table.TableCellEditor; /** -* A table cell editor customized for keyboard navigation, much like -* working with a spreadsheet. The default cell editor unfortunately -* does none of these things: -*

-* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ -*/ -public class KeyableCellEditor implements TableCellEditor, FocusListener, - KeyListener, Serializable -{ - List listeners; - JTextField textField; - Object lastValue; - Format currentFormat; - - JTable table; - -/** -* Default constructor - a standard JTextField will be used for editing. -*/ - public KeyableCellEditor() - { - this( (JTextField) null ); - } - -/** -* Constructor specifying a type of JTextField to be used for editing. -* The JTextField will have its border replaced with a black line border. -* @param aTextField A JTextField or subclass for editing values. -*/ - public KeyableCellEditor( JTextField aTextField ) - { - listeners = new Vector(); - lastValue = null; - - // default to stock JTextField - textField = aTextField; - if ( textField == null ) - { - textField = new JTextField(); - } - - textField.setBorder(new LineBorder(Color.black)); - - // handle arrow keys while caret is showing - textField.addKeyListener( this ); - - // handle lost focus - textField.addFocusListener( this ); - } - - public Component getTableCellEditorComponent(JTable table, - Object value, - boolean isSelected, - int row, - int column) - { - this.table = table; - table.removeKeyListener( this ); // if any - table.addKeyListener( this ); - return getEditorComponent( value ); - } - - protected Component getEditorComponent( Object value ) - { - if ( value != null ) - { - textField.setText( value.toString() ); - } - else - { - textField.setText( "" ); - } - - if ( value instanceof Number ) - { - textField.setHorizontalAlignment(JTextField.RIGHT); - } - else - { - textField.setHorizontalAlignment(JTextField.LEFT); - } - - // remember original value - lastValue = value; - - // select all text and get focus - textField.selectAll(); - textField.requestFocus(); - - return textField; - } - - public Object getCellEditorValue() - { - return lastValue; - } - - public boolean isCellEditable(EventObject anEvent) - { - // key events should replace the selection - // NOTE: For whatever reason, key events trigger result in a null parameter - if ( anEvent == null ) - { - textField.setText(""); - textField.requestFocus(); - return true; - } - - return true; - } - - public boolean shouldSelectCell(EventObject anEvent) - { // System.out.println( "KeyableCellEditor.shouldSelectCell: " + anEvent ); - - // key events should replace the selection - // NOTE: For whatever reason, key events are not generated - if ( anEvent instanceof KeyEvent ) - { - textField.setText(""); - textField.requestFocus(); - return true; - } - - // otherwise, select all text and continue - textField.selectAll(); - textField.requestFocus(); - - return true; - } - - public boolean stopCellEditing() - { - lastValue = textField.getText(); - fireEditingStopped(); - table.removeKeyListener( this ); // if any - return true; - } - - public void cancelCellEditing() - { - fireEditingCanceled(); - table.removeKeyListener( this ); // if any - } - - public void addCellEditorListener(CellEditorListener l) - { - listeners.add( l ); - } - - public void removeCellEditorListener(CellEditorListener l) - { - listeners.remove( l ); - } - - protected void fireEditingCanceled() - { - ChangeEvent event = new ChangeEvent( this ); - Iterator it = new ArrayList( listeners ).iterator(); // copy to prevent modification exception - while ( it.hasNext() ) - { - ((CellEditorListener)it.next()).editingCanceled( event ); - } - } - - protected void fireEditingStopped() - { - ChangeEvent event = new ChangeEvent( this ); - Iterator it = new ArrayList( listeners ).iterator(); // copy to prevent modification exception - while ( it.hasNext() ) - { - ((CellEditorListener)it.next()).editingStopped( event ); - } - } - - protected void onEnterKey() - { - stopCellEditing(); - } - - protected void onEscapeKey() - { - cancelCellEditing(); - } - - protected void moveEditCell( int dRow, int dCol ) - { - if ( table == null ) return; - int row = table.getSelectedRow() + dRow; - int col = table.getSelectedColumn() + dCol; - - row = Math.max( 0, row ); - row = Math.min( row, table.getRowCount() - 1 ); - col = Math.max( 0, col ); - col = Math.min( col, table.getColumnCount() - 1 ); - - stopCellEditing(); - table.setRowSelectionInterval( row, row ); - table.setColumnSelectionInterval( col, col ); - table.editCellAt( row, col ); - textField.selectAll(); - textField.requestFocus(); - } - - // interface KeyListener - - public void keyTyped(KeyEvent e) - { // System.out.println( "KeyableCellEditor.keyTyped: " + KeyEvent.getKeyText( e.getKeyCode() ) ); - } - - public void keyPressed(KeyEvent e) - { // System.out.println( "KeyableCellEditor.keyPressed: " + KeyEvent.getKeyText( e.getKeyCode() ) ); - - // catch LEFT and RIGHT here before JTextField consumes them - - int keyCode = e.getKeyCode(); - if ( keyCode == KeyEvent.VK_LEFT ) - { - if ( textField.getSelectionStart() == 0 ) - { - moveEditCell( 0, -1 ); - e.consume(); - return; - } - } - if ( keyCode == KeyEvent.VK_RIGHT ) - { - if ( textField.getSelectionEnd() == textField.getText().length() ) - { - moveEditCell( 0, 1 ); - e.consume(); - return; - } - } - if ( keyCode == KeyEvent.VK_UP ) - { - moveEditCell( -1, 0 ); - e.consume(); - return; - } - if ( keyCode == KeyEvent.VK_DOWN ) - { - moveEditCell( 1, 0 ); - e.consume(); - return; - } - } - - public void keyReleased(KeyEvent e) - { // System.out.println( "KeyableCellEditor.keyReleased: " + KeyEvent.getKeyText( e.getKeyCode() ) ); - - // catch ENTER here to allow JTextField to process it as well - - int keyCode = e.getKeyCode(); - if ( keyCode == KeyEvent.VK_ENTER ) - { - onEnterKey(); - return; - } - if ( keyCode == KeyEvent.VK_ESCAPE ) - { - onEscapeKey(); - return; - } - - // tabs are apparently only received on key release - if ( keyCode == KeyEvent.VK_TAB ) - { - if ( e.isShiftDown() ) - { - moveEditCell( 0, -1 ); - } - else - { - moveEditCell( 0, 1 ); - } - e.consume(); - return; - } - - } - - // interface FocusListener - - public void focusGained(FocusEvent e) - { // System.out.println( "focusGained: " ); - } - - public void focusLost(FocusEvent e) - { // System.out.println( "focusLost: " ); - stopCellEditing(); - } + * A table cell editor customized for keyboard navigation, much like working + * with a spreadsheet. The default cell editor unfortunately does none of these + * things: + * + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) + * $ + */ +public class KeyableCellEditor implements TableCellEditor, FocusListener, KeyListener, Serializable { + List listeners; + JTextField textField; + Object lastValue; + Format currentFormat; + + JTable table; + + /** + * Default constructor - a standard JTextField will be used for editing. + */ + public KeyableCellEditor() { + this((JTextField) null); + } + + /** + * Constructor specifying a type of JTextField to be used for editing. The + * JTextField will have its border replaced with a black line border. + * + * @param aTextField A JTextField or subclass for editing values. + */ + public KeyableCellEditor(JTextField aTextField) { + listeners = new Vector(); + lastValue = null; + + // default to stock JTextField + textField = aTextField; + if (textField == null) { + textField = new JTextField(); + } + + textField.setBorder(new LineBorder(Color.black)); + + // handle arrow keys while caret is showing + textField.addKeyListener(this); + + // handle lost focus + textField.addFocusListener(this); + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + this.table = table; + table.removeKeyListener(this); // if any + table.addKeyListener(this); + return getEditorComponent(value); + } + + protected Component getEditorComponent(Object value) { + if (value != null) { + textField.setText(value.toString()); + } else { + textField.setText(""); + } + + if (value instanceof Number) { + textField.setHorizontalAlignment(JTextField.RIGHT); + } else { + textField.setHorizontalAlignment(JTextField.LEFT); + } + + // remember original value + lastValue = value; + + // select all text and get focus + textField.selectAll(); + textField.requestFocus(); + + return textField; + } + + public Object getCellEditorValue() { + return lastValue; + } + + public boolean isCellEditable(EventObject anEvent) { + // key events should replace the selection + // NOTE: For whatever reason, key events trigger result in a null parameter + if (anEvent == null) { + textField.setText(""); + textField.requestFocus(); + return true; + } + + return true; + } + + public boolean shouldSelectCell(EventObject anEvent) { // System.out.println( "KeyableCellEditor.shouldSelectCell: " + // + anEvent ); + + // key events should replace the selection + // NOTE: For whatever reason, key events are not generated + if (anEvent instanceof KeyEvent) { + textField.setText(""); + textField.requestFocus(); + return true; + } + + // otherwise, select all text and continue + textField.selectAll(); + textField.requestFocus(); + + return true; + } + + public boolean stopCellEditing() { + lastValue = textField.getText(); + fireEditingStopped(); + table.removeKeyListener(this); // if any + return true; + } + + public void cancelCellEditing() { + fireEditingCanceled(); + table.removeKeyListener(this); // if any + } + + public void addCellEditorListener(CellEditorListener l) { + listeners.add(l); + } + + public void removeCellEditorListener(CellEditorListener l) { + listeners.remove(l); + } + + protected void fireEditingCanceled() { + ChangeEvent event = new ChangeEvent(this); + Iterator it = new ArrayList(listeners).iterator(); // copy to prevent modification exception + while (it.hasNext()) { + ((CellEditorListener) it.next()).editingCanceled(event); + } + } + + protected void fireEditingStopped() { + ChangeEvent event = new ChangeEvent(this); + Iterator it = new ArrayList(listeners).iterator(); // copy to prevent modification exception + while (it.hasNext()) { + ((CellEditorListener) it.next()).editingStopped(event); + } + } + + protected void onEnterKey() { + stopCellEditing(); + } + + protected void onEscapeKey() { + cancelCellEditing(); + } + + protected void moveEditCell(int dRow, int dCol) { + if (table == null) + return; + int row = table.getSelectedRow() + dRow; + int col = table.getSelectedColumn() + dCol; + + row = Math.max(0, row); + row = Math.min(row, table.getRowCount() - 1); + col = Math.max(0, col); + col = Math.min(col, table.getColumnCount() - 1); + + stopCellEditing(); + table.setRowSelectionInterval(row, row); + table.setColumnSelectionInterval(col, col); + table.editCellAt(row, col); + textField.selectAll(); + textField.requestFocus(); + } + + // interface KeyListener + + public void keyTyped(KeyEvent e) { // System.out.println( "KeyableCellEditor.keyTyped: " + KeyEvent.getKeyText( + // e.getKeyCode() ) ); + } + + public void keyPressed(KeyEvent e) { // System.out.println( "KeyableCellEditor.keyPressed: " + KeyEvent.getKeyText( + // e.getKeyCode() ) ); + + // catch LEFT and RIGHT here before JTextField consumes them + + int keyCode = e.getKeyCode(); + if (keyCode == KeyEvent.VK_LEFT) { + if (textField.getSelectionStart() == 0) { + moveEditCell(0, -1); + e.consume(); + return; + } + } + if (keyCode == KeyEvent.VK_RIGHT) { + if (textField.getSelectionEnd() == textField.getText().length()) { + moveEditCell(0, 1); + e.consume(); + return; + } + } + if (keyCode == KeyEvent.VK_UP) { + moveEditCell(-1, 0); + e.consume(); + return; + } + if (keyCode == KeyEvent.VK_DOWN) { + moveEditCell(1, 0); + e.consume(); + return; + } + } + + public void keyReleased(KeyEvent e) { // System.out.println( "KeyableCellEditor.keyReleased: " + + // KeyEvent.getKeyText( e.getKeyCode() ) ); + + // catch ENTER here to allow JTextField to process it as well + + int keyCode = e.getKeyCode(); + if (keyCode == KeyEvent.VK_ENTER) { + onEnterKey(); + return; + } + if (keyCode == KeyEvent.VK_ESCAPE) { + onEscapeKey(); + return; + } + + // tabs are apparently only received on key release + if (keyCode == KeyEvent.VK_TAB) { + if (e.isShiftDown()) { + moveEditCell(0, -1); + } else { + moveEditCell(0, 1); + } + e.consume(); + return; + } + + } + + // interface FocusListener + + public void focusGained(FocusEvent e) { // System.out.println( "focusGained: " ); + } + + public void focusLost(FocusEvent e) { // System.out.println( "focusLost: " ); + stopCellEditing(); + } } - - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/LineWrappingRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/LineWrappingRenderer.java index 4a7f07e..326d825 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/LineWrappingRenderer.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/LineWrappingRenderer.java @@ -31,124 +31,111 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** -* A list cell renderer that wraps its text to subsequent lines -* depending on the length of text string and the width of the -* parent list. -* -* This renderer depends on listening to the parent list's viewport -* and fixing the list's width to match the viewport's size. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @date $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ -* @revision $Revision: 904 $ -*/ -public class LineWrappingRenderer extends MultiLineLabel - implements ListCellRenderer, ChangeListener -{ - protected static Border noFocusBorder; - - protected JList list; - protected JViewport viewport; - protected int preferredWidth; - -/** -* Required constructor. The renderer keeps a reference to -* the list in which it is used and its viewport. This list -* is the only list that may use this renderer. The renderer -* will use the current size of the list to determine where -* lines will initially break. -* @param containerList The list that will be using this renderer. -*/ - public LineWrappingRenderer( JList containerList ) - { - super(); - setLineWrap(true); - noFocusBorder = new EmptyBorder(1, 1, 1, 1); - - list = containerList; - preferredWidth = 400; - if ( list.getParent() instanceof JViewport ) - { - viewport = (JViewport) list.getParent(); - viewport.addChangeListener( this ); - int newWidth = viewport.getExtentSize().width; - if ( newWidth > 0 ) preferredWidth = newWidth; - } - else - { - // should function adequately in absence of a viewport - // System.err.println( "LineWrappingRenderer.init: list.getParent = " + list.getParent() ); - } - } - -/** -* Returns the preferred size of the label, with width -* constrained to the current width. -* @return the size -*/ - public Dimension getPreferredSize() - { - int width = getWidth(); - if ( width != preferredWidth ) - { - // if component has not yet been placed within the list - if ( width < list.getWidth() / 2 ) width = list.getWidth(); - preferredWidth = width; - } - return new Dimension( preferredWidth, super.getPreferredSize().height ); - } - -/** -* Returns this component with the width set to the -* width of the specified JList. -* @return this component. -*/ - public Component getListCellRendererComponent ( - JList list, - Object value, - int index, - boolean isSelected, - boolean cellHasFocus ) - { // System.out.println( "LineWrappingRenderer.getListCellRendererComponent:" ); - - if ( list != this.list ) - { - System.err.println( "LineWrappingRenderer.getListCellRendererComponent: " + - "warning: the list using the renderer is not the list specified in the constructor." ); - } - - if (isSelected) - { - setBackground(this.list.getSelectionBackground()); - setForeground(this.list.getSelectionForeground()); - } - else - { - setBackground(this.list.getBackground()); - setForeground(this.list.getForeground()); - } - - setText((value == null) ? "" : value.toString()); - - setEnabled(this.list.isEnabled()); - setFont(this.list.getFont()); - setBorder( (cellHasFocus) ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder ); - - return this; - } - -/** -* Overridden to respond to viewport changes. -*/ - public void stateChanged(ChangeEvent e) - { - int newWidth = viewport.getExtentSize().width; - if ( newWidth > 0 ) preferredWidth = newWidth; - - // set fixed width on list - list.setFixedCellWidth( preferredWidth ); - setSize( preferredWidth, super.getSize().height ); - } + * A list cell renderer that wraps its text to subsequent lines depending on the + * length of text string and the width of the parent list. + * + * This renderer depends on listening to the parent list's viewport and fixing + * the list's width to match the viewport's size. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @date $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ + * @revision $Revision: 904 $ + */ +public class LineWrappingRenderer extends MultiLineLabel implements ListCellRenderer, ChangeListener { + protected static Border noFocusBorder; + + protected JList list; + protected JViewport viewport; + protected int preferredWidth; + + /** + * Required constructor. The renderer keeps a reference to the list in which it + * is used and its viewport. This list is the only list that may use this + * renderer. The renderer will use the current size of the list to determine + * where lines will initially break. + * + * @param containerList The list that will be using this renderer. + */ + public LineWrappingRenderer(JList containerList) { + super(); + setLineWrap(true); + noFocusBorder = new EmptyBorder(1, 1, 1, 1); + + list = containerList; + preferredWidth = 400; + if (list.getParent() instanceof JViewport) { + viewport = (JViewport) list.getParent(); + viewport.addChangeListener(this); + int newWidth = viewport.getExtentSize().width; + if (newWidth > 0) + preferredWidth = newWidth; + } else { + // should function adequately in absence of a viewport + // System.err.println( "LineWrappingRenderer.init: list.getParent = " + + // list.getParent() ); + } + } + + /** + * Returns the preferred size of the label, with width constrained to the + * current width. + * + * @return the size + */ + public Dimension getPreferredSize() { + int width = getWidth(); + if (width != preferredWidth) { + // if component has not yet been placed within the list + if (width < list.getWidth() / 2) + width = list.getWidth(); + preferredWidth = width; + } + return new Dimension(preferredWidth, super.getPreferredSize().height); + } + + /** + * Returns this component with the width set to the width of the specified + * JList. + * + * @return this component. + */ + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, + boolean cellHasFocus) { // System.out.println( "LineWrappingRenderer.getListCellRendererComponent:" ); + + if (list != this.list) { + System.err.println("LineWrappingRenderer.getListCellRendererComponent: " + + "warning: the list using the renderer is not the list specified in the constructor."); + } + + if (isSelected) { + setBackground(this.list.getSelectionBackground()); + setForeground(this.list.getSelectionForeground()); + } else { + setBackground(this.list.getBackground()); + setForeground(this.list.getForeground()); + } + + setText((value == null) ? "" : value.toString()); + + setEnabled(this.list.isEnabled()); + setFont(this.list.getFont()); + setBorder((cellHasFocus) ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder); + + return this; + } + + /** + * Overridden to respond to viewport changes. + */ + public void stateChanged(ChangeEvent e) { + int newWidth = viewport.getExtentSize().width; + if (newWidth > 0) + preferredWidth = newWidth; + + // set fixed width on list + list.setFixedCellWidth(preferredWidth); + setSize(preferredWidth, super.getSize().height); + } } diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/MultiLineLabel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/MultiLineLabel.java index b5f8a9b..ce362c9 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/MultiLineLabel.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/MultiLineLabel.java @@ -23,113 +23,100 @@ import javax.swing.LookAndFeel; import javax.swing.text.Highlighter; /** -* A custom JTextArea that looks and feels like a JLabel, but supports -* line wrapping. This works a lot more like the IFC label component. -* NOTE: doesn't support icons (yet). -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @date $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ -* @revision $Revision: 904 $ -*/ -public class MultiLineLabel extends JTextArea -{ + * A custom JTextArea that looks and feels like a JLabel, but supports line + * wrapping. This works a lot more like the IFC label component. NOTE: doesn't + * support icons (yet). + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @date $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ + * @revision $Revision: 904 $ + */ +public class MultiLineLabel extends JTextArea { /** - * Saves a reference to the original highlighter - * to enable/disable text selection. - */ + * Saves a reference to the original highlighter to enable/disable text + * selection. + */ protected Highlighter originalHighlighter; - -/* -* Creates a MultiLineLabel instance with an empty string for the title. -*/ - public MultiLineLabel() - { - super(); - // turn on wrapping and disable editing and highlighting + /* + * Creates a MultiLineLabel instance with an empty string for the title. + */ + public MultiLineLabel() { + super(); - setLineWrap(true); - setWrapStyleWord(true); - setEditable(false); - setSelectable( false ); - } + // turn on wrapping and disable editing and highlighting -/* -* Creates a MultiLineLabel instance with the specified text. -* @param text The specified text. -*/ - public MultiLineLabel( String text ) - { - super( text ); + setLineWrap(true); + setWrapStyleWord(true); + setEditable(false); + setSelectable(false); + } - // turn on wrapping and disable editing and highlighting + /* + * Creates a MultiLineLabel instance with the specified text. + * + * @param text The specified text. + */ + public MultiLineLabel(String text) { + super(text); - setLineWrap(true); - setWrapStyleWord(true); - setEditable(false); - setSelectable( false ); - } + // turn on wrapping and disable editing and highlighting -/* -* Overridden to look like a label. -* @param text The specified text. -*/ - public void updateUI() - { - // got the implementation idea from usenet + setLineWrap(true); + setWrapStyleWord(true); + setEditable(false); + setSelectable(false); + } - super.updateUI(); + /* + * Overridden to look like a label. + * + * @param text The specified text. + */ + public void updateUI() { + // got the implementation idea from usenet - // turn on wrapping and disable editing and highlighting + super.updateUI(); - setLineWrap(true); - setWrapStyleWord(true); - setEditable(false); - setSelectable( false ); + // turn on wrapping and disable editing and highlighting - // Set the text area's border, colors and font to - // that of a label + setLineWrap(true); + setWrapStyleWord(true); + setEditable(false); + setSelectable(false); - LookAndFeel.installBorder(this, "Label.border"); + // Set the text area's border, colors and font to + // that of a label - LookAndFeel.installColorsAndFont(this, - "Label.background", - "Label.foreground", - "Label.font"); - } + LookAndFeel.installBorder(this, "Label.border"); -/** -* Sets whether text is selectable. -* Default is non-selectable text. -*/ - public void setSelectable( boolean selectable ) - { - if ( selectable ) - { - setHighlighter( originalHighlighter ); - } - else - { + LookAndFeel.installColorsAndFont(this, "Label.background", "Label.foreground", "Label.font"); + } + + /** + * Sets whether text is selectable. Default is non-selectable text. + */ + public void setSelectable(boolean selectable) { + if (selectable) { + setHighlighter(originalHighlighter); + } else { originalHighlighter = getHighlighter(); - setHighlighter( null ); + setHighlighter(null); } } - -/** -* Gets whether text is selectable. -* Default is non-selectable text. -*/ - public boolean isSelectable() - { - return ( getHighlighter() != null ); + + /** + * Gets whether text is selectable. Default is non-selectable text. + */ + public boolean isSelectable() { + return (getHighlighter() != null); } -/** -* Overridden to return false. -*/ - public boolean isFocusTraversable() - { + /** + * Overridden to return false. + */ + public boolean isFocusTraversable() { return false; - } + } } diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/NumericTextField.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/NumericTextField.java index b3d2d03..dee8f27 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/NumericTextField.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/NumericTextField.java @@ -19,416 +19,346 @@ License along with this library; if not, see http://www.gnu.org package net.wotonomy.ui.swing.components; /** -* NumericTextField is a "smart" text field that restricts the user's input. The -* input is restructed to numeric only wich can be of two types: integer and real -* numbers. A range can also be placed on the text field. The default type is -* integer, with the being (Integer.MIN_VALUE, Integer.MAX_VALUE). -* -* @author rob@straylight.princeton.com -* @author $Author: cgruber $ -* @version $Revision: 893 $ -*/ -public class NumericTextField extends SmartTextField -{ - -/******************************* -* CONSTANTS -*******************************/ - -/** -* Restrict the text input to integers (whole numbers) only. -*/ - public final static int INTEGER = 0; - -/** -* Restrict the text input to floating-point numbers only. -*/ - public final static int FLOAT = 1; - - private Number maximumValue = null; - private Number minimumValue = null; - - private boolean sign = false; - private int newCaretPosition = 0; - private int valueType = INTEGER; - - -/******************************* -* PUBLIC METHODS -*******************************/ - -/** -* Default constructor. -*/ - public NumericTextField() - { - this("", 0); - } - -/** -* Constructor. -* @param text The initial string the text field is set to. -*/ - public NumericTextField(String text) - { - this(text, 0); - } - -/** -* Constructor. -* @param columns Width of the text field (in characters). -*/ - public NumericTextField(int columns) - { - this("", columns); - } - -/** -* Constructor. -* @param text The initial string the text field is set to. -* @param columns Width of the text field (in characters). -*/ - public NumericTextField(String text, int columns) - { - super(text, columns); - } - -/** -* Sets the upper limit of the range of numbers to accept. -* @param newMaximumValue The maximum number accepted by the text field. -*/ - public void setMaximumValue(double newMaximumValue) - { - if (newMaximumValue >= 0) - { - maximumValue = new Double( newMaximumValue ); - } - else - { - maximumValue = null; - } - } - -/** -* Returns the upper limit of the range of numbers to accept. -* @return The maximum number accepted by this text field. -*/ - public double getMaximumValue() - { - if ( valueType == INTEGER ) - { - return (maximumValue == null) ? (double) Integer.MAX_VALUE : maximumValue.doubleValue(); - } - else - { - return (maximumValue == null) ? Double.MAX_VALUE : maximumValue.doubleValue(); - } - } - -/** -* Sets the lower limit of the range of numbers to accept. -* @param newMinimumValue The minimum number accepted by the text field. -*/ - public void setMinimumValue(double newMinimumValue) - { - if (newMinimumValue <= 0) - { - minimumValue = new Double( newMinimumValue ); - } - else - { - minimumValue = null; - } - } - -/** -* Returns the lower limit of the range of numbers to accept. -* @return The minimum number accepted by this text field. -*/ - public double getMinimumValue() - { - if ( valueType == INTEGER ) - { - return (minimumValue == null) ? (double) Integer.MIN_VALUE : minimumValue.doubleValue(); - } - else - { - return (minimumValue == null) ? -1.0*Double.MAX_VALUE : minimumValue.doubleValue(); - // NOTE: Double.MIN_VALUE returns the smallest positive value - oooops. - } - } - -/** -* Sets which type of number this text field can accept. -* @see #INTEGER -* @see #FLOAT -* @param newValueType The type of number to accept. -*/ - public void setValueType(int newValueType) - { - if ((newValueType != INTEGER) && (newValueType != FLOAT)) - { - valueType = INTEGER; - } - else - { - valueType = newValueType; - } - } - -/** -* Returns which type of number this text field accepts. The default is -* integer. -* @see #INTEGER -* @see #FLOAT -* @return The type of number to accept. -*/ - public int getValueType() - { - return valueType; - } - -/** -* Returns the integer numeric value of the string in the text field. The type -* can be either integer of float. -* @return The current value in the text field. -*/ - public int getIntValue() - { - int value = 0; - - try - { - value = Integer.parseInt(getText()); - } - catch (NumberFormatException e) - { - try - { - Double dValue = Double.valueOf(getText()); - value = dValue.intValue(); - } - catch (NumberFormatException ignored) {} - } - - return value; - } - -/** -* Sets the text field to integer value specified. -* @param aValue An integer value to display in the text field. -*/ - public void setIntValue(int aValue) - { - setText(Integer.toString(aValue)); - } - -/** -* Returns the real number numeric value of the string in the text field. The type -* can be either integer of float. -* @return The current value in the text field. -*/ - public double getDoubleValue() - { - Double value = new Double(0); - - try - { - value = Double.valueOf(getText()); - } - catch (NumberFormatException ignored) {} - - return value.doubleValue(); - } - -/** -* Sets the text field to the double value specified. If the text field type is -* FLOAT then the the number is display as a real number. If the text field -* type is INTEGER then the number is converted to a whole number for displaying. -* @param aValue A double value to display in the text field. -*/ - public void setDoubleValue(double aValue) - { - Double temp = new Double(aValue); - - if (valueType == FLOAT) - { - setText(temp.toString()); - } - else - { - setText(Integer.toString(temp.intValue())); - } - } - -/******************************* -* PROTECTED METHODS -*******************************/ - - protected boolean isValidCharacter(char aChar) - { - if (((aChar >= ' ') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~'))) - { - if (aChar == '.') - { - if ( valueType == FLOAT ) - { - return true; - } - else - { - return false; - } - } - else if (aChar == '-') - { - if ( getMinimumValue() < 0 ) - { - return true; - } - else - { - return false; - } - } - else if (aChar == '+') - { - if ( getMaximumValue() >= 0 ) - { - return true; - } - else - { - return false; - } - } - return false; - } - return true; - } - - protected boolean isValidString(String aString) - { - int iValue = 0; - double dValue = 0.0; - - String tempString = new String(scanForSignChar(aString)); - - if ( valueType == INTEGER ) - { - try - { - iValue = Integer.parseInt(tempString); - } - catch (NumberFormatException e1) - { - if ((tempString.compareTo("-") == 0) && (getMinimumValue() < 0.0)) - { - iValue = 0; - } - else - { - return false; - } - } - if ((((double)iValue) < getMinimumValue()) || (((double)iValue) > getMaximumValue())) - { - return false; - } - } - else - { - // Double.valueOf requires a zero before the decimal point - if ( tempString.startsWith( "." ) ) - { - tempString = "0" + tempString; - } - try - { - dValue = Double.valueOf(tempString).doubleValue(); - } - catch (NumberFormatException e2) - { - if ((tempString.compareTo("-") == 0) && (getMinimumValue() < 0.0)) - { - dValue = 0.0; - } - else - { - return false; - } - } - - if ((dValue < getMinimumValue()) || (dValue > getMaximumValue())) - { - return false; - } - } - - return true; - } - - protected void postProcessing() - { - if (sign) - { - setText(scanForSignChar(getText())); - setCaretPosition(newCaretPosition); - } - sign = false; - } - - -/******************************* -* PRIVATE METHODS -*******************************/ - - private String scanForSignChar(String aString) - { - String newString = ""; - boolean positive = false; - boolean negative = false; - int oldCaretPosition = getCaretPosition(); - int charactersAdded = 0; - - newCaretPosition = 0; - - if (aString.length() <= 0) - { - return aString; - } - - for (int i = 0; i < aString.length(); ++i) - { - switch (aString.charAt(i)) - { - case '+': positive = true; - break; - case '-': negative = true; - break; - default: newString += aString.charAt(i); - charactersAdded++; - break; - } - - if ((i + 1) == oldCaretPosition) - { - newCaretPosition = charactersAdded; - } - } - - if ((!(positive)) && (negative)) - { - newString = "-" + newString; - newCaretPosition++; - } - - if (positive || negative) - { - sign = true; - } - - return newString; - } + * NumericTextField is a "smart" text field that restricts the user's input. The + * input is restructed to numeric only wich can be of two types: integer and + * real numbers. A range can also be placed on the text field. The default type + * is integer, with the being (Integer.MIN_VALUE, Integer.MAX_VALUE). + * + * @author rob@straylight.princeton.com + * @author $Author: cgruber $ + * @version $Revision: 893 $ + */ +public class NumericTextField extends SmartTextField { + + /******************************* + * CONSTANTS + *******************************/ + + /** + * Restrict the text input to integers (whole numbers) only. + */ + public final static int INTEGER = 0; + + /** + * Restrict the text input to floating-point numbers only. + */ + public final static int FLOAT = 1; + + private Number maximumValue = null; + private Number minimumValue = null; + + private boolean sign = false; + private int newCaretPosition = 0; + private int valueType = INTEGER; + + /******************************* + * PUBLIC METHODS + *******************************/ + + /** + * Default constructor. + */ + public NumericTextField() { + this("", 0); + } + + /** + * Constructor. + * + * @param text The initial string the text field is set to. + */ + public NumericTextField(String text) { + this(text, 0); + } + + /** + * Constructor. + * + * @param columns Width of the text field (in characters). + */ + public NumericTextField(int columns) { + this("", columns); + } + + /** + * Constructor. + * + * @param text The initial string the text field is set to. + * @param columns Width of the text field (in characters). + */ + public NumericTextField(String text, int columns) { + super(text, columns); + } + + /** + * Sets the upper limit of the range of numbers to accept. + * + * @param newMaximumValue The maximum number accepted by the text field. + */ + public void setMaximumValue(double newMaximumValue) { + if (newMaximumValue >= 0) { + maximumValue = new Double(newMaximumValue); + } else { + maximumValue = null; + } + } + + /** + * Returns the upper limit of the range of numbers to accept. + * + * @return The maximum number accepted by this text field. + */ + public double getMaximumValue() { + if (valueType == INTEGER) { + return (maximumValue == null) ? (double) Integer.MAX_VALUE : maximumValue.doubleValue(); + } else { + return (maximumValue == null) ? Double.MAX_VALUE : maximumValue.doubleValue(); + } + } + + /** + * Sets the lower limit of the range of numbers to accept. + * + * @param newMinimumValue The minimum number accepted by the text field. + */ + public void setMinimumValue(double newMinimumValue) { + if (newMinimumValue <= 0) { + minimumValue = new Double(newMinimumValue); + } else { + minimumValue = null; + } + } + + /** + * Returns the lower limit of the range of numbers to accept. + * + * @return The minimum number accepted by this text field. + */ + public double getMinimumValue() { + if (valueType == INTEGER) { + return (minimumValue == null) ? (double) Integer.MIN_VALUE : minimumValue.doubleValue(); + } else { + return (minimumValue == null) ? -1.0 * Double.MAX_VALUE : minimumValue.doubleValue(); + // NOTE: Double.MIN_VALUE returns the smallest positive value - oooops. + } + } + + /** + * Sets which type of number this text field can accept. + * + * @see #INTEGER + * @see #FLOAT + * @param newValueType The type of number to accept. + */ + public void setValueType(int newValueType) { + if ((newValueType != INTEGER) && (newValueType != FLOAT)) { + valueType = INTEGER; + } else { + valueType = newValueType; + } + } + + /** + * Returns which type of number this text field accepts. The default is integer. + * + * @see #INTEGER + * @see #FLOAT + * @return The type of number to accept. + */ + public int getValueType() { + return valueType; + } + + /** + * Returns the integer numeric value of the string in the text field. The type + * can be either integer of float. + * + * @return The current value in the text field. + */ + public int getIntValue() { + int value = 0; + + try { + value = Integer.parseInt(getText()); + } catch (NumberFormatException e) { + try { + Double dValue = Double.valueOf(getText()); + value = dValue.intValue(); + } catch (NumberFormatException ignored) { + } + } + + return value; + } + + /** + * Sets the text field to integer value specified. + * + * @param aValue An integer value to display in the text field. + */ + public void setIntValue(int aValue) { + setText(Integer.toString(aValue)); + } + + /** + * Returns the real number numeric value of the string in the text field. The + * type can be either integer of float. + * + * @return The current value in the text field. + */ + public double getDoubleValue() { + Double value = new Double(0); + + try { + value = Double.valueOf(getText()); + } catch (NumberFormatException ignored) { + } + + return value.doubleValue(); + } + + /** + * Sets the text field to the double value specified. If the text field type is + * FLOAT then the the number is display as a real number. If the text field type + * is INTEGER then the number is converted to a whole number for displaying. + * + * @param aValue A double value to display in the text field. + */ + public void setDoubleValue(double aValue) { + Double temp = new Double(aValue); + + if (valueType == FLOAT) { + setText(temp.toString()); + } else { + setText(Integer.toString(temp.intValue())); + } + } + + /******************************* + * PROTECTED METHODS + *******************************/ + + protected boolean isValidCharacter(char aChar) { + if (((aChar >= ' ') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~'))) { + if (aChar == '.') { + if (valueType == FLOAT) { + return true; + } else { + return false; + } + } else if (aChar == '-') { + if (getMinimumValue() < 0) { + return true; + } else { + return false; + } + } else if (aChar == '+') { + if (getMaximumValue() >= 0) { + return true; + } else { + return false; + } + } + return false; + } + return true; + } + + protected boolean isValidString(String aString) { + int iValue = 0; + double dValue = 0.0; + + String tempString = new String(scanForSignChar(aString)); + + if (valueType == INTEGER) { + try { + iValue = Integer.parseInt(tempString); + } catch (NumberFormatException e1) { + if ((tempString.compareTo("-") == 0) && (getMinimumValue() < 0.0)) { + iValue = 0; + } else { + return false; + } + } + if ((((double) iValue) < getMinimumValue()) || (((double) iValue) > getMaximumValue())) { + return false; + } + } else { + // Double.valueOf requires a zero before the decimal point + if (tempString.startsWith(".")) { + tempString = "0" + tempString; + } + try { + dValue = Double.valueOf(tempString).doubleValue(); + } catch (NumberFormatException e2) { + if ((tempString.compareTo("-") == 0) && (getMinimumValue() < 0.0)) { + dValue = 0.0; + } else { + return false; + } + } + + if ((dValue < getMinimumValue()) || (dValue > getMaximumValue())) { + return false; + } + } + + return true; + } + + protected void postProcessing() { + if (sign) { + setText(scanForSignChar(getText())); + setCaretPosition(newCaretPosition); + } + sign = false; + } + + /******************************* + * PRIVATE METHODS + *******************************/ + + private String scanForSignChar(String aString) { + String newString = ""; + boolean positive = false; + boolean negative = false; + int oldCaretPosition = getCaretPosition(); + int charactersAdded = 0; + + newCaretPosition = 0; + + if (aString.length() <= 0) { + return aString; + } + + for (int i = 0; i < aString.length(); ++i) { + switch (aString.charAt(i)) { + case '+': + positive = true; + break; + case '-': + negative = true; + break; + default: + newString += aString.charAt(i); + charactersAdded++; + break; + } + + if ((i + 1) == oldCaretPosition) { + newCaretPosition = charactersAdded; + } + } + + if ((!(positive)) && (negative)) { + newString = "-" + newString; + newCaretPosition++; + } + + if (positive || negative) { + sign = true; + } + + return newString; + } } - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTable.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTable.java index 9db2834..996f8e0 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTable.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTable.java @@ -47,233 +47,235 @@ import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; /** -* PropertyEditorTable is a table designed to display and edit the properties -* of an object. Because JTable assumes all cells in a column display -* the same data type, we have to subclass to determine the class -* based on the cell contents. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ + * PropertyEditorTable is a table designed to display and edit the properties of + * an object. Because JTable assumes all cells in a column display the same data + * type, we have to subclass to determine the class based on the cell contents. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ public class PropertyEditorTable extends JTable { // // Constructors // - /** - * Constructs a default JTable which is initialized with a default - * data model, a default column model, and a default selection - * model. - * - * @see #createDefaultDataModel - * @see #createDefaultColumnModel - * @see #createDefaultSelectionModel - */ - public PropertyEditorTable() { - super(null, null, null); - } - - /** - * Constructs a JTable which is initialized with dm as the - * data model, a default column model, and a default selection - * model. - * - * @param dm The data model for the table - * @see #createDefaultColumnModel - * @see #createDefaultSelectionModel - */ - public PropertyEditorTable(TableModel dm) { - super(dm, null, null); - } - - /** - * Constructs a JTable which is initialized with dm as the - * data model, cm as the column model, and a default selection - * model. - * - * @param dm The data model for the table - * @param cm The column model for the table - * @see #createDefaultSelectionModel - */ - public PropertyEditorTable(TableModel dm, TableColumnModel cm) { - super(dm, cm, null); - } - - /** - * Constructs a JTable which is initialized with dm as the - * data model, cm as the column model, and sm as the - * selection model. If any of the parameters are null this - * method will initialize the table with the corresponding - * default model. The autoCreateColumnsFromModel flag is set - * to false if cm is non-null, otherwise it is set to true - * and the column model is populated with suitable TableColumns - * for the columns in dm. - * - * @param dm The data model for the table - * @param cm The column model for the table - * @param sm The row selection model for the table - * @see #createDefaultDataModel - * @see #createDefaultColumnModel - * @see #createDefaultSelectionModel - */ - public PropertyEditorTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) { - super( dm, cm, sm ); + /** + * Constructs a default JTable which is initialized with a default data model, a + * default column model, and a default selection model. + * + * @see #createDefaultDataModel + * @see #createDefaultColumnModel + * @see #createDefaultSelectionModel + */ + public PropertyEditorTable() { + super(null, null, null); } - /** - * Constructs a JTable with numRows and numColumns of - * empty cells using the DefaultTableModel. The columns will have - * names of the form "A", "B", "C", etc. - * - * @param numRows The number of rows the table holds - * @param numColumns The number of columns the table holds - */ - public PropertyEditorTable(int numRows, int numColumns) { - super( numRows, numColumns ); - } - - /** - * Constructs a JTable to display the values in the Vector of Vectors, - * rowData, with column names, columnNames. - * The Vectors contained in rowData should contain the values - * for that row. In other words, the value of the cell at row 1, - * column 5 can be obtained with the following code: - *

- *

((Vector)rowData.elementAt(1)).elementAt(5);
- *

- * All rows must be of the same length as columnNames. - *

- * @param rowData The data for the new table - * @param columnNames Names of each column - */ - public PropertyEditorTable(final Vector rowData, final Vector columnNames) { - super( rowData, columnNames ); - } - - /** - * Constructs a JTable to display the values in the two dimensional array, - * rowData, with column names, columnNames. - * rowData is an Array of rows, so the value of the cell at row 1, - * column 5 can be obtained with the following code: - *

- *

 rowData[1][5]; 
- *

- * All rows must be of the same length as columnNames. - *

- * @param rowData The data for the new table - * @param columnNames Names of each column - */ - public PropertyEditorTable(final Object[][] rowData, final Object[] columnNames) { - super( rowData, columnNames ); + /** + * Constructs a JTable which is initialized with dm as the data model, a + * default column model, and a default selection model. + * + * @param dm The data model for the table + * @see #createDefaultColumnModel + * @see #createDefaultSelectionModel + */ + public PropertyEditorTable(TableModel dm) { + super(dm, null, null); } - /** - * Returns the type of the column at the specified view position. - * - * @return the type of the column at position column in the view - * where the first column is column 0. + /** + * Constructs a JTable which is initialized with dm as the data model, + * cm as the column model, and a default selection model. * - * Modified mln: now a wrapper for getCellClass() + * @param dm The data model for the table + * @param cm The column model for the table + * @see #createDefaultSelectionModel + */ + public PropertyEditorTable(TableModel dm, TableColumnModel cm) { + super(dm, cm, null); + } + + /** + * Constructs a JTable which is initialized with dm as the data model, + * cm as the column model, and sm as the selection model. If any + * of the parameters are null this method will initialize the table with + * the corresponding default model. The autoCreateColumnsFromModel flag + * is set to false if cm is non-null, otherwise it is set to true and the + * column model is populated with suitable TableColumns for the columns in + * dm. * - */ - public Class getColumnClass(int column) { - return getCellClass( 0, column ); - } - - /** - * Returns the type of the cell at the specified view position. - * - * @return the type of the cell at position row, column in the view - * where the first column is column 0. + * @param dm The data model for the table + * @param cm The column model for the table + * @param sm The row selection model for the table + * @see #createDefaultDataModel + * @see #createDefaultColumnModel + * @see #createDefaultSelectionModel + */ + public PropertyEditorTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) { + super(dm, cm, sm); + } + + /** + * Constructs a JTable with numRows and numColumns of empty cells + * using the DefaultTableModel. The columns will have names of the form "A", + * "B", "C", etc. * - * Modified mln: new methods + * @param numRows The number of rows the table holds + * @param numColumns The number of columns the table holds + */ + public PropertyEditorTable(int numRows, int numColumns) { + super(numRows, numColumns); + } + + /** + * Constructs a JTable to display the values in the Vector of Vectors, + * rowData, with column names, columnNames. The Vectors contained + * in rowData should contain the values for that row. In other words, the + * value of the cell at row 1, column 5 can be obtained with the following code: + *

+ * + *

+	 * ((Vector) rowData.elementAt(1)).elementAt(5);
+	 * 
+ *

+ * All rows must be of the same length as columnNames. + *

+ * + * @param rowData The data for the new table + * @param columnNames Names of each column + */ + public PropertyEditorTable(final Vector rowData, final Vector columnNames) { + super(rowData, columnNames); + } + + /** + * Constructs a JTable to display the values in the two dimensional array, + * rowData, with column names, columnNames. rowData is an + * Array of rows, so the value of the cell at row 1, column 5 can be obtained + * with the following code: + *

+ * + *

+	 *  rowData[1][5];
+	 * 
+ *

+ * All rows must be of the same length as columnNames. + *

+ * + * @param rowData The data for the new table + * @param columnNames Names of each column + */ + public PropertyEditorTable(final Object[][] rowData, final Object[] columnNames) { + super(rowData, columnNames); + } + + /** + * Returns the type of the column at the specified view position. + * + * @return the type of the column at position column in the view where + * the first column is column 0. + * + * Modified mln: now a wrapper for getCellClass() + * + */ + public Class getColumnClass(int column) { + return getCellClass(0, column); + } + + /** + * Returns the type of the cell at the specified view position. + * + * @return the type of the cell at position row, column in the + * view where the first column is column 0. * - */ - public Class getCellClass(int row, int column) { + * Modified mln: new methods + * + */ + public Class getCellClass(int row, int column) { TableModel model = getModel(); - if ( model instanceof PropertyEditorTableModel ) - return ((PropertyEditorTableModel)model).getCellClass( row, column ); - else + if (model instanceof PropertyEditorTableModel) + return ((PropertyEditorTableModel) model).getCellClass(row, column); + else return model.getColumnClass(convertColumnIndexToModel(column)); - } - - /** - * Return an appropriate renderer for the cell specified by this this row and - * column. If the TableColumn for this column has a non-null renderer, return that. - * If not, find the class of the data in this column (using getColumnClass()) - * and return the default renderer for this type of data. - * - * @param row the row of the cell to render, where 0 is the first - * @param column the column of the cell to render, where 0 is the first + } + + /** + * Return an appropriate renderer for the cell specified by this this row and + * column. If the TableColumn for this column has a non-null renderer, return + * that. If not, find the class of the data in this column (using + * getColumnClass()) and return the default renderer for this type of data. + * + * @param row the row of the cell to render, where 0 is the first + * @param column the column of the cell to render, where 0 is the first + * + * Modified mln: calls getCellClass if there's no column model * - * Modified mln: calls getCellClass if there's no column model + */ + public TableCellRenderer getCellRenderer(int row, int column) { + TableColumn tableColumn = getColumnModel().getColumn(column); + TableCellRenderer renderer = tableColumn.getCellRenderer(); + if (renderer == null) { + renderer = getDefaultRenderer(getCellClass(row, column)); + } + return renderer; + } + + /** + * Return an appropriate editor for the cell specified by this this row and + * column. If the TableColumn for this column has a non-null editor, return + * that. If not, find the class of the data in this column (using + * getColumnClass()) and return the default editor for this type of data. * - */ - public TableCellRenderer getCellRenderer(int row, int column) { - TableColumn tableColumn = getColumnModel().getColumn(column); - TableCellRenderer renderer = tableColumn.getCellRenderer(); - if (renderer == null) { - renderer = getDefaultRenderer(getCellClass(row, column)); - } - return renderer; - } - - - /** - * Return an appropriate editor for the cell specified by this this row and - * column. If the TableColumn for this column has a non-null editor, return that. - * If not, find the class of the data in this column (using getColumnClass()) - * and return the default editor for this type of data. - * - * @param row the row of the cell to edit, where 0 is the first - * @param column the column of the cell to edit, where 0 is the first + * @param row the row of the cell to edit, where 0 is the first + * @param column the column of the cell to edit, where 0 is the first * - * Modified mp: calls getCellClass if there's no column model + * Modified mp: calls getCellClass if there's no column model * - */ - public TableCellEditor getCellEditor(int row, int column) { - TableColumn tableColumn = getColumnModel().getColumn(column); - TableCellEditor editor = tableColumn.getCellEditor(); - if (editor == null) { - editor = getDefaultEditor(getCellClass(row, column)); - } - return editor; - } + */ + public TableCellEditor getCellEditor(int row, int column) { + TableColumn tableColumn = getColumnModel().getColumn(column); + TableCellEditor editor = tableColumn.getCellEditor(); + if (editor == null) { + editor = getDefaultEditor(getCellClass(row, column)); + } + return editor; + } protected void createDefaultRenderers() { super.createDefaultRenderers(); -/* // copying this code here as a sample of creating a renderer - // Dates - DefaultTableCellRenderer dateRenderer = new DefaultTableCellRenderer() { - DateFormat formatter = DateFormat.getDateInstance(); - public void setValue(Object value) { - setText((value == null) ? "" : formatter.format(value)); } - }; - dateRenderer.setHorizontalAlignment(JLabel.RIGHT); - setDefaultRenderer(Date.class, dateRenderer); -*/ - - DefaultTableCellRenderer fontRenderer = new DefaultTableCellRenderer() { - public void setValue(Object value) { - setText( getFontDescription( (Font) value ) ); + /* + * // copying this code here as a sample of creating a renderer // Dates + * DefaultTableCellRenderer dateRenderer = new DefaultTableCellRenderer() { + * DateFormat formatter = DateFormat.getDateInstance(); public void + * setValue(Object value) { setText((value == null) ? "" : + * formatter.format(value)); } }; + * dateRenderer.setHorizontalAlignment(JLabel.RIGHT); + * setDefaultRenderer(Date.class, dateRenderer); + */ + + DefaultTableCellRenderer fontRenderer = new DefaultTableCellRenderer() { + public void setValue(Object value) { + setText(getFontDescription((Font) value)); } }; - fontRenderer.setHorizontalAlignment(JLabel.RIGHT); - setDefaultRenderer(Font.class, fontRenderer); + fontRenderer.setHorizontalAlignment(JLabel.RIGHT); + setDefaultRenderer(Font.class, fontRenderer); - setUpColorRenderer( this ); - setUpMethodRenderer( this ); - } + setUpColorRenderer(this); + setUpMethodRenderer(this); + } - protected String getFontDescription( Font f ) { + protected String getFontDescription(Font f) { String s; - if ( f != null ) { + if (f != null) { s = f.getName(); - if ( f.isBold() ) s += " Bold"; - if ( f.isItalic() ) s += " Italic"; + if (f.isBold()) + s += " Bold"; + if (f.isItalic()) + s += " Italic"; s += " " + f.getSize(); } else { s = ""; @@ -281,292 +283,263 @@ public class PropertyEditorTable extends JTable { return s; } - protected void createDefaultEditors() { + protected void createDefaultEditors() { super.createDefaultEditors(); -/* // copying this code here as a sample of creating an editor - // Numbers - JTextField rightAlignedTextField = new JTextField(); - rightAlignedTextField.setHorizontalAlignment(JTextField.RIGHT); - rightAlignedTextField.setBorder(new LineBorder(Color.black)); - setDefaultEditor(Number.class, new DefaultCellEditor(rightAlignedTextField)); - - // Booleans - JCheckBox centeredCheckBox = new JCheckBox(); - centeredCheckBox.setHorizontalAlignment(JCheckBox.CENTER); - setDefaultEditor(Boolean.class, new DefaultCellEditor(centeredCheckBox)); -*/ - setUpColorEditor( this ); - setUpMethodEditor( this ); + /* + * // copying this code here as a sample of creating an editor // Numbers + * JTextField rightAlignedTextField = new JTextField(); + * rightAlignedTextField.setHorizontalAlignment(JTextField.RIGHT); + * rightAlignedTextField.setBorder(new LineBorder(Color.black)); + * setDefaultEditor(Number.class, new DefaultCellEditor(rightAlignedTextField)); + * + * // Booleans JCheckBox centeredCheckBox = new JCheckBox(); + * centeredCheckBox.setHorizontalAlignment(JCheckBox.CENTER); + * setDefaultEditor(Boolean.class, new DefaultCellEditor(centeredCheckBox)); + */ + setUpColorEditor(this); + setUpMethodEditor(this); } - - // following code lifted from: + // following code lifted from: // http://java.sun.com/docs/books/tutorial/ui/swing/example-swing/TableDialogEditDemo.java - class ColorRenderer extends JLabel - implements TableCellRenderer { - Border unselectedBorder = null; - Border selectedBorder = null; - boolean isBordered = true; - - public ColorRenderer(boolean isBordered) { - super(); - this.isBordered = isBordered; - this.setOpaque(true); //MUST do this for background to show up. - } - - public Component getTableCellRendererComponent( - JTable table, Object color, - boolean isSelected, boolean hasFocus, - int row, int column) { - this.setBackground((Color)color); - if (isBordered) { - if (isSelected) { - if (selectedBorder == null) { - selectedBorder = BorderFactory.createMatteBorder(2,5,2,5, - table.getSelectionBackground()); - } - this.setBorder(selectedBorder); - } else { - if (unselectedBorder == null) { - unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5, - table.getBackground()); - } - this.setBorder(unselectedBorder); - } - } - return this; - } - } - - private void setUpColorRenderer(JTable table) { - table.setDefaultRenderer(Color.class, - new ColorRenderer(true)); - } - - //Set up the editor for the Color cells. - private void setUpColorEditor(JTable table) { - //First, set up the button that brings up the dialog. - final JButton button = new JButton("") { - public void setText(String s) { - //Button never shows text -- only color. - } - }; - button.setBackground(Color.white); - button.setBorderPainted(false); - button.setMargin(new Insets(0,0,0,0)); - - //Now create an editor to encapsulate the button, and - //set it up as the editor for all Color cells. - final ColorEditor colorEditor = new ColorEditor(button); - table.setDefaultEditor(Color.class, colorEditor); - - //Set up the dialog that the button brings up. - final JColorChooser colorChooser = new JColorChooser(); - //XXX: PENDING: add the following when setPreviewPanel - //XXX: starts working. - //JComponent preview = new ColorRenderer(false); - //preview.setPreferredSize(new Dimension(50, 10)); - //colorChooser.setPreviewPanel(preview); - ActionListener okListener = new ActionListener() { - public void actionPerformed(ActionEvent e) { - colorEditor.currentColor = colorChooser.getColor(); - } - }; - final JDialog dialog = JColorChooser.createDialog(button, - "Pick a Color", - true, - colorChooser, - okListener, - null); //XXXDoublecheck this is OK - - //Here's the code that brings up the dialog. - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - button.setBackground(colorEditor.currentColor); - colorChooser.setColor(colorEditor.currentColor); - //Without the following line, the dialog comes up - //in the middle of the screen. - //dialog.setLocationRelativeTo(button); - dialog.show(); - } - }); - } - - /* - * The editor button that brings up the dialog. - * We extend DefaultCellEditor for convenience, - * even though it mean we have to create a dummy - * check box. Another approach would be to copy - * the implementation of TableCellEditor methods - * from the source code for DefaultCellEditor. - */ - class ColorEditor extends DefaultCellEditor { - Color currentColor = null; - - public ColorEditor(JButton b) { - super(new JCheckBox()); //Unfortunately, the constructor - //expects a check box, combo box, - //or text field. - editorComponent = b; - setClickCountToStart(1); //This is usually 1 or 2. - - //Must do this so that editing stops when appropriate. - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - fireEditingStopped(); - } - }); - } - - protected void fireEditingStopped() { - super.fireEditingStopped(); - } - - public Object getCellEditorValue() { - return currentColor; - } - - public Component getTableCellEditorComponent(JTable table, - Object value, - boolean isSelected, - int row, - int column) { - ((JButton)editorComponent).setText(value.toString()); - currentColor = (Color)value; - return editorComponent; - } - } - - class MethodRenderer extends JLabel - implements TableCellRenderer { - - Method theMethod = null; - JTable theTable = null; - - public MethodRenderer() { - super(); - } - - public Component getTableCellRendererComponent( - JTable table, Object method, - boolean isSelected, boolean hasFocus, - int row, int column) { - theMethod = (Method) method; - theTable = table; - setText( " " + theMethod.getReturnType().getName() ); - return this; - } - } - - private void setUpMethodRenderer(JTable table) { - table.setDefaultRenderer(Method.class, - new MethodRenderer()); - } - - /* - * We extend DefaultCellEditor for convenience, - * as with ColorEditor. - */ - class MethodEditor extends DefaultCellEditor { - Method theMethod = null; - JTable theTable = null; - - public MethodEditor(JButton b) { - super(new JCheckBox()); //Unfortunately, the constructor - //expects a check box, combo box, - //or text field. - editorComponent = b; - setClickCountToStart(1); //This is usually 1 or 2. - - //Must do this so that editing stops when appropriate. - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - fireEditingStopped(); - } - }); - } - - protected void fireEditingStopped() { - super.fireEditingStopped(); - } - - public Object getCellEditorValue() { - return theMethod; - } - - public Component getTableCellEditorComponent(JTable table, - Object value, - boolean isSelected, - int row, - int column) { - ((JButton)editorComponent).setText(value.toString()); - theMethod = (Method)value; - theTable = table; - return editorComponent; - } - } - - //Set up the editor for the Method cells. - private void setUpMethodEditor(PropertyEditorTable table) { - //First, set up the button that brings up the dialog. - final JButton button = new JButton("invoking method") { - public void setText(String s) { - //Button never shows text -- only color. - } - }; - button.setBackground(Color.white); - button.setBorderPainted(false); - button.setMargin(new Insets(0,0,0,0)); - - //Now create an editor to encapsulate the button, and - //set it up as the editor for all Color cells. - final MethodEditor methodEditor = new MethodEditor(button); - table.setDefaultEditor(Method.class, methodEditor); - - // handle the button-click - final PropertyEditorTable theTable = table; - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - - Component parent = SwingUtilities.getRoot( theTable ); - if ( parent == null ) parent = theTable; - - Cursor oldCursor = parent.getCursor(); - parent.setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) ); - - Object result = null; - Object inspectedObject = ((PropertyEditorTableModel) - methodEditor.theTable.getModel()).inspectedObject; - try - { - methodEditor.theMethod.setAccessible( true ); - result = methodEditor.theMethod.invoke( - inspectedObject, (Object[])null ); - } - catch ( Exception exc ) - { - System.err.println( "PropertyEditorTable.MethodRenderer.actionPerformed: " + - "Error occurred: " + exc ); - } - theTable.methodInvoked( inspectedObject, methodEditor.theMethod, result ); - - parent.setCursor( oldCursor ); - } - }); - } + class ColorRenderer extends JLabel implements TableCellRenderer { + Border unselectedBorder = null; + Border selectedBorder = null; + boolean isBordered = true; -/** -* Called by the method cell editor when a method is invoked. -* @param anObject The object upon which the method was invoked. -* @param aMethod The method that was invoked. -* @param aResult The result of the method invocation; may be null. -*/ - public void methodInvoked( Object anObject, Method aMethod, Object aResult ) - { - System.out.println( aMethod.getName() + ": " + aResult ); - } -} + public ColorRenderer(boolean isBordered) { + super(); + this.isBordered = isBordered; + this.setOpaque(true); // MUST do this for background to show up. + } + public Component getTableCellRendererComponent(JTable table, Object color, boolean isSelected, boolean hasFocus, + int row, int column) { + this.setBackground((Color) color); + if (isBordered) { + if (isSelected) { + if (selectedBorder == null) { + selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getSelectionBackground()); + } + this.setBorder(selectedBorder); + } else { + if (unselectedBorder == null) { + unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getBackground()); + } + this.setBorder(unselectedBorder); + } + } + return this; + } + } + + private void setUpColorRenderer(JTable table) { + table.setDefaultRenderer(Color.class, new ColorRenderer(true)); + } + // Set up the editor for the Color cells. + private void setUpColorEditor(JTable table) { + // First, set up the button that brings up the dialog. + final JButton button = new JButton("") { + public void setText(String s) { + // Button never shows text -- only color. + } + }; + button.setBackground(Color.white); + button.setBorderPainted(false); + button.setMargin(new Insets(0, 0, 0, 0)); + + // Now create an editor to encapsulate the button, and + // set it up as the editor for all Color cells. + final ColorEditor colorEditor = new ColorEditor(button); + table.setDefaultEditor(Color.class, colorEditor); + + // Set up the dialog that the button brings up. + final JColorChooser colorChooser = new JColorChooser(); + // XXX: PENDING: add the following when setPreviewPanel + // XXX: starts working. + // JComponent preview = new ColorRenderer(false); + // preview.setPreferredSize(new Dimension(50, 10)); + // colorChooser.setPreviewPanel(preview); + ActionListener okListener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + colorEditor.currentColor = colorChooser.getColor(); + } + }; + final JDialog dialog = JColorChooser.createDialog(button, "Pick a Color", true, colorChooser, okListener, null); // XXXDoublecheck + // this + // is + // OK + + // Here's the code that brings up the dialog. + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + button.setBackground(colorEditor.currentColor); + colorChooser.setColor(colorEditor.currentColor); + // Without the following line, the dialog comes up + // in the middle of the screen. + // dialog.setLocationRelativeTo(button); + dialog.show(); + } + }); + } + + /* + * The editor button that brings up the dialog. We extend DefaultCellEditor for + * convenience, even though it mean we have to create a dummy check box. Another + * approach would be to copy the implementation of TableCellEditor methods from + * the source code for DefaultCellEditor. + */ + class ColorEditor extends DefaultCellEditor { + Color currentColor = null; + + public ColorEditor(JButton b) { + super(new JCheckBox()); // Unfortunately, the constructor + // expects a check box, combo box, + // or text field. + editorComponent = b; + setClickCountToStart(1); // This is usually 1 or 2. + + // Must do this so that editing stops when appropriate. + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + fireEditingStopped(); + } + }); + } + + protected void fireEditingStopped() { + super.fireEditingStopped(); + } + + public Object getCellEditorValue() { + return currentColor; + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, + int column) { + ((JButton) editorComponent).setText(value.toString()); + currentColor = (Color) value; + return editorComponent; + } + } + + class MethodRenderer extends JLabel implements TableCellRenderer { + + Method theMethod = null; + JTable theTable = null; + + public MethodRenderer() { + super(); + } + + public Component getTableCellRendererComponent(JTable table, Object method, boolean isSelected, + boolean hasFocus, int row, int column) { + theMethod = (Method) method; + theTable = table; + setText(" " + theMethod.getReturnType().getName()); + return this; + } + } + + private void setUpMethodRenderer(JTable table) { + table.setDefaultRenderer(Method.class, new MethodRenderer()); + } + + /* + * We extend DefaultCellEditor for convenience, as with ColorEditor. + */ + class MethodEditor extends DefaultCellEditor { + Method theMethod = null; + JTable theTable = null; + + public MethodEditor(JButton b) { + super(new JCheckBox()); // Unfortunately, the constructor + // expects a check box, combo box, + // or text field. + editorComponent = b; + setClickCountToStart(1); // This is usually 1 or 2. + + // Must do this so that editing stops when appropriate. + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + fireEditingStopped(); + } + }); + } + + protected void fireEditingStopped() { + super.fireEditingStopped(); + } + + public Object getCellEditorValue() { + return theMethod; + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, + int column) { + ((JButton) editorComponent).setText(value.toString()); + theMethod = (Method) value; + theTable = table; + return editorComponent; + } + } + + // Set up the editor for the Method cells. + private void setUpMethodEditor(PropertyEditorTable table) { + // First, set up the button that brings up the dialog. + final JButton button = new JButton("invoking method") { + public void setText(String s) { + // Button never shows text -- only color. + } + }; + button.setBackground(Color.white); + button.setBorderPainted(false); + button.setMargin(new Insets(0, 0, 0, 0)); + + // Now create an editor to encapsulate the button, and + // set it up as the editor for all Color cells. + final MethodEditor methodEditor = new MethodEditor(button); + table.setDefaultEditor(Method.class, methodEditor); + + // handle the button-click + final PropertyEditorTable theTable = table; + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + + Component parent = SwingUtilities.getRoot(theTable); + if (parent == null) + parent = theTable; + + Cursor oldCursor = parent.getCursor(); + parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + Object result = null; + Object inspectedObject = ((PropertyEditorTableModel) methodEditor.theTable.getModel()).inspectedObject; + try { + methodEditor.theMethod.setAccessible(true); + result = methodEditor.theMethod.invoke(inspectedObject, (Object[]) null); + } catch (Exception exc) { + System.err + .println("PropertyEditorTable.MethodRenderer.actionPerformed: " + "Error occurred: " + exc); + } + theTable.methodInvoked(inspectedObject, methodEditor.theMethod, result); + + parent.setCursor(oldCursor); + } + }); + } + + /** + * Called by the method cell editor when a method is invoked. + * + * @param anObject The object upon which the method was invoked. + * @param aMethod The method that was invoked. + * @param aResult The result of the method invocation; may be null. + */ + public void methodInvoked(Object anObject, Method aMethod, Object aResult) { + System.out.println(aMethod.getName() + ": " + aResult); + } +} diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTableModel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTableModel.java index f6a2a8d..f6f80f2 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTableModel.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTableModel.java @@ -31,30 +31,28 @@ import javax.swing.Timer; import javax.swing.table.AbstractTableModel; /** -* PropertyEditorTableModel introspects an object to facilitate -* editing it in a PropertyTable. -* -* Because the model always reflects the current state of the -* inspected object, it is useful to have a table update at -* automated intervals. By default, this feature is turned off. -* If you turn it on, you'll want to remember to turn it off -* when you're done with the table model. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ -public class PropertyEditorTableModel extends AbstractTableModel implements ActionListener -{ + * PropertyEditorTableModel introspects an object to facilitate editing it in a + * PropertyTable. + * + * Because the model always reflects the current state of the inspected object, + * it is useful to have a table update at automated intervals. By default, this + * feature is turned off. If you turn it on, you'll want to remember to turn it + * off when you're done with the table model. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ +public class PropertyEditorTableModel extends AbstractTableModel implements ActionListener { protected Object inspectedObject = null; final String[] columnNames = { "Property", "Value" }; - static final String METHOD_TAG = " "; + static final String METHOD_TAG = " "; Vector properties = new Vector(); Hashtable methods = new Hashtable(0); - public void setObject( Object o ) { + public void setObject(Object o) { inspectedObject = o; Class c = o.getClass(); Method[] m = c.getMethods(); @@ -62,67 +60,56 @@ public class PropertyEditorTableModel extends AbstractTableModel implements Acti properties = new Vector(); methods = new Hashtable(m.length, 1F); String name, propertyName; - for ( int i = 0; i < m.length; i++ ) { + for (int i = 0; i < m.length; i++) { // if ( m[i].getName().startsWith( "get" ) ) // System.out.println( m[i].getName() + ": " + m[i].getReturnType().getName() ); // get methods - if ( - ( ( m[i].getName().startsWith( "get" ) ) || ( m[i].getName().startsWith( "is" ) ) ) - && ( m[i].getParameterTypes().length == 0 ) - ) { - name = m[i].getName(); - if ( m[i].getName().startsWith( "is" ) ) { - propertyName = name.substring( 2, name.length() ); - // probably should only add "is" methods if accompanied by "set" method - } else { // "get" - propertyName = name.substring( 3, name.length() ); - } - if ( - ( m[i].getReturnType().getName().equals( String.class.getName() ) ) - || ( m[i].getReturnType().getName().equals( "boolean" ) ) - || ( m[i].getReturnType().getName().equals( "int" ) ) - || ( m[i].getReturnType().getName().equals( "float" ) ) - || ( m[i].getReturnType().getName().equals( "char" ) ) - || ( m[i].getReturnType().getName().equals( Color.class.getName() ) ) - || ( m[i].getReturnType().getName().equals( Font.class.getName() ) ) - ) { - properties.addElement( propertyName ); // put property - methods.put( name, m[i] ); // put method + if (((m[i].getName().startsWith("get")) || (m[i].getName().startsWith("is"))) + && (m[i].getParameterTypes().length == 0)) { + name = m[i].getName(); + if (m[i].getName().startsWith("is")) { + propertyName = name.substring(2, name.length()); + // probably should only add "is" methods if accompanied by "set" method + } else { // "get" + propertyName = name.substring(3, name.length()); } - else - { - // handle unknown types as invokable methods: - properties.addElement( propertyName + METHOD_TAG ); - methods.put( propertyName + METHOD_TAG, m[i] ); - } - } - else + if ((m[i].getReturnType().getName().equals(String.class.getName())) + || (m[i].getReturnType().getName().equals("boolean")) + || (m[i].getReturnType().getName().equals("int")) + || (m[i].getReturnType().getName().equals("float")) + || (m[i].getReturnType().getName().equals("char")) + || (m[i].getReturnType().getName().equals(Color.class.getName())) + || (m[i].getReturnType().getName().equals(Font.class.getName()))) { + properties.addElement(propertyName); // put property + methods.put(name, m[i]); // put method + } else { + // handle unknown types as invokable methods: + properties.addElement(propertyName + METHOD_TAG); + methods.put(propertyName + METHOD_TAG, m[i]); + } + } else // set methods - if ( ( m[i].getName().startsWith( "set" ) ) && - ( m[i].getParameterTypes().length == 1 ) ) { + if ((m[i].getName().startsWith("set")) && (m[i].getParameterTypes().length == 1)) { name = m[i].getName(); - if ( - ( m[i].getParameterTypes()[0].getName().equals( String.class.getName() ) ) - || ( m[i].getParameterTypes()[0].getName().equals( "boolean" ) ) - || ( m[i].getParameterTypes()[0].getName().equals( "int" ) ) - || ( m[i].getParameterTypes()[0].getName().equals( "float" ) ) - || ( m[i].getParameterTypes()[0].getName().equals( Color.class.getName() ) ) + if ((m[i].getParameterTypes()[0].getName().equals(String.class.getName())) + || (m[i].getParameterTypes()[0].getName().equals("boolean")) + || (m[i].getParameterTypes()[0].getName().equals("int")) + || (m[i].getParameterTypes()[0].getName().equals("float")) + || (m[i].getParameterTypes()[0].getName().equals(Color.class.getName())) // || ( m[i].getParameterTypes()[0].getName().equals( Font.class.getName() ) ) ) { // System.out.println( "Accepted: " + name + ": " + m[i].getParameterTypes()[0].getName() ); - methods.put( name, m[i] ); // set method + methods.put(name, m[i]); // set method } else { // System.out.println( "Rejected: " + name + ": " + m[i].getParameterTypes()[0].getName() ); } + } else + // zero-parameter methods to be invoked on click + if (m[i].getParameterTypes().length == 0) { + properties.addElement(m[i].getName() + METHOD_TAG); + methods.put(m[i].getName() + METHOD_TAG, m[i]); } - else - // zero-parameter methods to be invoked on click - if ( m[i].getParameterTypes().length == 0 ) - { - properties.addElement( m[i].getName() + METHOD_TAG ); - methods.put( m[i].getName() + METHOD_TAG, m[i] ); - } } @@ -131,288 +118,259 @@ public class PropertyEditorTableModel extends AbstractTableModel implements Acti } public int getColumnCount() { - return columnNames.length; + return columnNames.length; } public int getRowCount() { - return properties.size(); + return properties.size(); } public String getColumnName(int col) { - return columnNames[col]; + return columnNames[col]; } public Object getValueAt(int row, int col) { - if ( col == 0 ) - return properties.elementAt( row ); - else - { + if (col == 0) + return properties.elementAt(row); + else { Method m = null; - m = (Method) methods.get( "get" + ( (String) properties.elementAt( row ) ) ) ; - if ( m == null ) // try "is" - m = (Method) methods.get( "is" + ( (String) properties.elementAt( row ) ) ) ; - if ( m == null ) { // try entire method name - m = (Method) methods.get( (String) properties.elementAt( row ) ) ; - if ( m != null ) return m; - } + m = (Method) methods.get("get" + ((String) properties.elementAt(row))); + if (m == null) // try "is" + m = (Method) methods.get("is" + ((String) properties.elementAt(row))); + if (m == null) { // try entire method name + m = (Method) methods.get((String) properties.elementAt(row)); + if (m != null) + return m; + } try { - return m.invoke( inspectedObject, (Object[])null ); - } catch ( Exception exc ) { - System.out.println( "InspectorFrame.tableModel.getValueAt: error occured while reflecting: " ); - System.out.println( exc ); + return m.invoke(inspectedObject, (Object[]) null); + } catch (Exception exc) { + System.out.println("InspectorFrame.tableModel.getValueAt: error occured while reflecting: "); + System.out.println(exc); } return null; } } - public Class getColumnClass( int col ) { + public Class getColumnClass(int col) { // System.out.println( "getColumnClass" ); -/* try { - throw new Exception(); - } catch ( Exception exc ) { - exc.printStackTrace( System.out ); - } -*/ return new String().getClass(); + /* + * try { throw new Exception(); } catch ( Exception exc ) { exc.printStackTrace( + * System.out ); } + */ return new String().getClass(); } public Class getCellClass(int row, int col) { // System.out.println( "getCellClass" ); -/* - - Class c; - Method m = (Method) methods.get( "set" + ( (String) properties.elementAt( row ) ) ) ; - if ( m == null ) - c = new Object().getClass(); - else { - c = m.getParameterTypes()[0]; - - // special case for boolean - if ( c.getName().equals( "boolean" ) ) - c = new Boolean(true).getClass(); - - // special case for int - if ( c.getName().equals( "int" ) ) - c = new Integer(0).getClass(); - } - System.out.println( row + ": " + c.getName() ); - return c; -*/ + /* + * + * Class c; Method m = (Method) methods.get( "set" + ( (String) + * properties.elementAt( row ) ) ) ; if ( m == null ) c = new + * Object().getClass(); else { c = m.getParameterTypes()[0]; + * + * // special case for boolean if ( c.getName().equals( "boolean" ) ) c = new + * Boolean(true).getClass(); + * + * // special case for int if ( c.getName().equals( "int" ) ) c = new + * Integer(0).getClass(); } System.out.println( row + ": " + c.getName() ); + * return c; + */ // return new String().getClass(); - Object o = getValueAt( row, col ); - if ( o == null ) o = "null"; + Object o = getValueAt(row, col); + if (o == null) + o = "null"; return o.getClass(); } /* - * Don't need to implement this method unless your table's - * editable. - */ + * Don't need to implement this method unless your table's editable. + */ public boolean isCellEditable(int row, int col) { - //Note that the data/cell address is constant, - //no matter where the cell appears onscreen. - if (col < 1) { - return false; - } else { - // handle method invocation - if ( ((String)properties.elementAt(row)).endsWith(METHOD_TAG) ) return true; - // handle read-only properties - Method m = (Method) methods.get( "set" + ( (String) properties.elementAt( row ) ) ) ; - if ( m == null ) + // Note that the data/cell address is constant, + // no matter where the cell appears onscreen. + if (col < 1) { return false; - else - return true; - } + } else { + // handle method invocation + if (((String) properties.elementAt(row)).endsWith(METHOD_TAG)) + return true; + // handle read-only properties + Method m = (Method) methods.get("set" + ((String) properties.elementAt(row))); + if (m == null) + return false; + else + return true; + } } /* - * Don't need to implement this method unless your table's - * data can change. - */ + * Don't need to implement this method unless your table's data can change. + */ public void setValueAt(Object value, int row, int col) { - // test for inspected object - if ( inspectedObject == null ) return; - // handle method invocation - no need to update values - if ( ((String)properties.elementAt(row)).endsWith( METHOD_TAG ) ) - { - fireTableDataChanged(); - return; - }; - - // handle writable properties - Method m = (Method) methods.get( "set" + ( (String) properties.elementAt( row ) ) ) ; + // test for inspected object + if (inspectedObject == null) + return; + // handle method invocation - no need to update values + if (((String) properties.elementAt(row)).endsWith(METHOD_TAG)) { + fireTableDataChanged(); + return; + } + ; + + // handle writable properties + Method m = (Method) methods.get("set" + ((String) properties.elementAt(row))); String parameterType = m.getParameterTypes()[0].getName(); // ugly cast code - if ( - ( parameterType.equals( "int" ) ) - || ( parameterType.equals( "java.lang.Integer" ) ) - ) - { - try { - value = new Integer((String)value); - } catch (NumberFormatException e) { - System.out.println("PropertyEditorTableModel.setValueAt: User attempted to enter non-integer" - + " value (" + value - + ") into an integer-only column."); - } + if ((parameterType.equals("int")) || (parameterType.equals("java.lang.Integer"))) { + try { + value = new Integer((String) value); + } catch (NumberFormatException e) { + System.out.println("PropertyEditorTableModel.setValueAt: User attempted to enter non-integer" + + " value (" + value + ") into an integer-only column."); + } } Object[] parameters = { value }; try { - m.invoke( inspectedObject, parameters ); - if ( inspectedObject instanceof Component ) { - Component c = (Component)inspectedObject; - if ( c.getParent() != null ) + m.invoke(inspectedObject, parameters); + if (inspectedObject instanceof Component) { + Component c = (Component) inspectedObject; + if (c.getParent() != null) c.getParent().repaint(); } - } catch ( Exception exc ) { - System.out.println( "PropertyEditorTableModel.setValueAt: error occured while reflecting: " ); - System.out.println( exc ); + } catch (Exception exc) { + System.out.println("PropertyEditorTableModel.setValueAt: error occured while reflecting: "); + System.out.println(exc); } fireTableDataChanged(); } + protected void sort(Vector v) { + quickSort(v, 0, v.size() - 1); + } - protected void sort(Vector v){ - quickSort(v, 0, v.size()-1); - } - - - // Liberated from the BasicDirectoryModel which was... - // Liberated from the 1.1 SortDemo - - // This is a generic version of C.A.R Hoare's Quick Sort - // algorithm. This will handle arrays that are already - // sorted, and arrays with duplicate keys.
- // - // If you think of a one dimensional array as going from - // the lowest index on the left to the highest index on the right - // then the parameters to this function are lowest index or - // left and highest index or right. The first time you call - // this function it will be with the parameters 0, a.length - 1. - // - // @param a an integer array - // @param lo0 left boundary of array partition - // @param hi0 right boundary of array partition - private void quickSort(Vector v, int lo0, int hi0) { - int lo = lo0; - int hi = hi0; - String mid; - - if (hi0 > lo0) { - // Arbitrarily establishing partition element as the midpoint of - // the array. - mid = (String) v.elementAt((lo0 + hi0) / 2); - - // loop through the array until indices cross - while(lo <= hi) { - // find the first element that is greater than or equal to - // the partition element starting from the left Index. - // - // Nasty to have to cast here. Would it be quicker - // to copy the vectors into arrays and sort the arrays? - while((lo < hi0) && lt((String)v.elementAt(lo), mid)) { - ++lo; - } + // Liberated from the BasicDirectoryModel which was... + // Liberated from the 1.1 SortDemo + + // This is a generic version of C.A.R Hoare's Quick Sort + // algorithm. This will handle arrays that are already + // sorted, and arrays with duplicate keys.
+ // + // If you think of a one dimensional array as going from + // the lowest index on the left to the highest index on the right + // then the parameters to this function are lowest index or + // left and highest index or right. The first time you call + // this function it will be with the parameters 0, a.length - 1. + // + // @param a an integer array + // @param lo0 left boundary of array partition + // @param hi0 right boundary of array partition + private void quickSort(Vector v, int lo0, int hi0) { + int lo = lo0; + int hi = hi0; + String mid; + + if (hi0 > lo0) { + // Arbitrarily establishing partition element as the midpoint of + // the array. + mid = (String) v.elementAt((lo0 + hi0) / 2); + + // loop through the array until indices cross + while (lo <= hi) { + // find the first element that is greater than or equal to + // the partition element starting from the left Index. + // + // Nasty to have to cast here. Would it be quicker + // to copy the vectors into arrays and sort the arrays? + while ((lo < hi0) && lt((String) v.elementAt(lo), mid)) { + ++lo; + } + + // find an element that is smaller than or equal to + // the partition element starting from the right Index. + while ((hi > lo0) && lt(mid, (String) v.elementAt(hi))) { + --hi; + } + + // if the indexes have not crossed, swap + if (lo <= hi) { + swap(v, lo, hi); + ++lo; + --hi; + } + } + + // If the right index has not reached the left side of array + // must now sort the left partition. + if (lo0 < hi) { + quickSort(v, lo0, hi); + } + + // If the left index has not reached the right side of array + // must now sort the right partition. + if (lo < hi0) { + quickSort(v, lo, hi0); + } - // find an element that is smaller than or equal to - // the partition element starting from the right Index. - while((hi > lo0) && lt(mid, (String)v.elementAt(hi))) { - --hi; } + } + + private void swap(Vector a, int i, int j) { + Object T = a.elementAt(i); + a.setElementAt(a.elementAt(j), i); + a.setElementAt(T, j); + } + + protected boolean lt(String a, String b) { + return a.compareTo(b) < 0; + } + + // automated updates - // if the indexes have not crossed, swap - if(lo <= hi) { - swap(v, lo, hi); - ++lo; - --hi; + private boolean autoUpdating = false; + private int updateInterval = 2000; // one-second delay on average + protected Timer timer = null; + + public boolean isAutoUpdating() { + return autoUpdating; + } + + public void setAutoUpdating(boolean shouldAutoUpdate) { + if (shouldAutoUpdate) { + if (timer == null) { + timer = new Timer(updateInterval, this); + timer.setRepeats(true); + timer.start(); + } + } else { + if (timer != null) { + timer.stop(); + timer = null; + } } - } + autoUpdating = shouldAutoUpdate; + } + + public int getUpdateInterval() { + return updateInterval; + } - // If the right index has not reached the left side of array - // must now sort the left partition. - if(lo0 < hi) { - quickSort(v, lo0, hi); - } + public void setUpdateInterval(int anInterval) { + if (timer != null) { + timer.setDelay(anInterval); + } - // If the left index has not reached the right side of array - // must now sort the right partition. - if(lo < hi0) { - quickSort(v, lo, hi0); - } + updateInterval = anInterval; + } + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == timer) { + fireTableDataChanged(); + } } - } - - private void swap(Vector a, int i, int j) { - Object T = a.elementAt(i); - a.setElementAt(a.elementAt(j), i); - a.setElementAt(T, j); - } - - protected boolean lt(String a, String b) { - return a.compareTo(b) < 0; - } - - // automated updates - - private boolean autoUpdating = false; - private int updateInterval = 2000; // one-second delay on average - protected Timer timer = null; - - public boolean isAutoUpdating() - { - return autoUpdating; - } - - public void setAutoUpdating( boolean shouldAutoUpdate ) - { - if ( shouldAutoUpdate ) - { - if ( timer == null ) - { - timer = new Timer( updateInterval, this ); - timer.setRepeats( true ); - timer.start(); - } - } - else - { - if ( timer != null ) - { - timer.stop(); - timer = null; - } - } - - autoUpdating = shouldAutoUpdate; - } - - public int getUpdateInterval() - { - return updateInterval; - } - - public void setUpdateInterval( int anInterval ) - { - if ( timer != null ) - { - timer.setDelay( anInterval ); - } - - updateInterval = anInterval; - } - - public void actionPerformed( ActionEvent evt ) - { - if ( evt.getSource() == timer ) - { - fireTableDataChanged(); - } - } } - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/RadioButtonPanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/RadioButtonPanel.java index 2956c71..62cbc2c 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/RadioButtonPanel.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/RadioButtonPanel.java @@ -26,149 +26,139 @@ import javax.swing.JRadioButton; import javax.swing.border.EmptyBorder; /** -* RadioButtonPanel is a simple extension of ButtonPanel. -* Differences are that it uses radio buttons and the -* default alignment is vertical. The radio buttons are -* placed in a ButtonGroup and the panel defaults to having -* no buttons selected. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $ -*/ -public class RadioButtonPanel extends ButtonPanel -{ -/** -* A ButtonGroup to help manage button state. -*/ - protected ButtonGroup buttonGroup; - -/** -* ButtonGroup does not make it easy to unselect all buttons. -* The preferred way to do it is actually to create a hidden button. -*/ - protected JRadioButton hiddenButton; - -/** -* Constructs a RadioButtonPanel. Three buttons are created -* so the panel is filled when used in a GUI-builder environment. -*/ - public RadioButtonPanel() - { - super(); - } - -/** -* Constructs a ButtonPanel using specified buttons. -* @param buttonList An array containing the strings to be used in labeling the buttons. -*/ - public RadioButtonPanel( String[] buttonList ) - { - super( buttonList ); - } - -/** -* Overridden to set vertical-center alignment and 2-pixel vgap. -*/ - protected void initLayout() - { - super.initLayout(); - buttonPanelLayout.setAlignment( BetterFlowLayout.CENTER_VERTICAL ); - buttonPanelLayout.setVgap( 2 ); // looks nicer than java l&f recommendation (imho) - } - -/** -* Overridden to return a JRadioButton. -* @param aLabel The label for the component that will be created. -* @return The newly created component. -*/ - protected Component createComponentWithLabel( String aLabel ) - { - String buttonLabel = aLabel; - JRadioButton newButton = new JRadioButton(); - newButton.setName( aLabel ); - newButton.setText( buttonLabel ); - newButton.setActionCommand( aLabel ); - newButton.addActionListener( this ); - - // reduce insets per java l&f guidelines (was 4 on each side) - newButton.setBorder( new EmptyBorder( 0, 4, 0, 4 ) ); - - if ( buttonGroup == null ) - { - buttonGroup = new ButtonGroup(); - - // cheesy hack to allow a buttongroup to have no items selected. - // note that the button is not added to container or buttonList. - hiddenButton = new JRadioButton( "Hidden Button" ); - buttonGroup.add( hiddenButton ); - } - buttonGroup.add( newButton ); - - return newButton; - } - -/** -* Selects the button whose name matches the given text value. -* @param newText A String matching the name of one of the buttons. -* If null, empty, or not matching, all buttons are deselected. -*/ - public void setValue(String aName) - { - if ( aName != null ) - { - Component c = null; - int count = buttonContainer.getComponentCount(); - for ( int i = 0; i < count; i++ ) - { - c = buttonContainer.getComponent( i ); - if ( c instanceof AbstractButton ) - { - if ( c.getName().equals( aName ) ) - { - ((AbstractButton)c).setSelected( true ); - return; - } - } - } - } - - // null, empty, or not matching - deselect all - hiddenButton.setSelected( true ); - } - -/** -* Gets the name of the currently selected button. -* @return A string matching the name of the currently selected button, -* or null of no button is selected. -*/ - public String getValue() - { - String result = null; - Component c = null; - int count = buttonContainer.getComponentCount(); - for ( int i = 0; i < count; i++ ) - { - c = buttonContainer.getComponent( i ); - if ( ( c instanceof AbstractButton ) && ( ((AbstractButton)c).isSelected() ) ) - { - return c.getName(); - } - } - return result; - } - -/** -* Tests whether the specified value is checked. -* @param aValue A value to be tested. -* @return True if the specified value is checked, otherwise false. -*/ - public boolean getValue( String aValue ) - { - if ( aValue == null ) return false; - return aValue.equals( getValue() ); - } + * RadioButtonPanel is a simple extension of ButtonPanel. Differences are that + * it uses radio buttons and the default alignment is vertical. The radio + * buttons are placed in a ButtonGroup and the panel defaults to having no + * buttons selected. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) + * $ + */ +public class RadioButtonPanel extends ButtonPanel { + /** + * A ButtonGroup to help manage button state. + */ + protected ButtonGroup buttonGroup; + + /** + * ButtonGroup does not make it easy to unselect all buttons. The preferred way + * to do it is actually to create a hidden button. + */ + protected JRadioButton hiddenButton; + + /** + * Constructs a RadioButtonPanel. Three buttons are created so the panel is + * filled when used in a GUI-builder environment. + */ + public RadioButtonPanel() { + super(); + } + + /** + * Constructs a ButtonPanel using specified buttons. + * + * @param buttonList An array containing the strings to be used in labeling the + * buttons. + */ + public RadioButtonPanel(String[] buttonList) { + super(buttonList); + } + + /** + * Overridden to set vertical-center alignment and 2-pixel vgap. + */ + protected void initLayout() { + super.initLayout(); + buttonPanelLayout.setAlignment(BetterFlowLayout.CENTER_VERTICAL); + buttonPanelLayout.setVgap(2); // looks nicer than java l&f recommendation (imho) + } + + /** + * Overridden to return a JRadioButton. + * + * @param aLabel The label for the component that will be created. + * @return The newly created component. + */ + protected Component createComponentWithLabel(String aLabel) { + String buttonLabel = aLabel; + JRadioButton newButton = new JRadioButton(); + newButton.setName(aLabel); + newButton.setText(buttonLabel); + newButton.setActionCommand(aLabel); + newButton.addActionListener(this); + + // reduce insets per java l&f guidelines (was 4 on each side) + newButton.setBorder(new EmptyBorder(0, 4, 0, 4)); + + if (buttonGroup == null) { + buttonGroup = new ButtonGroup(); + + // cheesy hack to allow a buttongroup to have no items selected. + // note that the button is not added to container or buttonList. + hiddenButton = new JRadioButton("Hidden Button"); + buttonGroup.add(hiddenButton); + } + buttonGroup.add(newButton); + + return newButton; + } + + /** + * Selects the button whose name matches the given text value. + * + * @param newText A String matching the name of one of the buttons. If null, + * empty, or not matching, all buttons are deselected. + */ + public void setValue(String aName) { + if (aName != null) { + Component c = null; + int count = buttonContainer.getComponentCount(); + for (int i = 0; i < count; i++) { + c = buttonContainer.getComponent(i); + if (c instanceof AbstractButton) { + if (c.getName().equals(aName)) { + ((AbstractButton) c).setSelected(true); + return; + } + } + } + } + + // null, empty, or not matching - deselect all + hiddenButton.setSelected(true); + } + + /** + * Gets the name of the currently selected button. + * + * @return A string matching the name of the currently selected button, or null + * of no button is selected. + */ + public String getValue() { + String result = null; + Component c = null; + int count = buttonContainer.getComponentCount(); + for (int i = 0; i < count; i++) { + c = buttonContainer.getComponent(i); + if ((c instanceof AbstractButton) && (((AbstractButton) c).isSelected())) { + return c.getName(); + } + } + return result; + } + + /** + * Tests whether the specified value is checked. + * + * @param aValue A value to be tested. + * @return True if the specified value is checked, otherwise false. + */ + public boolean getValue(String aValue) { + if (aValue == null) + return false; + return aValue.equals(getValue()); + } } - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartPasswordField.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartPasswordField.java index 6914cf6..55ba2f7 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartPasswordField.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartPasswordField.java @@ -26,249 +26,227 @@ import java.awt.event.KeyEvent; import javax.swing.JPasswordField; /** - * SmartPasswordField is an extention of JPasswordField. It does everything - * a JPassword does, as well as limit the number of characters. The user - * of this class can specify that a password can only have a maximum of - * 10 characters for instance. + * SmartPasswordField is an extention of JPasswordField. It does everything a + * JPassword does, as well as limit the number of characters. The user of this + * class can specify that a password can only have a maximum of 10 characters + * for instance. * * @author rob@straylight.princeton.com * @author $Author: cgruber $ * @version $Revision: 904 $ */ -public class SmartPasswordField extends JPasswordField -{ - -/******************************* -* CONSTANTS -*******************************/ - private static final int BACKSPACE = 8; - private static final int DELETE = 127; - private static final int SPACE = 32; - private static final int DASH = 45; - private static final int UNDERSCORE = 95; - private static final int PERIOD = 46; - private static final int PASTE = 22; // Ctl-V - - private int passwordLength = Integer.MAX_VALUE; - -/******************************* -* PUBLIC METHODS -*******************************/ - -/** -* Default constructor. -*/ - public SmartPasswordField() - { - super(); - } - -/** -* This constructor allows the user to set the maximum length of the password. -* @param aLength The maximum length of the password. -*/ - public SmartPasswordField( int aLength ) - { - this(); - setPasswordLength( aLength ); - } - -/** -* Sets the maximum lenght of the password. The value must be 0 or greater. -* If the length specified is less than 0, then no action occurs. -* @param aLength The maximum lenght of the password. -*/ - public void setPasswordLength( int aLength ) - { - if ( aLength >= 0 ) - { - passwordLength = aLength; - } - } - -/** -* Returns the current maximum length of the password. -* @return The current maximum length of the password. -*/ - public int getPasswordLength() - { - return passwordLength; - } - -/** -* This method processes a key event. This event is generated by input from the -* keyboard when this text field has the focus. This method is called for every -* key that is pressed and released on the keyboard. This includes modifier -* keys like the shift and alt keys. This class looks at the key and determines -* if the key is valid input given the restrictions of this class.

-* @param e A key event generated by a keyboard action. -*/ - public void processKeyEvent(KeyEvent e) - { - String currentText = ""; - String testString = ""; - char newChar = e.getKeyChar(); - int currentLength = 0; - int selectionStart = 0; - int selectionEnd = 0; - int endOfHead = 0; - int startOfTail = 0; - boolean backspace = false; - boolean delete = false; - boolean paste = false; - boolean insertionPoint = false; - boolean selectionAtStart = false; - boolean selectionAtEnd = false; - - backspace = (newChar == BACKSPACE); - delete = (newChar == DELETE); - paste = (newChar == PASTE); - - if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste))) // A "key-typed" event - { - if (isValidCharacter(newChar)) - { - - if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste)) - { - // Analyze the current contents of the field - currentText = new String( getPassword() ); - currentLength = currentText.length(); - - selectionStart = getSelectionStart(); - selectionEnd = getSelectionEnd(); - - insertionPoint = (selectionStart == selectionEnd); - selectionAtStart = (selectionStart == 0); - selectionAtEnd = (selectionEnd >= currentLength); - if (selectionEnd > currentLength) - { - setSelectionEnd(currentLength); - } - - // Generate new string - if (selectionStart > 0) // Create head of test string - { - endOfHead = selectionStart; - if (insertionPoint && backspace) - { - endOfHead -= 1; - } - testString += currentText.substring(0, endOfHead); - } - - if (!(backspace || delete || paste)) // Add the new character - { - testString += newChar; - } - - if (paste) // Add the string from the clipboard - { - Transferable data = getToolkit().getSystemClipboard().getContents(this); - if (data != null) - { - try - { - String clipString = (String)data.getTransferData(DataFlavor.stringFlavor); - testString += clipString; - } - catch (java.io.IOException ioe) - { - // Do nothing - } - catch (UnsupportedFlavorException ufe) - { - // Do nothing - } - } - } - - if (selectionEnd < currentLength) // Add the tail of the string - { - startOfTail = selectionEnd; - if (insertionPoint && delete) - { - startOfTail += 1; - } - testString += currentText.substring(startOfTail); - } - - } - - if (testString.compareTo("") != 0) // Null string is OK - { - if (!(isValidString(testString))) - { - e.consume(); - } - } - } - else - { - e.consume(); - } - } - super.processKeyEvent(e); - - postProcessing(); - } - - -/******************************* -* PROTECTED METHODS -*******************************/ - -/** -* Returns whether the inputted character is valid or not. In this case all -* characters are valid input. -* @param aChar A character to perform the validity test with. -* @return True if the character is valid for this subclassed text field.
-* False is the character is not valid. -*/ - protected boolean isValidCharacter(char aChar) - { - return true; - } - -/** -* Returns whether a string is valid for this text field. As the user types from -* the keyboard, this method is called to determine if the new string in the text -* field is valid based upon the restriction of this class. The length of the -* new string is checked against the maximum password length. -* @param aString The string to perform the validity check with. -* @return True if the length of the string is less than or equal to the maximum length. -* False if the character is not valud. -*/ - protected boolean isValidString(String aString) - { - if ( aString.length() > passwordLength ) - { - return false; - } - - return true; - } - -/** -* This class does not need any post processing. -*/ - protected void postProcessing() - { - /* Do Nothing */ - } - - -/******************************* -* PRIVATE METHODS -*******************************/ - - private boolean isPrintableCharacter(char inputChar) - { - if ((inputChar >= ' ') && (inputChar <= '~')) - { - return true; - } - return false; - } +public class SmartPasswordField extends JPasswordField { + + /******************************* + * CONSTANTS + *******************************/ + private static final int BACKSPACE = 8; + private static final int DELETE = 127; + private static final int SPACE = 32; + private static final int DASH = 45; + private static final int UNDERSCORE = 95; + private static final int PERIOD = 46; + private static final int PASTE = 22; // Ctl-V + + private int passwordLength = Integer.MAX_VALUE; + + /******************************* + * PUBLIC METHODS + *******************************/ + + /** + * Default constructor. + */ + public SmartPasswordField() { + super(); + } + + /** + * This constructor allows the user to set the maximum length of the password. + * + * @param aLength The maximum length of the password. + */ + public SmartPasswordField(int aLength) { + this(); + setPasswordLength(aLength); + } + + /** + * Sets the maximum lenght of the password. The value must be 0 or greater. If + * the length specified is less than 0, then no action occurs. + * + * @param aLength The maximum lenght of the password. + */ + public void setPasswordLength(int aLength) { + if (aLength >= 0) { + passwordLength = aLength; + } + } + + /** + * Returns the current maximum length of the password. + * + * @return The current maximum length of the password. + */ + public int getPasswordLength() { + return passwordLength; + } + + /** + * This method processes a key event. This event is generated by input from the + * keyboard when this text field has the focus. This method is called for every + * key that is pressed and released on the keyboard. This includes modifier keys + * like the shift and alt keys. This class looks at the key and determines if + * the key is valid input given the restrictions of this class.
+ *
+ * + * @param e A key event generated by a keyboard action. + */ + public void processKeyEvent(KeyEvent e) { + String currentText = ""; + String testString = ""; + char newChar = e.getKeyChar(); + int currentLength = 0; + int selectionStart = 0; + int selectionEnd = 0; + int endOfHead = 0; + int startOfTail = 0; + boolean backspace = false; + boolean delete = false; + boolean paste = false; + boolean insertionPoint = false; + boolean selectionAtStart = false; + boolean selectionAtEnd = false; + + backspace = (newChar == BACKSPACE); + delete = (newChar == DELETE); + paste = (newChar == PASTE); + + if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste))) // A "key-typed" event + { + if (isValidCharacter(newChar)) { + + if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste)) { + // Analyze the current contents of the field + currentText = new String(getPassword()); + currentLength = currentText.length(); + + selectionStart = getSelectionStart(); + selectionEnd = getSelectionEnd(); + + insertionPoint = (selectionStart == selectionEnd); + selectionAtStart = (selectionStart == 0); + selectionAtEnd = (selectionEnd >= currentLength); + if (selectionEnd > currentLength) { + setSelectionEnd(currentLength); + } + + // Generate new string + if (selectionStart > 0) // Create head of test string + { + endOfHead = selectionStart; + if (insertionPoint && backspace) { + endOfHead -= 1; + } + testString += currentText.substring(0, endOfHead); + } + + if (!(backspace || delete || paste)) // Add the new character + { + testString += newChar; + } + + if (paste) // Add the string from the clipboard + { + Transferable data = getToolkit().getSystemClipboard().getContents(this); + if (data != null) { + try { + String clipString = (String) data.getTransferData(DataFlavor.stringFlavor); + testString += clipString; + } catch (java.io.IOException ioe) { + // Do nothing + } catch (UnsupportedFlavorException ufe) { + // Do nothing + } + } + } + + if (selectionEnd < currentLength) // Add the tail of the string + { + startOfTail = selectionEnd; + if (insertionPoint && delete) { + startOfTail += 1; + } + testString += currentText.substring(startOfTail); + } + + } + + if (testString.compareTo("") != 0) // Null string is OK + { + if (!(isValidString(testString))) { + e.consume(); + } + } + } else { + e.consume(); + } + } + super.processKeyEvent(e); + + postProcessing(); + } + + /******************************* + * PROTECTED METHODS + *******************************/ + + /** + * Returns whether the inputted character is valid or not. In this case all + * characters are valid input. + * + * @param aChar A character to perform the validity test with. + * @return True if the character is valid for this subclassed text field.
+ * False is the character is not valid. + */ + protected boolean isValidCharacter(char aChar) { + return true; + } + + /** + * Returns whether a string is valid for this text field. As the user types from + * the keyboard, this method is called to determine if the new string in the + * text field is valid based upon the restriction of this class. The length of + * the new string is checked against the maximum password length. + * + * @param aString The string to perform the validity check with. + * @return True if the length of the string is less than or equal to the maximum + * length. False if the character is not valud. + */ + protected boolean isValidString(String aString) { + if (aString.length() > passwordLength) { + return false; + } + + return true; + } + + /** + * This class does not need any post processing. + */ + protected void postProcessing() { + /* Do Nothing */ + } + + /******************************* + * PRIVATE METHODS + *******************************/ + + private boolean isPrintableCharacter(char inputChar) { + if ((inputChar >= ' ') && (inputChar <= '~')) { + return true; + } + return false; + } } diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartTextField.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartTextField.java index cee37e1..5092179 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartTextField.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartTextField.java @@ -27,218 +27,204 @@ import javax.swing.JTextField; /** * SmartTextField is an abstract class for that allows the text field to - * intelligently analyze the user's input in real-time. As the user enters + * intelligently analyze the user's input in real-time. As the user enters * keystrokes, the generated string is analyzed to determine if the new string * is valid based on the criteria of the concrete classes that extend this - * class. An invalid keystroke is rejected and not displayed in the text - * field. This class can be extended to to create smart text fields that only - * accept integers or floating points number or alphabetic strings of maximum - * length. These are several examples. + * class. An invalid keystroke is rejected and not displayed in the text field. + * This class can be extended to to create smart text fields that only accept + * integers or floating points number or alphabetic strings of maximum length. + * These are several examples. * * @author rob@straylight.princeton.com * @author $Author: cgruber $ * @version $Revision: 904 $ */ -public abstract class SmartTextField extends JTextField -{ - -/******************************* -* CONSTANTS -*******************************/ - private static final int BACKSPACE = 8; - private static final int DELETE = 127; - private static final int SPACE = 32; - private static final int DASH = 45; - private static final int UNDERSCORE = 95; - private static final int PERIOD = 46; - private static final int PASTE = 22; // Ctl-V - - -/******************************* -* PUBLIC METHODS -*******************************/ - -/** -* This method processes a key event. This event is generated by input from the -* keyboard when this text field has the focus. This method is called for every -* key that is pressed and released on the keyboard. This includes modifier -* keys like the shift and alt keys. This class looks at the key and determines -* if the key is valid input given the restrictions of the concrete sub-classes.

-* Example - A smart text field only allows alphabetic characters. If the key -* pressed is a "2" then this method will determine that the key is invalid and -* "consume" the key event.

-* Note - Every printable character has a "TYPED" key event. Currentlt under -* Java 1.2.1 this does not happen. Bug 4186905 relating this bug has been -* fixed and is awaiting release. -* @param e A key event generated by a keyboard action. -*/ - public void processKeyEvent(KeyEvent e) - { - String currentText = ""; - String testString = ""; - char newChar = e.getKeyChar(); - int currentLength = 0; - int selectionStart = 0; - int selectionEnd = 0; - int endOfHead = 0; - int startOfTail = 0; - boolean backspace = false; - boolean delete = false; - boolean paste = false; - boolean insertionPoint = false; - boolean selectionAtStart = false; - boolean selectionAtEnd = false; - - backspace = (newChar == BACKSPACE); - delete = (newChar == DELETE); - paste = (newChar == PASTE); - - if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste))) // A "key-typed" event - { - if (isValidCharacter(newChar)) - { - - if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste)) - { - // Analyze the current contents of the field - currentText = getText(); - currentLength = currentText.length(); - - selectionStart = getSelectionStart(); - selectionEnd = getSelectionEnd(); - - insertionPoint = (selectionStart == selectionEnd); - selectionAtStart = (selectionStart == 0); - selectionAtEnd = (selectionEnd >= currentLength); - if (selectionEnd > currentLength) - { - setSelectionEnd(currentLength); - } - - // Generate new string - if (selectionStart > 0) // Create head of test string - { - endOfHead = selectionStart; - if (insertionPoint && backspace) - { - endOfHead -= 1; - } - testString += currentText.substring(0, endOfHead); - } - - if (!(backspace || delete || paste)) // Add the new character - { - testString += newChar; - } - - if (paste) // Add the string from the clipboard - { - Transferable data = getToolkit().getSystemClipboard().getContents(this); - if (data != null) - { - try - { - String clipString = (String)data.getTransferData(DataFlavor.stringFlavor); - testString += clipString; - } - catch (java.io.IOException ioe) - { - // Do nothing - } - catch (UnsupportedFlavorException ufe) - { - // Do nothing - } - } - } - - if (selectionEnd < currentLength) // Add the tail of the string - { - startOfTail = selectionEnd; - if (insertionPoint && delete) - { - startOfTail += 1; - } - testString += currentText.substring(startOfTail); - } - - } - - if (testString.compareTo("") != 0) // Null string is OK - { - if (!(isValidString(testString))) - { - e.consume(); - } - } - } - else - { - e.consume(); - } - } - super.processKeyEvent(e); - - postProcessing(); - } - - -/******************************* -* PROTECTED METHODS -*******************************/ - -/** -* Default constructor for this class. The initial text of the smart text field -* can be specified as well as the size (in characters) of the text field. -* @param text The initial string that is displayed in the text field. -* @param columns THe width of the text field in characters. -*/ - protected SmartTextField(String text, int columns) - { - super(text, columns); - } - -/** -* Returns whether a character is valid for this text field. As the user types -* from the keyboard, this method is called to determine if the character is a -* valid character based in the restrictions of the subclass. -* @param aChar A character to perform the validity test with. -* @return True if the character is valid for this subclassed text field.
-* False is the character is not valid. -*/ - abstract protected boolean isValidCharacter(char aChar); - -/** -* Returns whether a string is valid for this text field. As the user types from -* the keyboard, this method is called to determine if the new string in the text -* field is valid based upon the restriction of the subclass. This is done after -* the character has been determined to be valid since there can be restrictions -* placed on the text string as a whole, such a maximum length or date format. -* @param aString The string to perform the validity check with. -* @return True if the string is valid for this subclassed text field.
-* False if the character is not valud. -*/ - abstract protected boolean isValidString(String aString); - -/** -* This method is used by the any subclass that need to complete any processing -* of the text string in the text field after all the requirement checks have -* been performed. -*/ - abstract protected void postProcessing(); - - -/******************************* -* PRIVATE METHODS -*******************************/ - - private boolean isPrintableCharacter(char inputChar) - { - if ((inputChar >= ' ') && (inputChar <= '~')) - { - return true; - } - return false; - } +public abstract class SmartTextField extends JTextField { + + /******************************* + * CONSTANTS + *******************************/ + private static final int BACKSPACE = 8; + private static final int DELETE = 127; + private static final int SPACE = 32; + private static final int DASH = 45; + private static final int UNDERSCORE = 95; + private static final int PERIOD = 46; + private static final int PASTE = 22; // Ctl-V + + /******************************* + * PUBLIC METHODS + *******************************/ + + /** + * This method processes a key event. This event is generated by input from the + * keyboard when this text field has the focus. This method is called for every + * key that is pressed and released on the keyboard. This includes modifier keys + * like the shift and alt keys. This class looks at the key and determines if + * the key is valid input given the restrictions of the concrete sub-classes. + *
+ *
+ * Example - A smart text field only allows alphabetic characters. If the key + * pressed is a "2" then this method will determine that the key is invalid and + * "consume" the key event.
+ *
+ * Note - Every printable character has a "TYPED" key event. Currentlt under + * Java 1.2.1 this does not happen. Bug 4186905 relating this bug has been fixed + * and is awaiting release. + * + * @param e A key event generated by a keyboard action. + */ + public void processKeyEvent(KeyEvent e) { + String currentText = ""; + String testString = ""; + char newChar = e.getKeyChar(); + int currentLength = 0; + int selectionStart = 0; + int selectionEnd = 0; + int endOfHead = 0; + int startOfTail = 0; + boolean backspace = false; + boolean delete = false; + boolean paste = false; + boolean insertionPoint = false; + boolean selectionAtStart = false; + boolean selectionAtEnd = false; + + backspace = (newChar == BACKSPACE); + delete = (newChar == DELETE); + paste = (newChar == PASTE); + + if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste))) // A "key-typed" event + { + if (isValidCharacter(newChar)) { + + if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste)) { + // Analyze the current contents of the field + currentText = getText(); + currentLength = currentText.length(); + + selectionStart = getSelectionStart(); + selectionEnd = getSelectionEnd(); + + insertionPoint = (selectionStart == selectionEnd); + selectionAtStart = (selectionStart == 0); + selectionAtEnd = (selectionEnd >= currentLength); + if (selectionEnd > currentLength) { + setSelectionEnd(currentLength); + } + + // Generate new string + if (selectionStart > 0) // Create head of test string + { + endOfHead = selectionStart; + if (insertionPoint && backspace) { + endOfHead -= 1; + } + testString += currentText.substring(0, endOfHead); + } + + if (!(backspace || delete || paste)) // Add the new character + { + testString += newChar; + } + + if (paste) // Add the string from the clipboard + { + Transferable data = getToolkit().getSystemClipboard().getContents(this); + if (data != null) { + try { + String clipString = (String) data.getTransferData(DataFlavor.stringFlavor); + testString += clipString; + } catch (java.io.IOException ioe) { + // Do nothing + } catch (UnsupportedFlavorException ufe) { + // Do nothing + } + } + } + + if (selectionEnd < currentLength) // Add the tail of the string + { + startOfTail = selectionEnd; + if (insertionPoint && delete) { + startOfTail += 1; + } + testString += currentText.substring(startOfTail); + } + + } + + if (testString.compareTo("") != 0) // Null string is OK + { + if (!(isValidString(testString))) { + e.consume(); + } + } + } else { + e.consume(); + } + } + super.processKeyEvent(e); + + postProcessing(); + } + + /******************************* + * PROTECTED METHODS + *******************************/ + + /** + * Default constructor for this class. The initial text of the smart text field + * can be specified as well as the size (in characters) of the text field. + * + * @param text The initial string that is displayed in the text field. + * @param columns THe width of the text field in characters. + */ + protected SmartTextField(String text, int columns) { + super(text, columns); + } + + /** + * Returns whether a character is valid for this text field. As the user types + * from the keyboard, this method is called to determine if the character is a + * valid character based in the restrictions of the subclass. + * + * @param aChar A character to perform the validity test with. + * @return True if the character is valid for this subclassed text field.
+ * False is the character is not valid. + */ + abstract protected boolean isValidCharacter(char aChar); + + /** + * Returns whether a string is valid for this text field. As the user types from + * the keyboard, this method is called to determine if the new string in the + * text field is valid based upon the restriction of the subclass. This is done + * after the character has been determined to be valid since there can be + * restrictions placed on the text string as a whole, such a maximum length or + * date format. + * + * @param aString The string to perform the validity check with. + * @return True if the string is valid for this subclassed text field.
+ * False if the character is not valud. + */ + abstract protected boolean isValidString(String aString); + + /** + * This method is used by the any subclass that need to complete any processing + * of the text string in the text field after all the requirement checks have + * been performed. + */ + abstract protected void postProcessing(); + + /******************************* + * PRIVATE METHODS + *******************************/ + + private boolean isPrintableCharacter(char inputChar) { + if ((inputChar >= ' ') && (inputChar <= '~')) { + return true; + } + return false; + } } diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/StatusButtonPanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/StatusButtonPanel.java index 3d9a85b..ba9f361 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/StatusButtonPanel.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/StatusButtonPanel.java @@ -34,243 +34,228 @@ import javax.swing.UIManager; import javax.swing.border.EmptyBorder; /** -* StatusButtonPanel extends ButtonPanel to provide a space -* to display status messages in a consistent manner.

-* Messages are erased after a certain predefined interval, -* defaulting to 10 seconds. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ -public class StatusButtonPanel extends ButtonPanel -{ -/** -* This is the action command to all listeners when the status text is changed. -*/ - public static final String STATUS_CHANGED = "STATUS_CHANGED"; - - // note: weirdness happens if you initialize - // this variable. Because it is set by initLayout - // and initLayout is called by the superclass constructor, - // this variable would get initialized after initLayout - // is called... - protected Component statusComponent; // = null; - - protected Timer timer = null; - protected int interval = 10000; // adjust as needed - -/** -* Constructs a StatusButtonPanel. Three buttons are created -* so the panel is filled when used in a GUI-builder environment. -*/ - public StatusButtonPanel() - { - super(); - setupTimer(); - } - -/** -* Constructs a StatusButtonPanel using specified buttons. -* @param buttonList An array containing the strings to be used in labeling the buttons. -*/ - public StatusButtonPanel( String[] buttonList ) - { - super( buttonList ); - setupTimer(); - } - -/** -* Initializes the timer instance variable. -*/ - protected void setupTimer() - { - timer = new Timer( interval, this ); - timer.addActionListener( this ); - timer.setRepeats( false ); - timer.start(); - } - -/** -* Returns the number of milliseconds before the status message is cleared. -* The default is 10000. -* @return The current delay interval in milliseconds. -*/ - public int getDelayInterval() - { - return interval; - } - -/** -* Sets the number of milliseconds before the status message is cleared. -* @param millis The new delay interval in milliseconds. -*/ - public void setDelayInterval( int millis ) - { - interval = millis; - timer.setDelay( interval ); - } - -/** -* Returns the visual component used to display the status. -* @return A component used for displaying status. -*/ - public Component getStatusComponent() - { - return statusComponent; - - } -/** -* Receives ActionEvents from the internal timer. -* @param e The action event in question. -*/ - public void actionPerformed(ActionEvent e) - { - if ( e.getSource() == timer ) - { - setText( "" ); - return; - } - - // otherwise continue with superclass implementation - super.actionPerformed( e ); - } - -/** -* This method is responsible for the initial layout of the panel. -* Subclasses can implement different layouts, but this method -* is responsible for initializing buttonPanelLayout to a valid -* layout manager and setting this panel to use it. This method -* must should initialize statusComponent to a component that ideally -* has get/setText methods, although this is not required. -*/ - protected void initLayout() - { - - statusComponent = new JTextField(); - JTextField textField = (JTextField) statusComponent; - textField.setColumns( 20 ); - textField.setBackground( getBackground() ); - textField.setEditable( false ); + * StatusButtonPanel extends ButtonPanel to provide a space to display status + * messages in a consistent manner.
+ *
+ * Messages are erased after a certain predefined interval, defaulting to 10 + * seconds. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ +public class StatusButtonPanel extends ButtonPanel { + /** + * This is the action command to all listeners when the status text is changed. + */ + public static final String STATUS_CHANGED = "STATUS_CHANGED"; + + // note: weirdness happens if you initialize + // this variable. Because it is set by initLayout + // and initLayout is called by the superclass constructor, + // this variable would get initialized after initLayout + // is called... + protected Component statusComponent; // = null; + + protected Timer timer = null; + protected int interval = 10000; // adjust as needed + + /** + * Constructs a StatusButtonPanel. Three buttons are created so the panel is + * filled when used in a GUI-builder environment. + */ + public StatusButtonPanel() { + super(); + setupTimer(); + } + + /** + * Constructs a StatusButtonPanel using specified buttons. + * + * @param buttonList An array containing the strings to be used in labeling the + * buttons. + */ + public StatusButtonPanel(String[] buttonList) { + super(buttonList); + setupTimer(); + } + + /** + * Initializes the timer instance variable. + */ + protected void setupTimer() { + timer = new Timer(interval, this); + timer.addActionListener(this); + timer.setRepeats(false); + timer.start(); + } + + /** + * Returns the number of milliseconds before the status message is cleared. The + * default is 10000. + * + * @return The current delay interval in milliseconds. + */ + public int getDelayInterval() { + return interval; + } + + /** + * Sets the number of milliseconds before the status message is cleared. + * + * @param millis The new delay interval in milliseconds. + */ + public void setDelayInterval(int millis) { + interval = millis; + timer.setDelay(interval); + } + + /** + * Returns the visual component used to display the status. + * + * @return A component used for displaying status. + */ + public Component getStatusComponent() { + return statusComponent; + + } + + /** + * Receives ActionEvents from the internal timer. + * + * @param e The action event in question. + */ + public void actionPerformed(ActionEvent e) { + if (e.getSource() == timer) { + setText(""); + return; + } + + // otherwise continue with superclass implementation + super.actionPerformed(e); + } + + /** + * This method is responsible for the initial layout of the panel. Subclasses + * can implement different layouts, but this method is responsible for + * initializing buttonPanelLayout to a valid layout manager and setting this + * panel to use it. This method must should initialize statusComponent to a + * component that ideally has get/setText methods, although this is not + * required. + */ + protected void initLayout() { + + statusComponent = new JTextField(); + JTextField textField = (JTextField) statusComponent; + textField.setColumns(20); + textField.setBackground(getBackground()); + textField.setEditable(false); // statusComponent = new PickListPanel(); // for testing - this.setLayout( new GridBagLayout() ); - - GridBagConstraints gbc = - new GridBagConstraints(); - gbc.gridx = GridBagConstraints.RELATIVE; - gbc.gridy = GridBagConstraints.RELATIVE; - gbc.gridwidth = 1; - gbc.gridheight = 1; - gbc.weightx = 1.0; - gbc.weighty = 0.0; - gbc.anchor = GridBagConstraints.CENTER; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(0, 5, 0, 10); - gbc.ipadx = 0; - gbc.ipady = 0; + this.setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = GridBagConstraints.RELATIVE; + gbc.gridy = GridBagConstraints.RELATIVE; + gbc.gridwidth = 1; + gbc.gridheight = 1; + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.anchor = GridBagConstraints.CENTER; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(0, 5, 0, 10); + gbc.ipadx = 0; + gbc.ipady = 0; //1.2 new GridBagConstraints(GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, //1.2 GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 10), 0, 0 ); - this.add( statusComponent, gbc ); - - buttonContainer = new JPanel(); - buttonPanelLayout = new BetterFlowLayout(); - buttonContainer.setLayout(buttonPanelLayout); - buttonPanelLayout.setAlignment( BetterFlowLayout.RIGHT ); - ((BetterFlowLayout)buttonPanelLayout).setWidthUniform( true ); - gbc.weightx = 0.0; - gbc.insets = new Insets( 0, 0, 0, 0 ); - this.add( buttonContainer, gbc ); - } - -/** -* Sets the text to appear in the status area. -* @param newText A string to appear in the status area. Nulls are allowed. -*/ - public void setText(String newText) - { - // TODO: should use property introspection instead - - // use reflection to call the "setText" method, if any. - try - { - Class c = statusComponent.getClass(); - Method m = c.getMethod( "setText", new Class[] { new String().getClass() } ); - m.invoke( statusComponent, new Object[] { newText } ); - broadcastEvent( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, STATUS_CHANGED ) ); - statusComponent.paint( statusComponent.getGraphics() ); - } - catch ( Exception exc ) - { - // "setText" method does not exist; do nothing. - } - - // if non-empty string, start the timer - if ( ! "".equals( newText ) ) - { - timer.restart(); - } - } - -/** -* Gets the text in the status area. -* @return The string being displayed in the status area. -*/ - public String getText() - { - // TODO: should use property introspection instead - - String value = ""; - // use reflection to call the "setText" method, if any. - try - { - Class c = statusComponent.getClass(); - Method m = c.getMethod( "getText", (Class[])null ); - value = (String) m.invoke( statusComponent, (Object[])null ); - } - catch ( Exception exc ) - { - // "getText" method does not exist; do nothing. - } - return value; - } - - // for testing - - public static void main( String[] argv ) - { - try - { - UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() ); - } - catch (Exception exc) - { - - } - - JFrame dialog = new JFrame(); - BorderLayout bl = new BorderLayout( 20, 20 ); + this.add(statusComponent, gbc); + + buttonContainer = new JPanel(); + buttonPanelLayout = new BetterFlowLayout(); + buttonContainer.setLayout(buttonPanelLayout); + buttonPanelLayout.setAlignment(BetterFlowLayout.RIGHT); + ((BetterFlowLayout) buttonPanelLayout).setWidthUniform(true); + gbc.weightx = 0.0; + gbc.insets = new Insets(0, 0, 0, 0); + this.add(buttonContainer, gbc); + } + + /** + * Sets the text to appear in the status area. + * + * @param newText A string to appear in the status area. Nulls are allowed. + */ + public void setText(String newText) { + // TODO: should use property introspection instead + + // use reflection to call the "setText" method, if any. + try { + Class c = statusComponent.getClass(); + Method m = c.getMethod("setText", new Class[] { new String().getClass() }); + m.invoke(statusComponent, new Object[] { newText }); + broadcastEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, STATUS_CHANGED)); + statusComponent.paint(statusComponent.getGraphics()); + } catch (Exception exc) { + // "setText" method does not exist; do nothing. + } + + // if non-empty string, start the timer + if (!"".equals(newText)) { + timer.restart(); + } + } + + /** + * Gets the text in the status area. + * + * @return The string being displayed in the status area. + */ + public String getText() { + // TODO: should use property introspection instead + + String value = ""; + // use reflection to call the "setText" method, if any. + try { + Class c = statusComponent.getClass(); + Method m = c.getMethod("getText", (Class[]) null); + value = (String) m.invoke(statusComponent, (Object[]) null); + } catch (Exception exc) { + // "getText" method does not exist; do nothing. + } + return value; + } + + // for testing + + public static void main(String[] argv) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception exc) { + + } + + JFrame dialog = new JFrame(); + BorderLayout bl = new BorderLayout(20, 20); // StatusButtonPanel panel = new StatusButtonPanel(); // System.out.println( panel.statusComponent ); - StatusButtonPanel panel = new StatusButtonPanel( new String[] { "Okay", "Cancel" } ); + StatusButtonPanel panel = new StatusButtonPanel(new String[] { "Okay", "Cancel" }); - dialog.getContentPane().setLayout( bl ); - dialog.getContentPane().add( panel, BorderLayout.SOUTH ); - dialog.setLocation( 50, 50 ); - // dialog.setSize( 450, 150 ); - dialog.pack(); - dialog.setVisible( true ); + dialog.getContentPane().setLayout(bl); + dialog.getContentPane().add(panel, BorderLayout.SOUTH); + dialog.setLocation(50, 50); + // dialog.setSize( 450, 150 ); + dialog.pack(); + dialog.setVisible(true); - panel.setBorder( new EmptyBorder( 5, 5, 5, 5 ) ); - panel.setAlignment( BetterFlowLayout.RIGHT ); + panel.setBorder(new EmptyBorder(5, 5, 5, 5)); + panel.setAlignment(BetterFlowLayout.RIGHT); // panel.getButton( "One" ).setEnabled( false ); - panel.setText( "File saved." ); - System.out.println( panel.getText() ); - } + panel.setText("File saved."); + System.out.println(panel.getText()); + } } - diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TintedImageFilter.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TintedImageFilter.java index a51ed16..b15a660 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TintedImageFilter.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TintedImageFilter.java @@ -22,79 +22,75 @@ import java.awt.Color; import java.awt.image.RGBImageFilter; /** - * TintedImageFilter tints all gray pixels half-way towards - * the value passed into the constructor. This "tints" a - * mostly grayscale image. This has proven useful for tinting - * user interface decorative images towards one of the SystemColor - * constants to better mesh with a platform look and feel. + * TintedImageFilter tints all gray pixels half-way towards the value passed + * into the constructor. This "tints" a mostly grayscale image. This has proven + * useful for tinting user interface decorative images towards one of the + * SystemColor constants to better mesh with a platform look and feel. * * @author michael@mpowers.net * @author $Author: cgruber $ * @version $Revision: 893 $ */ - public class TintedImageFilter extends RGBImageFilter - { - double redOffset, greenOffset, blueOffset; - - public TintedImageFilter( Color aColor ) - { - canFilterIndexColorModel = true; - redOffset = getOffset( aColor.getRed() ); - greenOffset = getOffset( aColor.getGreen() ); - blueOffset = getOffset( aColor.getBlue() ); - } - - /** - * Calculates the offset used to modify color - * values. This method returns half the difference - * between the specified color level and 192. - */ - protected double getOffset( int colorValue ) - { - return ( colorValue - 192 ) / 2; - } +public class TintedImageFilter extends RGBImageFilter { + double redOffset, greenOffset, blueOffset; - public int filterRGB(int x, int y, int rgb) - { + public TintedImageFilter(Color aColor) { + canFilterIndexColorModel = true; + redOffset = getOffset(aColor.getRed()); + greenOffset = getOffset(aColor.getGreen()); + blueOffset = getOffset(aColor.getBlue()); + } + + /** + * Calculates the offset used to modify color values. This method returns half + * the difference between the specified color level and 192. + */ + protected double getOffset(int colorValue) { + return (colorValue - 192) / 2; + } + + public int filterRGB(int x, int y, int rgb) { + + int red = (rgb & 0xff0000) >> 16; + int green = (rgb & 0x00ff00) >> 8; + int blue = (rgb & 0x0000ff); + + // if roughly black + if (red + green + blue < 30) + return rgb; + + // if roughly gray + if ((Math.abs(red - green) < 10) && (Math.abs(red - blue) < 10)) { + red += redOffset; + if (red < 0) + red = 0; + if (red > 255) + red = 255; + green += greenOffset; + if (green < 0) + green = 0; + if (green > 255) + green = 255; + blue += blueOffset; + if (blue < 0) + blue = 0; + if (blue > 255) + blue = 255; + + return new Color(red, green, blue).getRGB(); + } + + return rgb; + } +} - int red = ( rgb & 0xff0000 ) >> 16; - int green = ( rgb & 0x00ff00 ) >> 8; - int blue = ( rgb & 0x0000ff ); - - // if roughly black - if ( red + green + blue < 30 ) return rgb; - - // if roughly gray - if ( ( Math.abs( red - green ) < 10 ) - && ( Math.abs( red - blue ) < 10 ) ) - { - red += redOffset; - if ( red < 0 ) red = 0; - if ( red > 255 ) red = 255; - green += greenOffset; - if ( green < 0 ) green = 0; - if ( green > 255 ) green = 255; - blue += blueOffset; - if ( blue < 0 ) blue = 0; - if ( blue > 255 ) blue = 255; - - return new Color( red, green, blue ).getRGB(); - } - - return rgb; - } - } - /* - * $Log$ - * Revision 1.1 2006/02/16 13:22:22 cgruber - * Check in all sources in eclipse-friendly maven-enabled packages. + * $Log$ Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in + * eclipse-friendly maven-enabled packages. * - * Revision 1.2 2001/01/18 21:27:04 mpowers - * Made the tinting a little darker. + * Revision 1.2 2001/01/18 21:27:04 mpowers Made the tinting a little darker. * - * Revision 1.1 2001/01/12 17:36:27 mpowers - * Contributing TintedImageFilter. + * Revision 1.1 2001/01/12 17:36:27 mpowers Contributing TintedImageFilter. * * */ diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeChooser.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeChooser.java index f0bb6c2..f5ab50c 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeChooser.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeChooser.java @@ -66,662 +66,538 @@ import javax.swing.tree.TreeSelectionModel; import net.wotonomy.foundation.internal.WotonomyException; /** -* TreeChooser is a FileChooser-like panel that -* uses a TreeModel as a data source. It basically -* provides an alternative to JTree for rendering -* and manipulating tree-like data. -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ -public class TreeChooser extends JPanel - implements ActionListener, ListSelectionListener, - TreeSelectionListener, TreeModelListener, ListCellRenderer -{ - /** - * The TreeChooser responds to this action command - * by calling displayPrevious(). - */ + * TreeChooser is a FileChooser-like panel that uses a TreeModel as a data + * source. It basically provides an alternative to JTree for rendering and + * manipulating tree-like data. + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ +public class TreeChooser extends JPanel + implements ActionListener, ListSelectionListener, TreeSelectionListener, TreeModelListener, ListCellRenderer { + /** + * The TreeChooser responds to this action command by calling displayPrevious(). + */ public static final String BACK = "Back"; - /** - * The TreeChooser responds to this action command - * by calling displayHome(). - */ + /** + * The TreeChooser responds to this action command by calling displayHome(). + */ public static final String HOME = "Home"; - /** - * The TreeChooser responds to this action command - * by calling displayParent(). - */ + /** + * The TreeChooser responds to this action command by calling displayParent(). + */ public static final String UP = "Up"; - /** - * The TreeChooser responds to this action command - * by attempting to navigate to the first node in - * the current selection and display that node's children. - */ + /** + * The TreeChooser responds to this action command by attempting to navigate to + * the first node in the current selection and display that node's children. + */ public static final String SELECT = "Select"; protected JList contents; - protected JComboBox pathCombo; + protected JComboBox pathCombo; protected JToolBar toolBar; - + protected TreeModel model; protected TreeSelectionModel selectionModel; - protected TreeCellRenderer renderer; - protected TreePath displayPath; - protected Stack pathStack; - protected int pathIndent; - - private ChooserComboBoxModel comboBoxModel; - private JTree bogusJTree; // needed for tree cell renderer - private Dimension preferredSize; - - public TreeChooser() - { - preferredSize = new Dimension( 300, 200 ); - model = new DefaultTreeModel( new DefaultMutableTreeNode( "Root" ) ); - displayPath = new TreePath( model.getRoot() ); - selectionModel = new DefaultTreeSelectionModel(); - renderer = new DefaultTreeCellRenderer(); - pathStack = new Stack(); - pathIndent = 0; // 16; - comboBoxModel = new ChooserComboBoxModel( this ); - - bogusJTree = new JTree(); - bogusJTree.setModel( model ); + protected TreeCellRenderer renderer; + protected TreePath displayPath; + protected Stack pathStack; + protected int pathIndent; + + private ChooserComboBoxModel comboBoxModel; + private JTree bogusJTree; // needed for tree cell renderer + private Dimension preferredSize; + + public TreeChooser() { + preferredSize = new Dimension(300, 200); + model = new DefaultTreeModel(new DefaultMutableTreeNode("Root")); + displayPath = new TreePath(model.getRoot()); + selectionModel = new DefaultTreeSelectionModel(); + renderer = new DefaultTreeCellRenderer(); + pathStack = new Stack(); + pathIndent = 0; // 16; + comboBoxModel = new ChooserComboBoxModel(this); + + bogusJTree = new JTree(); + bogusJTree.setModel(model); init(); - displayHome(); - - stopListening(); // clear existing listeners - startListening(); - } - - public Dimension getPreferredSize() - { - return preferredSize; - } - - protected void init() - { - this.setLayout( new BorderLayout( 10, 10 ) ); - + displayHome(); + + stopListening(); // clear existing listeners + startListening(); + } + + public Dimension getPreferredSize() { + return preferredSize; + } + + protected void init() { + this.setLayout(new BorderLayout(10, 10)); + contents = initList(); - contents.getSelectionModel().setSelectionMode( - ListSelectionModel.MULTIPLE_INTERVAL_SELECTION ); - // synchs with DefaultTreeSelectionModel - - JScrollPane scrollPane = new JScrollPane( contents ); - scrollPane.setPreferredSize( new Dimension( 200, 150 ) ); - this.add( scrollPane, BorderLayout.CENTER ); - - Component previewPane = initPreviewPane(); - if ( previewPane != null ) - { - this.add( previewPane, BorderLayout.EAST ); - } - + contents.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + // synchs with DefaultTreeSelectionModel + + JScrollPane scrollPane = new JScrollPane(contents); + scrollPane.setPreferredSize(new Dimension(200, 150)); + this.add(scrollPane, BorderLayout.CENTER); + + Component previewPane = initPreviewPane(); + if (previewPane != null) { + this.add(previewPane, BorderLayout.EAST); + } + JPanel navigationPanel = new JPanel(); - navigationPanel.setLayout( new BorderLayout( 10, 10 ) ); - this.add( navigationPanel, BorderLayout.NORTH ); - + navigationPanel.setLayout(new BorderLayout(10, 10)); + this.add(navigationPanel, BorderLayout.NORTH); + pathCombo = initComboBox(); - if ( pathCombo != null ) - { - pathCombo.setModel( comboBoxModel ); + if (pathCombo != null) { + pathCombo.setModel(comboBoxModel); // put combo in a grid bag to handle varying - // heights of JToolBars across platforms + // heights of JToolBars across platforms JPanel panel = new JPanel(); - panel.setLayout( new GridBagLayout() ); + panel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1.0; - panel.add( pathCombo, gbc ); - navigationPanel.add( panel, BorderLayout.CENTER ); - } - + panel.add(pathCombo, gbc); + navigationPanel.add(panel, BorderLayout.CENTER); + } + Component toolBar = initToolBar(); - if ( toolBar != null ) - { - navigationPanel.add( toolBar, BorderLayout.EAST ); - } - - } - - /** - * Creates tool bar or return null if no tool bar is desired. - * This implementation returns a JToolBar containing buttons - * for BACK, UP, and HOME. - */ - protected Component initToolBar() - { + if (toolBar != null) { + navigationPanel.add(toolBar, BorderLayout.EAST); + } + + } + + /** + * Creates tool bar or return null if no tool bar is desired. This + * implementation returns a JToolBar containing buttons for BACK, UP, and HOME. + */ + protected Component initToolBar() { JToolBar toolBar = new JToolBar(); - toolBar.setFloatable( false ); - JButton button; - button = new JButton( UIManager.getIcon("FileChooser.upFolderIcon") ); - button.setActionCommand( UP ); - button.addActionListener( this ); - toolBar.add( button ); - button = new JButton( UIManager.getIcon("FileChooser.homeFolderIcon") ); - button.setActionCommand( HOME ); - button.addActionListener( this ); - toolBar.add( button ); -/* - button = new JButton( UIManager.getIcon("FileChooser.newFolderIcon") ); - button.setActionCommand( BACK ); - button.addActionListener( this ); - toolBar.add( button ); -*/ - return toolBar; - } - - /** - * Creates the component that is used to display a preview of the - * selected item(s) in the content area. This component would listen - * to the selection model to update itself when the selected items change. - * Return null to omit this component. - * This implementation returns null. - */ - protected Component initPreviewPane() - { - return null; - } - - /** - * Creates the JComboBox that is used to render the path leading to - * the displayed contents. Return null to omit this combo box. - * This implementation returns a stock JComboBox that uses this - * class as its cell renderer. - */ - protected JComboBox initComboBox() - { - JComboBox comboBox = new JComboBox(); - comboBox.setRenderer( this ); - return comboBox; - } - - /** - * Creates the JList that is used to render the path leading to - * the displayed contents. This method may not return null. - * This implementation returns a stock JList that uses this - * class as its cell renderer and fires a SELECT action event - * on double click. - */ - protected JList initList() - { - JList list = new JList(); - list.setCellRenderer( this ); - list.addMouseListener( new MouseAdapter() - { - public void mouseClicked( MouseEvent evt ) - { - if ( evt.getClickCount() > 1 ) - { - actionPerformed( new ActionEvent( this, 0, SELECT ) ); - } - } - }); - return list; - } - - /** - * Begins listening to the specified tree model - * and tree selection model. - */ - protected void startListening() - { - model.addTreeModelListener( this ); - selectionModel.addTreeSelectionListener( this ); - contents.addListSelectionListener( this ); - } - - /** - * Stops listening to the specified tree model - * and tree selection model. - */ - protected void stopListening() - { - model.removeTreeModelListener( this ); - selectionModel.removeTreeSelectionListener( this ); - contents.removeListSelectionListener( this ); - } - - /** - * Returns the TreeModel used by the TreeChooser. - */ - public TreeModel getModel() - { - return model; - } - - /** - * Sets the TreeModel used by the TreeChooser. - */ - public void setModel( TreeModel aTreeModel ) - { - stopListening(); - model = aTreeModel; - bogusJTree.setModel( aTreeModel ); - pathStack.removeAllElements(); - startListening(); - displayHome(); - } - - /** - * Returns the TreeSelectionModel used by the TreeChooser. - */ - public TreeSelectionModel getSelectionModel() - { - return selectionModel; - } - - /** - * Sets the TreeSelectionModel used by the TreeChooser. - */ - public void setSelectionModel( TreeSelectionModel aSelectionModel ) - { - selectionModel = aSelectionModel; - if ( aSelectionModel.getSelectionMode() == - TreeSelectionModel.SINGLE_TREE_SELECTION ) - { - contents.getSelectionModel().setSelectionMode( - ListSelectionModel.SINGLE_SELECTION ); - } - else - { - contents.getSelectionModel().setSelectionMode( - ListSelectionModel.MULTIPLE_INTERVAL_SELECTION ); - } - updateSelection(); - } - - /** - * Returns the TreeCellRenderer used by the TreeChooser. - */ - public TreeCellRenderer getRenderer() - { - return renderer; - } - - /** - * Sets the TreeCellRenderer used by the TreeChooser. - */ - public void setRenderer( TreeCellRenderer aRenderer ) - { - renderer = aRenderer; - updateContents(); - } - - /** - * Displays the "home" directory. - * This implementation displays the root node's children. - */ - public void displayHome() - { - setDisplayPath( null ); - } - - /** - * Displays the parent path of the currently displayed path. - */ - public void displayParent() - { - setDisplayPath( displayPath.getParentPath() ); - } - - /** - * Displays the last displayed path before the current one, - * emulating the behavior of a "back" button. - */ - public void displayPrevious() - { - if ( pathStack.empty() ) - { - displayHome(); - } - else - { - setDisplayPathDirect( (TreePath) pathStack.pop() ); - updateContents(); - } - } - - /** - * Pushes the previous item onto the stack, sets - * the display path, and then updates the contents. - * If aPath is null, the root node's children are displayed. - */ - public void setDisplayPath( TreePath aPath ) - { - if ( aPath == null ) - { - aPath = new TreePath( getModel().getRoot() ); - } - if ( ! displayPath.equals ( aPath ) ) - { - pathStack.push( displayPath ); - setDisplayPathDirect( aPath ); - } - updateContents(); - } - - /** - * Sets the displayPath field and does not - * update the stack nor update the contents. - */ - protected void setDisplayPathDirect( TreePath aPath ) - { - displayPath = aPath; - } - - /** - * Gets the currently displayed path. - */ - public TreePath getDisplayPath() - { - return displayPath; - } - - /** - * Called when selected path changes or when model indicates - * that the displayed path has changed. - */ - protected void updateContents() - { - stopListening(); - - // update combo box - comboBoxModel.fireContentsChanged(); - - // update list contents - Object displayedObject = displayPath.getLastPathComponent(); -/* -//FIXME: this display group doesn't seem to be getting the sort orderings from parent -if ( displayedObject instanceof net.wotonomy.ui.EODisplayGroup ) -System.out.println( ((net.wotonomy.ui.EODisplayGroup)displayedObject).displayedObjects() ); -*/ - int count = model.getChildCount( displayedObject ); - Object[] children = new Object[ count ]; - for ( int i = 0; i < count; i++ ) - { - children[i] = model.getChild( displayedObject, i ); - } - contents.setListData( children ); - - startListening(); - - // synchronize the selection - updateSelection(); - } - - /** - * Updates the selection in the list to reflect the - * selection in the tree selection model. - */ - public void updateSelection() - { - int index; - Object last = displayPath.getLastPathComponent(); - TreePath[] selectionPaths = selectionModel.getSelectionPaths(); - if ( selectionPaths != null ) - { - List selectedIndices = new LinkedList(); - for ( int i = 0; i < selectionPaths.length; i++ ) - { - if ( displayPath.equals( selectionPaths[i].getParentPath() ) ) - { - index = getModel().getIndexOfChild( - last, selectionPaths[i].getLastPathComponent() ); - if ( index != -1 ) - { - selectedIndices.add( new Integer( index ) ); - } - else // should never happen - { - throw new WotonomyException( - "Could not find child of displayed node." ); - } - } - } - int[] selected = new int[ selectedIndices.size() ]; - for ( int i = 0; i < selected.length; i++ ) - { - selected[i] = ((Integer)selectedIndices.get(i)).intValue(); - } - stopListening(); - contents.setSelectedIndices( selected ); - startListening(); - } - } - - // interface TreeModelListener - - public void treeNodesChanged( TreeModelEvent evt ) - { -/* - if ( displayPath.getLastPathComponent().toString().equals( - evt.getTreePath().getLastPathComponent().toString() ) ) - { -System.out.println( "TreeChooser.treeNodesChanged: " + count++ ); -*/ - updateContents(); -/* - } - else - { - System.out.println( evt.getTreePath() + " != " + displayPath ); - } -*/ - } - - public void treeNodesInserted( TreeModelEvent evt ) - { + toolBar.setFloatable(false); + JButton button; + button = new JButton(UIManager.getIcon("FileChooser.upFolderIcon")); + button.setActionCommand(UP); + button.addActionListener(this); + toolBar.add(button); + button = new JButton(UIManager.getIcon("FileChooser.homeFolderIcon")); + button.setActionCommand(HOME); + button.addActionListener(this); + toolBar.add(button); + /* + * button = new JButton( UIManager.getIcon("FileChooser.newFolderIcon") ); + * button.setActionCommand( BACK ); button.addActionListener( this ); + * toolBar.add( button ); + */ + return toolBar; + } + + /** + * Creates the component that is used to display a preview of the selected + * item(s) in the content area. This component would listen to the selection + * model to update itself when the selected items change. Return null to omit + * this component. This implementation returns null. + */ + protected Component initPreviewPane() { + return null; + } + + /** + * Creates the JComboBox that is used to render the path leading to the + * displayed contents. Return null to omit this combo box. This implementation + * returns a stock JComboBox that uses this class as its cell renderer. + */ + protected JComboBox initComboBox() { + JComboBox comboBox = new JComboBox(); + comboBox.setRenderer(this); + return comboBox; + } + + /** + * Creates the JList that is used to render the path leading to the displayed + * contents. This method may not return null. This implementation returns a + * stock JList that uses this class as its cell renderer and fires a SELECT + * action event on double click. + */ + protected JList initList() { + JList list = new JList(); + list.setCellRenderer(this); + list.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + if (evt.getClickCount() > 1) { + actionPerformed(new ActionEvent(this, 0, SELECT)); + } + } + }); + return list; + } + + /** + * Begins listening to the specified tree model and tree selection model. + */ + protected void startListening() { + model.addTreeModelListener(this); + selectionModel.addTreeSelectionListener(this); + contents.addListSelectionListener(this); + } + + /** + * Stops listening to the specified tree model and tree selection model. + */ + protected void stopListening() { + model.removeTreeModelListener(this); + selectionModel.removeTreeSelectionListener(this); + contents.removeListSelectionListener(this); + } + + /** + * Returns the TreeModel used by the TreeChooser. + */ + public TreeModel getModel() { + return model; + } + + /** + * Sets the TreeModel used by the TreeChooser. + */ + public void setModel(TreeModel aTreeModel) { + stopListening(); + model = aTreeModel; + bogusJTree.setModel(aTreeModel); + pathStack.removeAllElements(); + startListening(); + displayHome(); + } + + /** + * Returns the TreeSelectionModel used by the TreeChooser. + */ + public TreeSelectionModel getSelectionModel() { + return selectionModel; + } + + /** + * Sets the TreeSelectionModel used by the TreeChooser. + */ + public void setSelectionModel(TreeSelectionModel aSelectionModel) { + selectionModel = aSelectionModel; + if (aSelectionModel.getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION) { + contents.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + } else { + contents.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + } + updateSelection(); + } + + /** + * Returns the TreeCellRenderer used by the TreeChooser. + */ + public TreeCellRenderer getRenderer() { + return renderer; + } + + /** + * Sets the TreeCellRenderer used by the TreeChooser. + */ + public void setRenderer(TreeCellRenderer aRenderer) { + renderer = aRenderer; + updateContents(); + } + + /** + * Displays the "home" directory. This implementation displays the root node's + * children. + */ + public void displayHome() { + setDisplayPath(null); + } + + /** + * Displays the parent path of the currently displayed path. + */ + public void displayParent() { + setDisplayPath(displayPath.getParentPath()); + } + + /** + * Displays the last displayed path before the current one, emulating the + * behavior of a "back" button. + */ + public void displayPrevious() { + if (pathStack.empty()) { + displayHome(); + } else { + setDisplayPathDirect((TreePath) pathStack.pop()); + updateContents(); + } + } + + /** + * Pushes the previous item onto the stack, sets the display path, and then + * updates the contents. If aPath is null, the root node's children are + * displayed. + */ + public void setDisplayPath(TreePath aPath) { + if (aPath == null) { + aPath = new TreePath(getModel().getRoot()); + } + if (!displayPath.equals(aPath)) { + pathStack.push(displayPath); + setDisplayPathDirect(aPath); + } + updateContents(); + } + + /** + * Sets the displayPath field and does not update the stack nor update the + * contents. + */ + protected void setDisplayPathDirect(TreePath aPath) { + displayPath = aPath; + } + + /** + * Gets the currently displayed path. + */ + public TreePath getDisplayPath() { + return displayPath; + } + + /** + * Called when selected path changes or when model indicates that the displayed + * path has changed. + */ + protected void updateContents() { + stopListening(); + + // update combo box + comboBoxModel.fireContentsChanged(); + + // update list contents + Object displayedObject = displayPath.getLastPathComponent(); + /* + * //FIXME: this display group doesn't seem to be getting the sort orderings + * from parent if ( displayedObject instanceof net.wotonomy.ui.EODisplayGroup ) + * System.out.println( + * ((net.wotonomy.ui.EODisplayGroup)displayedObject).displayedObjects() ); + */ + int count = model.getChildCount(displayedObject); + Object[] children = new Object[count]; + for (int i = 0; i < count; i++) { + children[i] = model.getChild(displayedObject, i); + } + contents.setListData(children); + + startListening(); + + // synchronize the selection + updateSelection(); + } + + /** + * Updates the selection in the list to reflect the selection in the tree + * selection model. + */ + public void updateSelection() { + int index; + Object last = displayPath.getLastPathComponent(); + TreePath[] selectionPaths = selectionModel.getSelectionPaths(); + if (selectionPaths != null) { + List selectedIndices = new LinkedList(); + for (int i = 0; i < selectionPaths.length; i++) { + if (displayPath.equals(selectionPaths[i].getParentPath())) { + index = getModel().getIndexOfChild(last, selectionPaths[i].getLastPathComponent()); + if (index != -1) { + selectedIndices.add(new Integer(index)); + } else // should never happen + { + throw new WotonomyException("Could not find child of displayed node."); + } + } + } + int[] selected = new int[selectedIndices.size()]; + for (int i = 0; i < selected.length; i++) { + selected[i] = ((Integer) selectedIndices.get(i)).intValue(); + } + stopListening(); + contents.setSelectedIndices(selected); + startListening(); + } + } + + // interface TreeModelListener + + public void treeNodesChanged(TreeModelEvent evt) { + /* + * if ( displayPath.getLastPathComponent().toString().equals( + * evt.getTreePath().getLastPathComponent().toString() ) ) { System.out.println( + * "TreeChooser.treeNodesChanged: " + count++ ); + */ + updateContents(); + /* + * } else { System.out.println( evt.getTreePath() + " != " + displayPath ); } + */ + } + + public void treeNodesInserted(TreeModelEvent evt) { // updateContents(); - } - - public void treeNodesRemoved( TreeModelEvent evt ) - { + } + + public void treeNodesRemoved(TreeModelEvent evt) { // updateContents(); - } - - public void treeStructureChanged( TreeModelEvent evt ) - { - if ( ( evt.getTreePath().equals( displayPath ) ) - || ( evt.getTreePath().isDescendant( displayPath ) ) ) - { + } + + public void treeStructureChanged(TreeModelEvent evt) { + if ((evt.getTreePath().equals(displayPath)) || (evt.getTreePath().isDescendant(displayPath))) { // setDisplayPath( evt.getTreePath() ); - } - - displayHome(); - } - - // interface TreeSelectionListener - - /** - * Called when the tree selection model's value changes. - * This is presumably an external change, so this calls - * updateSelection. - */ - public void valueChanged( TreeSelectionEvent evt ) - { - updateSelection(); - } - - // interface ListSelectionListener - - /** - * Called when user changes the selection in the list. - * This implementation updates the tree selection model - * with the corresponding selection. - */ - public void valueChanged( ListSelectionEvent evt ) - { - if ( ! evt.getValueIsAdjusting() ) - { - Object last = displayPath.getLastPathComponent(); - int[] selection = contents.getSelectedIndices(); - TreePath[] selectionPaths = new TreePath[ selection.length ]; - for ( int i = 0; i < selection.length; i++ ) - { - selectionPaths[i] = displayPath.pathByAddingChild( - getModel().getChild( last, selection[i] ) ); - } - selectionModel.setSelectionPaths( selectionPaths ); - } - - } - - // interface ListCellRenderer - - /** - * This method returns the component returned by the tree cell renderer. - */ - public Component getListCellRendererComponent( - JList list, - Object value, - int index, - boolean isSelected, - boolean cellHasFocus ) - { - boolean isLeaf = ( model.isLeaf( value ) ); - - bogusJTree.setForeground( list.getForeground() ); - bogusJTree.setBackground( list.getBackground() ); - - JComponent result = (JComponent) renderer.getTreeCellRendererComponent( - bogusJTree, value, isSelected, (list != contents), - isLeaf, index, cellHasFocus ); -/* - if ( ( list != contents ) && ( index > -1 ) ) - { - result.setBorder( - BorderFactory.createEmptyBorder( 0, index*pathIndent, 0, 0 ) ); - } - else - { - result.setBorder( - BorderFactory.createEmptyBorder() ); - } -*/ - return result; - } - - // interface ActionListener - - public void actionPerformed( ActionEvent evt ) - { - String command = evt.getActionCommand(); - - if ( HOME.equals( command ) ) - { - displayHome(); - } - else - if ( UP.equals( command ) ) - { - displayParent(); - } - else - if ( BACK.equals( command ) ) - { - displayPrevious(); - } - else - if ( SELECT.equals( command ) ) - { - Cursor oldCursor = getCursor(); - setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) ); - - int index = contents.getSelectedIndex(); - // if selection - if ( index != -1 ) - { - Object parent = displayPath.getLastPathComponent(); - Object child = getModel().getChild( parent, index ); - // if selected item is not a leaf - if ( getModel().getChildCount( child ) > 0 ) - { - // navigate to selected item - setDisplayPath( displayPath.pathByAddingChild( child ) ); - } - } - - setCursor( oldCursor ); - } - - } - - private class ChooserComboBoxModel implements ComboBoxModel - { - TreeChooser treeChooser; - Vector listeners; - - ChooserComboBoxModel( TreeChooser aTreeChooser ) - { - treeChooser = aTreeChooser; - listeners = new Vector(); - } - - public int getSize() - { - return treeChooser.displayPath.getPathCount(); - } - - public Object getElementAt(int index) - { - return treeChooser.displayPath.getPathComponent( index ); - } - - public Object getSelectedItem() - { - return treeChooser.displayPath.getLastPathComponent(); - } - - public void setSelectedItem(Object anItem) - { - if ( ! ( - treeChooser.displayPath.getLastPathComponent().equals( anItem ) ) ) - { - Object[] items = treeChooser.displayPath.getPath(); - TreePath path = new TreePath( getModel().getRoot() ); - for ( int i = 1; i < items.length; i++ ) - { - if ( path.getLastPathComponent() == anItem ) - { - treeChooser.setDisplayPath( path ); - return; - } - path = path.pathByAddingChild( items[i] ); - } - } - } - - public void addListDataListener(ListDataListener l) - { - listeners.add( l ); - } - - public void removeListDataListener(ListDataListener l) - { - listeners.remove( l ); - } - - public void fireContentsChanged() - { - Enumeration e = listeners.elements(); - while ( e.hasMoreElements() ) - { - ((ListDataListener)e.nextElement()).contentsChanged( - new ListDataEvent( - this, ListDataEvent.CONTENTS_CHANGED, 0, getSize() ) ); - } - } - } - -} + } + + displayHome(); + } + + // interface TreeSelectionListener + + /** + * Called when the tree selection model's value changes. This is presumably an + * external change, so this calls updateSelection. + */ + public void valueChanged(TreeSelectionEvent evt) { + updateSelection(); + } + // interface ListSelectionListener + /** + * Called when user changes the selection in the list. This implementation + * updates the tree selection model with the corresponding selection. + */ + public void valueChanged(ListSelectionEvent evt) { + if (!evt.getValueIsAdjusting()) { + Object last = displayPath.getLastPathComponent(); + int[] selection = contents.getSelectedIndices(); + TreePath[] selectionPaths = new TreePath[selection.length]; + for (int i = 0; i < selection.length; i++) { + selectionPaths[i] = displayPath.pathByAddingChild(getModel().getChild(last, selection[i])); + } + selectionModel.setSelectionPaths(selectionPaths); + } + + } + + // interface ListCellRenderer + + /** + * This method returns the component returned by the tree cell renderer. + */ + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, + boolean cellHasFocus) { + boolean isLeaf = (model.isLeaf(value)); + + bogusJTree.setForeground(list.getForeground()); + bogusJTree.setBackground(list.getBackground()); + + JComponent result = (JComponent) renderer.getTreeCellRendererComponent(bogusJTree, value, isSelected, + (list != contents), isLeaf, index, cellHasFocus); + /* + * if ( ( list != contents ) && ( index > -1 ) ) { result.setBorder( + * BorderFactory.createEmptyBorder( 0, index*pathIndent, 0, 0 ) ); } else { + * result.setBorder( BorderFactory.createEmptyBorder() ); } + */ + return result; + } + + // interface ActionListener + + public void actionPerformed(ActionEvent evt) { + String command = evt.getActionCommand(); + + if (HOME.equals(command)) { + displayHome(); + } else if (UP.equals(command)) { + displayParent(); + } else if (BACK.equals(command)) { + displayPrevious(); + } else if (SELECT.equals(command)) { + Cursor oldCursor = getCursor(); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + int index = contents.getSelectedIndex(); + // if selection + if (index != -1) { + Object parent = displayPath.getLastPathComponent(); + Object child = getModel().getChild(parent, index); + // if selected item is not a leaf + if (getModel().getChildCount(child) > 0) { + // navigate to selected item + setDisplayPath(displayPath.pathByAddingChild(child)); + } + } + + setCursor(oldCursor); + } + + } + + private class ChooserComboBoxModel implements ComboBoxModel { + TreeChooser treeChooser; + Vector listeners; + + ChooserComboBoxModel(TreeChooser aTreeChooser) { + treeChooser = aTreeChooser; + listeners = new Vector(); + } + + public int getSize() { + return treeChooser.displayPath.getPathCount(); + } + + public Object getElementAt(int index) { + return treeChooser.displayPath.getPathComponent(index); + } + + public Object getSelectedItem() { + return treeChooser.displayPath.getLastPathComponent(); + } + + public void setSelectedItem(Object anItem) { + if (!(treeChooser.displayPath.getLastPathComponent().equals(anItem))) { + Object[] items = treeChooser.displayPath.getPath(); + TreePath path = new TreePath(getModel().getRoot()); + for (int i = 1; i < items.length; i++) { + if (path.getLastPathComponent() == anItem) { + treeChooser.setDisplayPath(path); + return; + } + path = path.pathByAddingChild(items[i]); + } + } + } + + public void addListDataListener(ListDataListener l) { + listeners.add(l); + } + + public void removeListDataListener(ListDataListener l) { + listeners.remove(l); + } + + public void fireContentsChanged() { + Enumeration e = listeners.elements(); + while (e.hasMoreElements()) { + ((ListDataListener) e.nextElement()) + .contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, getSize())); + } + } + } + +} diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeTableCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeTableCellRenderer.java index fbf3791..c6e1a99 100644 --- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeTableCellRenderer.java +++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeTableCellRenderer.java @@ -32,193 +32,158 @@ import javax.swing.JViewport; import javax.swing.table.TableCellRenderer; /** -* A TableCellRenderer that paints a portion of a JTree. -* Extends JViewport to take advantage of buffering and -* fast blitting (avoids repeated clipping and repainting). -* Defaults opaque to false: to see selection background -* painted, call setOpaque( true ). -* -* @author michael@mpowers.net -* @author $Author: cgruber $ -* @version $Revision: 904 $ -*/ + * A TableCellRenderer that paints a portion of a JTree. Extends JViewport to + * take advantage of buffering and fast blitting (avoids repeated clipping and + * repainting). Defaults opaque to false: to see selection background painted, + * call setOpaque( true ). + * + * @author michael@mpowers.net + * @author $Author: cgruber $ + * @version $Revision: 904 $ + */ public class TreeTableCellRenderer extends JViewport implements TableCellRenderer, MouseListener { - JTree tree; - Component emptyComponent; - JTable delegateTable; - int lastKnownColumn; - - /** - * Constructor takes a JTree and modifies it by setting - * rootVisible to false, showsRootHandles to true, - * opaque to false, and border to null. - */ - public TreeTableCellRenderer( JTree aTree ) - { - setView( aTree ); - setBorder( null ); - tree = aTree; - tree.setRootVisible( false ); - tree.setShowsRootHandles( true ); - tree.setBorder( null ); - tree.setOpaque( false ); - - Object renderer = tree.getCellRenderer(); - if ( renderer instanceof JComponent ) - { - ((JComponent)renderer).setOpaque( false ); - } - Object editor = tree.getCellEditor(); - if ( editor instanceof JComponent ) - { - ((JComponent)editor).setOpaque( false ); - } - - this.setOpaque( false ); - emptyComponent = new JLabel(); - } - - public Component getTableCellRendererComponent( - JTable table, Object value, - boolean isSelected, boolean hasFocus, - int row, int column) - { - if ( isSelected ) - { - setForeground( table.getSelectionForeground() ); - setBackground( table.getSelectionBackground() ); - } - else - { - setForeground( table.getForeground() ); - setBackground( table.getBackground() ); - } - - lastKnownColumn = column; - if ( delegateTable != table ) - { - if ( delegateTable != null ) - { - delegateTable.removeMouseListener( this ); - } - table.addMouseListener( this ); - delegateTable = table; - } - - Rectangle rect = tree.getRowBounds( row ); - if ( rect != null ) - { - setViewPosition( new Point( 0 /*rect.x*/, rect.y ) ); - - //FIXME: this causes problems for some LAFs (like Metal): - // in particular, the table height seems to get stuck. - //if ( table.getRowHeight( row ) != rect.height ) - //{ - // table.setRowHeight( row, rect.height ); - //} - return this; - } - else - { - return emptyComponent; - } - } - - public void mouseClicked(MouseEvent e) - { - delegateToTree( e ); - } - - public void mousePressed(MouseEvent e) - { - delegateToTree( e ); - } - - public void mouseReleased(MouseEvent e) - { - delegateToTree( e ); - } - - public void mouseEntered(MouseEvent e) - { - delegateToTree( e ); - } - - public void mouseExited(MouseEvent e) - { - delegateToTree( e ); - } - - protected void delegateToTree(MouseEvent e) - { - int col = delegateTable.getColumnModel().getColumnIndexAtX( e.getX() ); - if ( col == lastKnownColumn ) - { - Rectangle nodeRect = tree.getRowBounds( 0 ); - Rectangle cellRect = delegateTable.getCellRect( -1, col, false ); - if ( nodeRect != null ) - { - e.translatePoint( -cellRect.x, nodeRect.y ); - tree.dispatchEvent( // e ); - new MouseEvent( tree, e.getID(), e.getWhen(), e.getModifiers(), - e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger() ) ); - } - } - } - - public void repaint() - { - //if ( delegateTable != null ) delegateTable.repaint(); - - // not calling super.repaint() does not seem to cause - // any problems so we're not doing it. - } + JTree tree; + Component emptyComponent; + JTable delegateTable; + int lastKnownColumn; + + /** + * Constructor takes a JTree and modifies it by setting rootVisible to false, + * showsRootHandles to true, opaque to false, and border to null. + */ + public TreeTableCellRenderer(JTree aTree) { + setView(aTree); + setBorder(null); + tree = aTree; + tree.setRootVisible(false); + tree.setShowsRootHandles(true); + tree.setBorder(null); + tree.setOpaque(false); + + Object renderer = tree.getCellRenderer(); + if (renderer instanceof JComponent) { + ((JComponent) renderer).setOpaque(false); + } + Object editor = tree.getCellEditor(); + if (editor instanceof JComponent) { + ((JComponent) editor).setOpaque(false); + } + + this.setOpaque(false); + emptyComponent = new JLabel(); + } + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { + if (isSelected) { + setForeground(table.getSelectionForeground()); + setBackground(table.getSelectionBackground()); + } else { + setForeground(table.getForeground()); + setBackground(table.getBackground()); + } + + lastKnownColumn = column; + if (delegateTable != table) { + if (delegateTable != null) { + delegateTable.removeMouseListener(this); + } + table.addMouseListener(this); + delegateTable = table; + } + + Rectangle rect = tree.getRowBounds(row); + if (rect != null) { + setViewPosition(new Point(0 /* rect.x */, rect.y)); + + // FIXME: this causes problems for some LAFs (like Metal): + // in particular, the table height seems to get stuck. + // if ( table.getRowHeight( row ) != rect.height ) + // { + // table.setRowHeight( row, rect.height ); + // } + return this; + } else { + return emptyComponent; + } + } + + public void mouseClicked(MouseEvent e) { + delegateToTree(e); + } + + public void mousePressed(MouseEvent e) { + delegateToTree(e); + } + + public void mouseReleased(MouseEvent e) { + delegateToTree(e); + } + + public void mouseEntered(MouseEvent e) { + delegateToTree(e); + } + + public void mouseExited(MouseEvent e) { + delegateToTree(e); + } + + protected void delegateToTree(MouseEvent e) { + int col = delegateTable.getColumnModel().getColumnIndexAtX(e.getX()); + if (col == lastKnownColumn) { + Rectangle nodeRect = tree.getRowBounds(0); + Rectangle cellRect = delegateTable.getCellRect(-1, col, false); + if (nodeRect != null) { + e.translatePoint(-cellRect.x, nodeRect.y); + tree.dispatchEvent( // e ); + new MouseEvent(tree, e.getID(), e.getWhen(), e.getModifiers(), e.getX(), e.getY(), + e.getClickCount(), e.isPopupTrigger())); + } + } + } + + public void repaint() { + // if ( delegateTable != null ) delegateTable.repaint(); + + // not calling super.repaint() does not seem to cause + // any problems so we're not doing it. + } } /* - * $Log$ - * Revision 1.2 2006/02/18 23:19:05 cgruber - * Update imports and maven dependencies. + * $Log$ Revision 1.2 2006/02/18 23:19:05 cgruber Update imports and maven + * dependencies. * - * Revision 1.1 2006/02/16 13:22:22 cgruber - * Check in all sources in eclipse-friendly maven-enabled packages. + * Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in + * eclipse-friendly maven-enabled packages. * - * Revision 1.11 2003/08/06 23:07:53 chochos - * general code cleanup (mostly, removing unused imports) + * Revision 1.11 2003/08/06 23:07:53 chochos general code cleanup (mostly, + * removing unused imports) * - * Revision 1.10 2002/04/12 20:07:35 mpowers - * Fixed cool/annoying view position. + * Revision 1.10 2002/04/12 20:07:35 mpowers Fixed cool/annoying view position. * - * Revision 1.9 2002/04/09 18:12:21 mpowers - * Fixes for 1.4. + * Revision 1.9 2002/04/09 18:12:21 mpowers Fixes for 1.4. * - * Revision 1.8 2002/03/22 22:39:24 mpowers - * Can now move column to any position in the table. + * Revision 1.8 2002/03/22 22:39:24 mpowers Can now move column to any position + * in the table. * - * Revision 1.7 2002/03/11 03:13:22 mpowers - * Adjusting for viewport position; no longer responding to repaint(). + * Revision 1.7 2002/03/11 03:13:22 mpowers Adjusting for viewport position; no + * longer responding to repaint(). * - * Revision 1.6 2002/03/07 23:04:36 mpowers - * Refining TreeColumnAssociation. + * Revision 1.6 2002/03/07 23:04:36 mpowers Refining TreeColumnAssociation. * - * Revision 1.5 2002/03/05 23:18:28 mpowers - * Added documentation. - * Added isSelectionPaintedImmediate and isSelectionTracking attributes - * to TableAssociation. - * Added getTableAssociation to TableColumnAssociation. + * Revision 1.5 2002/03/05 23:18:28 mpowers Added documentation. Added + * isSelectionPaintedImmediate and isSelectionTracking attributes to + * TableAssociation. Added getTableAssociation to TableColumnAssociation. * - * Revision 1.3 2002/02/27 23:19:17 mpowers - * Refactoring of TreeAssociation to create TreeModelAssociation parent. + * Revision 1.3 2002/02/27 23:19:17 mpowers Refactoring of TreeAssociation to + * create TreeModelAssociation parent. * - * Revision 1.2 2002/02/18 23:13:55 mpowers - * Only setting row height when needed. + * Revision 1.2 2002/02/18 23:13:55 mpowers Only setting row height when needed. * - * Revision 1.1 2002/02/18 03:46:08 mpowers - * Implemented TreeTableCellRenderer. + * Revision 1.1 2002/02/18 03:46:08 mpowers Implemented TreeTableCellRenderer. * * */ - - -- cgit v1.2.3