Swing utility tasks
A utility class which collects common tasks can greatly reduce code duplication. This is especially true for Swing, where many elements of GUI construction are repetitive.
Extended Example
This utility class has methods for :
- centering windows on the screen, or with respect to a parent window
- equalizing sizes of components
- imposing an alignment on all items in a Box
- creating borders of a standard size
- creating dialog titles of a standard format
- retrieving images
- creating pairs of components, both for user input (JLabel and JTextField) and simple display (two JLabel objects)
- using GridBagConstraints in a concise manner
- formatting numbers, times, and percents
package hirondelle.stocks.util.ui; import hirondelle.stocks.util.Args; import hirondelle.stocks.util.Consts; import hirondelle.stocks.util.Util; import java.util.*; import java.text.*; import java.net.URL; import javax.swing.*; import javax.swing.border.Border; import java.awt.*; import javax.swing.plaf.metal.MetalLookAndFeel; import hirondelle.stocks.preferences.GeneralLookPreferencesEditor; /** Static convenience methods for GUIs which eliminate code duplication.*/ public final class UiUtil { /** * <tt>pack</tt>, center, and <tt>show</tt> a window on the screen. * * <P>If the size of <tt>aWindow</tt> exceeds that of the screen, * then the size of <tt>aWindow</tt> is reset to the size of the screen. */ public static void centerAndShow(Window aWindow){ //note that the order here is important aWindow.pack(); /* * If called from outside the event dispatch thread (as is * the case upon startup, in the launch thread), then * in principle this code is not thread-safe: once pack has * been called, the component is realized, and (most) further * work on the component should take place in the event-dispatch * thread. * * In practice, it is exceedingly unlikely that this will lead * to an error, since invisible components cannot receive events. */ Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); Dimension window = aWindow.getSize(); //ensure that no parts of aWindow will be off-screen if (window.height > screen.height) { window.height = screen.height; } if (window.width > screen.width) { window.width = screen.width; } int xCoord = (screen.width/2 - window.width/2); int yCoord = (screen.height/2 - window.height/2); aWindow.setLocation( xCoord, yCoord ); aWindow.show(); } /** * A window is packed, centered with respect to a parent, and then shown. * * <P>This method is intended for dialogs only. * * <P>If centering with respect to a parent causes any part of the dialog * to be off screen, then the centering is overidden, such that all of the * dialog will always appear fully on screen, but it will still appear * near the parent. * * @param aWindow must have non-null result for <tt>aWindow.getParent</tt>. */ public static void centerOnParentAndShow(Window aWindow){ aWindow.pack(); Dimension parent = aWindow.getParent().getSize(); Dimension window = aWindow.getSize(); int xCoord = aWindow.getParent().getLocationOnScreen().x + (parent.width/2 - window.width/2) ; int yCoord = aWindow.getParent().getLocationOnScreen().y + (parent.height/2 - window.height/2) ; //Ensure that no part of aWindow will be off-screen Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); int xOffScreenExcess = xCoord + window.width - screen.width; if ( xOffScreenExcess > 0 ) { xCoord = xCoord - xOffScreenExcess; } if (xCoord < 0 ) { xCoord = 0; } int yOffScreenExcess = yCoord + window.height - screen.height; if ( yOffScreenExcess > 0 ) { yCoord = yCoord - yOffScreenExcess; } if (yCoord < 0) { yCoord = 0; } aWindow.setLocation( xCoord, yCoord ); aWindow.show(); } /** * Return a border of dimensions recommended by the Java Look and Feel * Design Guidelines, suitable for many common cases. * *<P>Each side of the border has size {@link UiConsts#STANDARD_BORDER}. */ public static Border getStandardBorder(){ return BorderFactory.createEmptyBorder( UiConsts.STANDARD_BORDER, UiConsts.STANDARD_BORDER, UiConsts.STANDARD_BORDER, UiConsts.STANDARD_BORDER ); } /** * Return text which conforms to the Look and Feel Design Guidelines * for the title of a dialog : the application name, a colon, then * the name of the specific dialog. * *<P>Example return value: <tt>StocksMonitor: Preferences</tt> * * @param aSpecificDialogName must have visible content */ public static String getDialogTitle(String aSpecificDialogName){ Args.checkForContent(aSpecificDialogName); StringBuilder result = new StringBuilder(Consts.APP_NAME); result.append(": "); result.append(aSpecificDialogName); return result.toString(); } /** * Make a horizontal row of buttons of equal size, whch are equally spaced, * and aligned on the right. * * <P>The returned component has border spacing only on the top (of the size * recommended by the Look and Feel Design Guidelines). * All other spacing must be applied elsewhere ; usually, this will only mean * that the dialog's top-level panel should use {@link #getStandardBorder}. * * @param aButtons contains the buttons to be placed in a row. */ public static JComponent getCommandRow(java.util.List<JComponent> aButtons){ equalizeSizes( aButtons ); JPanel panel = new JPanel(); LayoutManager layout = new BoxLayout(panel, BoxLayout.X_AXIS); panel.setLayout( layout ); panel.setBorder( BorderFactory.createEmptyBorder(UiConsts.THREE_SPACES, 0, 0, 0) ); panel.add( Box.createHorizontalGlue() ); Iterator<JComponent> buttonsIter = aButtons.iterator(); while ( buttonsIter.hasNext() ) { panel.add( buttonsIter.next() ); if ( buttonsIter.hasNext() ) { panel.add( Box.createHorizontalStrut(UiConsts.ONE_SPACE) ); } } return panel; } /** * Make a vertical row of buttons of equal size, whch are equally spaced, * and aligned on the right. * * <P>The returned component has border spacing only on the left (of the size * recommended by the Look and Feel Design Guidelines). * All other spacing must be applied elsewhere ; usually, this will only mean * that the dialog's top-level panel should use {@link #getStandardBorder}. * * @param aButtons contains the buttons to be placed in a column */ public static JComponent getCommandColumn( java.util.List<JComponent> aButtons ){ equalizeSizes( aButtons ); JPanel panel = new JPanel(); LayoutManager layout = new BoxLayout(panel, BoxLayout.Y_AXIS); panel.setLayout( layout ); panel.setBorder( BorderFactory.createEmptyBorder(0,UiConsts.THREE_SPACES, 0,0) ); //(no for-each is used here, because of the 'not-yet-last' check) Iterator<JComponent> buttonsIter = aButtons.iterator(); while ( buttonsIter.hasNext() ) { panel.add(buttonsIter.next()); if ( buttonsIter.hasNext() ) { panel.add( Box.createVerticalStrut(UiConsts.ONE_SPACE) ); } } panel.add( Box.createVerticalGlue() ); return panel; } /** * Return an <tt>ImageIcon</tt> using its <tt>String</tt> identifier. * * @param aImageId starts with '/', and refers to an image resource * which is accessible through {@link Class#getResource}. */ public static ImageIcon getImageIcon(String aImageId){ if( ! aImageId.startsWith(BACK_SLASH) ){ throw new IllegalArgumentException( "Image identifier does not start with backslash: " + aImageId ); } return fetchImageIcon(aImageId, UiUtil.class); } /** * Return an <tt>ImageIcon</tt> using its <tt>String</tt> identifier, relative to * a given class. * * @param aImageId does NOT start with '/', and must refer to an image resource which is * accessible through {@link Class#getResource}. * @param aClass the class relative to which the image is located. */ public static ImageIcon getImageIcon(String aImageId, Class<?> aClass){ if( aImageId.startsWith(BACK_SLASH) ){ throw new IllegalArgumentException( "Image identifier starts with a backslash: " + aImageId ); } return fetchImageIcon(aImageId, aClass); } /** * Return a square icon which paints nothing, and whose dimensions correspond * to the user preference for icon size. * * <P>A common problem occurs with text alignment in menus, where there is * a mixture of menu items with and without an icon. Adding an empty icon * to menu items which do not have one will adjust its alignment to match * that of the others which do have an icon. */ public static Icon getEmptyIcon(){ GeneralLookPreferencesEditor prefs = new GeneralLookPreferencesEditor(); return prefs.hasLargeIcons() ? EmptyIcon.SIZE_24 : EmptyIcon.SIZE_16; } /** * Return a <tt>Dimension</tt> whose size is defined not in terms of pixels, * but in terms of a given percent of the screen's width and height. * *<P> Use to set the preferred size of a component to a certain * percentage of the screen. * * @param aPercentWidth percentage width of the screen, in range <tt>1..100</tt>. * @param aPercentHeight percentage height of the screen, in range <tt>1..100</tt>. */ public static final Dimension getDimensionFromPercent( int aPercentWidth, int aPercentHeight ){ int low = 1; int high = 100; Args.checkForRange(aPercentWidth, low, high); Args.checkForRange(aPercentHeight, low, high); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); return calcDimensionFromPercent(screenSize, aPercentWidth, aPercentHeight); } /** * Sets the items in <tt>aComponents</tt> to the same size. * * <P>Sets each component's preferred and maximum sizes. * The actual size is determined by the layout manager, whcih adjusts * for locale-specific strings and customized fonts. (See this * <a href="http://java.sun.com/products/jlf/ed2/samcode/prefere.html">Sun doc</a> * for more information.) * * @param aComponents items whose sizes are to be equalized */ public static void equalizeSizes(java.util.List<JComponent> aComponents) { Dimension targetSize = new Dimension(0,0); for(JComponent comp: aComponents ) { Dimension compSize = comp.getPreferredSize(); double width = Math.max(targetSize.getWidth(), compSize.getWidth()); double height = Math.max(targetSize.getHeight(), compSize.getHeight()); targetSize.setSize(width, height); } setSizes(aComponents, targetSize); } /** * Create a pair of components, a <tt>JLabel</tt> and an associated * <tt>JTextField</tt>, as is typically used for user input. * *<P>The <tt>JLabel</tt> appears on the left, and the <tt>JTextField</tt> * appears on the same row, just to the right of the <tt>JLabel</tt>. * The <tt>JLabel</tt> has a mnemonic which forwards focus to the * <tt>JTextField</tt> when activated. * * @param aContainer holds the pair of components. * @param aName text of the <tt>JLabel</tt> component. * @param aInitialValue possibly-null initial value to appear * in the <tt>JTextField</tt>; if <tt>null</tt>, then * <tt>JTextField</tt> will be blank. * @param aMnemonic <tt>KeyEvent</tt> field, used as the mnemonic for * the <tt>JLabel</tt>. * @param aConstraints applied to the <tt>JLabel</tt>; the corresponding * constraints for the <tt>JTextField</tt> are the same as * <tt>aConstraints</tt>, except for <tt>gridx</tt> being incremented by one; * in addition, if <tt>aConstraints</tt> has <tt>weightx=0</tt> (the default), * then the entry field will receive <tt>weightx=1.0</tt> (entry field gets more * horizontal space upon resize). * @param aTooltip possibly-null text displayed as tool tip for the * <tt>JTextField</tt> ; if <tt>null</tt>, the tool tip is turned off. * @return the user input <tt>JTextField</tt>. */ public static JTextField addSimpleEntryField( Container aContainer, String aName, String aInitialValue, int aMnemonic, GridBagConstraints aConstraints, String aTooltip ){ Args.checkForNull(aName); JLabel label = new JLabel(aName); label.setDisplayedMnemonic(aMnemonic); aContainer.add( label, aConstraints ); JTextField result = new JTextField(UiConsts.SIMPLE_FIELD_WIDTH); label.setLabelFor(result); result.setToolTipText(aTooltip); if (aInitialValue != null) { result.setText(aInitialValue); } aConstraints.gridx = ++aConstraints.gridx; if (aConstraints.weightx == 0.0){ aConstraints.weightx = 1.0; } aContainer.add(result, aConstraints); return result; } /** * Return a set of constraints with convenient default values. * *<P>Return constraints with these values : *<ul> * <li> <tt>gridx, gridy</tt> - set to <tt>aX, aY</tt> * <li> <tt>anchor - GridBagConstraints.WEST</tt> * <li> <tt>insets - Insets(0,0,0, UiConsts.ONE_SPACE)</tt> *</ul> * *<P> All other items simply take their default values : *<ul> * <li> <tt>fill - GridBagConstraints.NONE</tt> * <li> <tt>gridwidth, gridheight - 0, 0</tt> * <li> <tt>weightx , weighty - 0, 0</tt> * <li> <tt>ipadx, ipady - 0, 0</tt> *</ul> * * <P>The caller is free to change the returned constraints, to customize for * their particular needs. * * @param aY in range <tt>0..10</tt>. * @param aX in range <tt>0..10</tt>. */ public static GridBagConstraints getConstraints(int aY, int aX){ int low = 0; int high = 10; Args.checkForRange(aY, low, high); Args.checkForRange(aX, low, high); GridBagConstraints result = new GridBagConstraints(); result.gridy = aY; result.gridx = aX; result.anchor = GridBagConstraints.WEST; result.insets = new Insets(0,0,0,UiConsts.ONE_SPACE); return result; } /** * Return {@link #getConstraints(int, int)}, with the addition of setting * <tt>gridwidth</tt> to <tt>aWidth</tt>, and setting * <tt>gridheight</tt> to <tt>aHeight</tt>. * * <P>The caller is free to change the returned constraints, to customize for * their particular needs. * * @param aY in range <tt>0..10</tt>. * @param aX in range <tt>0..10</tt>. * @param aWidth in range <tt>1..10</tt>. * @param aHeight in range <tt>1..10</tt>. */ public static GridBagConstraints getConstraints(int aY, int aX, int aWidth, int aHeight){ int low = 0; int high = 10; Args.checkForRange(aHeight, low, high); Args.checkForRange(aWidth, low, high); GridBagConstraints result = getConstraints(aY, aX); result.gridheight = aHeight; result.gridwidth = aWidth; return result; } /** * Create a pair of <tt>JLabel</tt> components, as is typically needed * for display of a name-value pair. * * <P>The name appears on the left, and the value appears on the right, * all on the same row. A colon and an empty space are appended to the name. * * <P> If the the length of "value" label is greater than * {@link UiConsts#MAX_LABEL_LENGTH}, then the text is truncated, an ellipsis * is placed at its end, and the full text is placed in a tooltip. * * @param aContainer holds the pair of components. * @param aName text of the name <tt>JLabel</tt>. * @param aValue possibly-null ; if null, then an empty <tt>String</tt> * is used for the value; otherwise <tt>Object.toString</tt> is used. * @param aConstraints for the name <tt>JLabel</tt>; the corresponding * constraints for the value <tt>JLabel</tt> are mostly taken from * <tt>aConstraints</tt>, except for <tt>gridx</tt> being incremented by one * (<tt>weightx</tt> may differ as well - see <tt>aWeightOnDisplay</tt>.) * @param aWeightOnDisplay if true, then set <tt>weightx</tt> for the value * field to 1.0 (to give it more horizontal space upon resize). * @return the <tt>JLabel</tt> for the value (which is usually variable). */ public static JLabel addSimpleDisplayField( Container aContainer, String aName, Object aValue, GridBagConstraints aConstraints, boolean aWeightOnDisplay ){ StringBuilder formattedName = new StringBuilder(aName); formattedName.append(": "); JLabel name = new JLabel( formattedName.toString() ); aContainer.add( name, aConstraints ); String valueText = (aValue != null? aValue.toString() : Consts.EMPTY_STRING); JLabel value = new JLabel(valueText); truncateLabelIfLong(value); aConstraints.gridx = ++aConstraints.gridx; if (aWeightOnDisplay){ aConstraints.weightx = 1.0; } aContainer.add( value, aConstraints ); return value; } /** * Present a number of read-only items to the user as a vertical listing * of <tt>JLabel</tt> name-value pairs. * * <P>Each pair is added in the style of * {@link #addSimpleDisplayField} (its <tt>aConstraints</tt> param are those * returned by {@link #getConstraints(int, int)}, and its <tt>aWeightOnDisplay</tt> * param is set to <tt>true</tt>). * * <P>The order of presentation is determined by the iteration order of * <tt>aNameValuePairs</tt>. * *<P>The number of items which should be presented using this method is limited, since * no scrolling mechanism is given to the user. * * @param aContainer holds the display fields. * @param aNameValuePairs has <tt>String</tt> keys for the names, * and values are possibly null <tt>Object</tt>s; * if null, then an empty <tt>String</tt> is displayed, otherwise * <tt>Object.toString</tt> is called on the value and displayed. */ public static void addSimpleDisplayFields( Container aContainer, Map<String, String> aNameValuePairs ) { Set<String> keys = aNameValuePairs.keySet(); int rowIdx = 0; for(String name: keys) { String value = aNameValuePairs.get(name); if(value == null){ value = Consts.EMPTY_STRING; } UiUtil.addSimpleDisplayField( aContainer, name, value, UiUtil.getConstraints(rowIdx,0), true ); ++rowIdx; } } /** * Adds "glue" (an empty component with desired resizing behavior) to the bottom * row of a <tt>GridBagLayout</tt> of components. When resized, this glue will * take up extra vertical space. * * <P>This method is especially useful for text data presented in a listing or * tabular form. Such components naturally resize horizontally, while their vertical * resizing should often be absent. If such a listing is resized vertically, then this * glue can take up the remaining vertical space, keeping the text at the top. * * @param aPanel uses <tt>GridBagLayout</tt>, and contains components whose * <tt>weighty</tt> values are all 0.0 (the default). * @param aLastRowIdx index of the last row of components, in which the glue will be * placed. */ public static void addVerticalGridGlue(JPanel aPanel, int aLastRowIdx) { GridBagConstraints glueConstraints = UiUtil.getConstraints(aLastRowIdx,0); glueConstraints.weighty = 1.0; glueConstraints.fill = GridBagConstraints.VERTICAL; aPanel.add( new JLabel(), glueConstraints ); } /** * Return a <tt>String</tt>, suitable for presentation to the end user, * representing a percentage having two decimal places, using the default locale. * * <P>An example return value is "5.15%". The intent of this method is to * provide a standard representation and number of decimals for the entire * application. If a different number of decimal places is required, then * the caller should use <tt>NumberFormat</tt> instead. */ public static String getLocalizedPercent( Number aNumber ){ NumberFormat localFormatter = NumberFormat.getPercentInstance(); localFormatter.setMinimumFractionDigits(2); return localFormatter.format( aNumber.doubleValue() ); } /** * Return a <tt>String</tt>, suitable for presentation to the end user, * representing an integral number with no decimal places, using the default * locale. * * <P>An example return value is "8,000". The intent of this method is to * provide a standard representation of integers for the entire * application. */ public static String getLocalizedInteger( Number aNumber ) { NumberFormat localFormatter = NumberFormat.getNumberInstance(); return localFormatter.format( aNumber.intValue() ); } /** * Return a <tt>String</tt>, suitable for presentation to the end user, * representing a date in <tt>DateFormat.SHORT</tt> and the default locale. */ public static String getLocalizedTime(Date aDate){ DateFormat dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT); return dateFormat.format(aDate); } /** * Make the sytem emit a beep. * * <P>May not beep unless the speakers are turned on, so this cannot * be guaranteed to work. */ public static void beep(){ Toolkit.getDefaultToolkit().beep(); } /** * An alternative to multi-line labels, for the presentation of * several lines of text, and for which the line breaks are determined * solely by the widget. * * @param aText must have visible content, doesn't contain newline characters or html. * @return <tt>JTextArea</tt> which is not editable, has improved spacing over the * supplied default (placing {@link UiConsts#ONE_SPACE} on the left and right), * which wraps lines on word boundaries, and whose background color is the * same as {@link javax.swing.plaf.metal.MetalLookAndFeel#getMenuBackground}. */ public static JTextArea getStandardTextArea(String aText){ Args.checkForContent(aText); if ( aText.indexOf(Consts.NEW_LINE) != -1 ){ throw new IllegalArgumentException("Must not contain new line characters: " + aText); } JTextArea result = new JTextArea(aText); result.setEditable(false); result.setWrapStyleWord(true); result.setLineWrap(true); result.setMargin( new Insets(0,UiConsts.ONE_SPACE,0,UiConsts.ONE_SPACE) ); //this is a bit hacky: the desired color is "secondary3", but cannot see how //to reference it directly; hence, an element which uses secondary3 is used instead. result.setBackground( MetalLookAndFeel.getMenuBackground() ); return result; } /** * An alternative to multi-line labels, for the presentation of * several lines of text, and for which line breaks are determined * solely by <tt>aText</tt>, and not by the widget. * * @param aText has visible content * @return <tt>JTextArea</tt> which is not editable, has improved spacing over the * supplied default (placing {@link UiConsts#ONE_SPACE} on the left and right), * and whose background color is the same as * {@link javax.swing.plaf.metal.MetalLookAndFeel#getMenuBackground}. */ public static JTextArea getStandardTextAreaHardNewLines(String aText){ Args.checkForContent(aText); JTextArea result = new JTextArea(aText); result.setEditable(false); result.setMargin( new Insets(0,UiConsts.ONE_SPACE,0,UiConsts.ONE_SPACE) ); result.setBackground( MetalLookAndFeel.getMenuBackground() ); return result; } /** * Imposes a uniform horizontal alignment on all items in a container. * *<P> Intended especially for <tt>BoxLayout</tt>, where all components need * to share the same alignment in order for display to be reasonable. * (Indeed, this method may only work for <tt>BoxLayout</tt>, since apparently * it is the only layout to use <tt>setAlignmentX, setAlignmentY</tt>.) * * @param aContainer contains only <tt>JComponent</tt> objects. */ public static void alignAllX(Container aContainer, UiUtil.AlignX aAlignment){ java.util.List<Component> components = Arrays.asList( aContainer.getComponents() ); for(Component comp: components){ JComponent jcomp = (JComponent)comp; jcomp.setAlignmentX( aAlignment.getValue() ); } } /** Enumeration for horizontal alignment. */ public enum AlignX { LEFT(Component.LEFT_ALIGNMENT), CENTER(Component.CENTER_ALIGNMENT), RIGHT(Component.RIGHT_ALIGNMENT); public float getValue(){ return fValue; } private final float fValue; private AlignX(float aValue){ fValue = aValue; } } /** * Imposes a uniform vertical alignment on all items in a container. * *<P> Intended especially for <tt>BoxLayout</tt>, where all components need * to share the same alignment in order for display to be reasonable. * (Indeed, this method may only work for <tt>BoxLayout</tt>, since apparently * it is the only layout to use <tt>setAlignmentX, setAlignmentY</tt>.) * * @param aContainer contains only <tt>JComponent</tt> objects. */ public static void alignAllY(Container aContainer, UiUtil.AlignY aAlignment){ java.util.List components = Arrays.asList( aContainer.getComponents() ); Iterator compsIter = components.iterator(); while ( compsIter.hasNext() ) { JComponent comp = (JComponent)compsIter.next(); comp.setAlignmentY( aAlignment.getValue() ); } } /** Type-safe enumeration vertical alignment. */ public enum AlignY { TOP(Component.TOP_ALIGNMENT), CENTER(Component.CENTER_ALIGNMENT), BOTTOM(Component.BOTTOM_ALIGNMENT); float getValue(){ return fValue; } private final float fValue; private AlignY( float aValue){ fValue = aValue; } } /** * Ensure that <tt>aRootPane</tt> has no default button associated with it. * * <P>Intended mainly for dialogs where the user is confirming a delete action. * In this case, an explicit Yes or No is preferred, with no default action being * taken when the user hits the Enter key. */ public static void noDefaultButton(JRootPane aRootPane){ aRootPane.setDefaultButton(null); } // PRIVATE // private static final String BACK_SLASH = "/"; /** * If <tt>aIconName</tt> indicates that the icon is part of the standard graphic * repository (by starting with "/toolbar"), then append either "16.gif" or * "24.gif" to the name, according to the user's current preference for icon size. */ private static String addSizeToStandardIcon(String aIconName){ assert( Util.textHasContent(aIconName) ); StringBuilder result = new StringBuilder(aIconName); if ( aIconName.startsWith("/toolbar") ) { GeneralLookPreferencesEditor prefs = new GeneralLookPreferencesEditor(); if ( prefs.hasLargeIcons() ) { result.append("24.gif"); } else { result.append("16.gif"); } } return result.toString(); } private static void setSizes(java.util.List aComponents, Dimension aDimension){ Iterator compsIter = aComponents.iterator(); while ( compsIter.hasNext() ) { JComponent comp = (JComponent) compsIter.next(); comp.setPreferredSize( (Dimension)aDimension.clone() ); comp.setMaximumSize( (Dimension)aDimension.clone() ); } } private static Dimension calcDimensionFromPercent( Dimension aSourceDimension, int aPercentWidth, int aPercentHeight ){ int width = aSourceDimension.width * aPercentWidth/100; int height = aSourceDimension.height * aPercentHeight/100; return new Dimension(width, height); } /** * If aLabel has text which is longer than MAX_LABEL_LENGTH, then truncate * the label text and place an ellipsis at the end; the original text is placed * in a tooltip. * * This is particularly useful for displaying file names, whose length * can vary widely between deployments. */ private static void truncateLabelIfLong(JLabel aLabel){ String originalText = aLabel.getText(); if (originalText.length() > UiConsts.MAX_LABEL_LENGTH){ aLabel.setToolTipText( originalText ); String truncatedText = originalText.substring(0, UiConsts.MAX_LABEL_LENGTH) + Consts.ELLIPSIS ; aLabel.setText(truncatedText); } } private static ImageIcon fetchImageIcon(String aImageId, Class<?> aClass){ String imgLocation = addSizeToStandardIcon(aImageId); URL imageURL = aClass.getResource(imgLocation); if ( imageURL != null ) { return new ImageIcon(imageURL); } else { throw new IllegalArgumentException("Cannot retrieve image using id: " + aImageId); } } }
See Also :
Would you use this technique?
|
|