Visual Color Imbalance Detector: Reconstructed project directories and files
[VistaCID.git] / org / tjworld / components / JIconToggleButton.java
1 /*\r
2         * JIconToggleButton.java\r
3  *\r
4         * Created on 28 October 2001, 15:59\r
5         * $Header: /VisTA/CID/Components/JIconToggleButton.java 7     7/11/01 16:53 Tj $\r
6         * \r
7         * $History: JIconToggleButton.java $\r
8  * \r
9  * *****************  Version 7  *****************\r
10  * User: Tj           Date: 7/11/01    Time: 16:53\r
11  * Updated in $/VisTA/CID/Components\r
12  * Fixed bug in updateUI() whereby setUI() was calling\r
13  * BasicIconRadioButtonUI.createUI() rather than\r
14  * BasicIconToggleButtonUI.createUI() !! Note to self: beware of copying\r
15  * and pasting code from similar classes!\r
16  * \r
17  * *****************  Version 6  *****************\r
18  * User: Tj           Date: 2/11/01    Time: 1:31\r
19  * Updated in $/VisTA/CID/Components\r
20  * Added algorithm to updateUI() to dynamically detect if this component\r
21  * has become a part of the javax.swing package\r
22  * \r
23  * *****************  Version 5  *****************\r
24  * User: Tj           Date: 1/11/01    Time: 22:53\r
25  * Updated in $/VisTA/CID/Components\r
26  * Ready for Bean Testing\r
27  * \r
28  * *****************  Version 4  *****************\r
29  * User: Tj           Date: 31/10/01   Time: 5:09\r
30  * Updated in $/VisTA/CID/Components\r
31  * Added VSS header\r
32 */\r
33 \r
34 package org.tjworld.components;\r
35 \r
36 import java.awt.Graphics;\r
37 import javax.swing.AbstractButton;\r
38 import javax.swing.JToggleButton;\r
39 import javax.swing.Icon;\r
40 import javax.swing.UIManager;\r
41 import javax.accessibility.*;\r
42 import javax.swing.plaf.basic.BasicToggleButtonUI;\r
43 import org.tjworld.components.BasicIconToggleButtonUI;\r
44 \r
45 /**\r
46         * A JToggleButton that can have an icon in the same way that a JMenuItem can.\r
47  *\r
48         * That is: [stateIcon]   [[userIcon] [Text]]\r
49  *\r
50         * setIcon() and setPressedIcon() access the User functionality indicator icons.\r
51         * get/setSelectedStateIcon(), get/setDeselectedStateIcon(), \r
52         * get/setDisabledSelectedStateIcon() and get/setDisabledDeselectedStateIcon()\r
53         * access the button state indicator icon.\r
54         *\r
55         * By default the state icons are null in a JIconToggleButton, but are used in inheriting\r
56         * classes such as JIconRadioButton and JIconCheckBox.\r
57         *\r
58         * Positioning logic (in BasicIconToggleButtonUI) places the stateIcon 'first' and closest to the\r
59         * leading edge of the button.\r
60         * In a LeftToRight Locale that means close to the left edge of the button.\r
61         * The default icon is placed with the button Text (if any) and positioned according to the rules\r
62         * governing text placement.\r
63         * \r
64         * getMinimumSize() will return a dimension that will show the stateIcon and userIcon but no text.\r
65         *\r
66         * A new UI delegate <code>BasicIconToggleButtonUI</code> knows how to paint this component, and\r
67         * is registered with the UIManager when an instance of this component is first created.\r
68         * \r
69         * Note: AbstractButton.setIcon() and AbstractButton.setPressedIcon() have been overridden\r
70         * to remove the bug that fires the wrong accessibleContext.PropertyChange notification in \r
71         * Java versions upto and including 1.4 beta 2 (the bug means setIcon() passes pressedIcon and \r
72         * setPressedIcon() passes defaultIcon as the 'new' Object reference!)\r
73         *\r
74         * PropertyChange Event Properties:\r
75         * "selectedStateIcon", "disabledSelectedStateIcon", "deselectedStateIcon","disabledDeselectedStateIcon"\r
76         * \r
77         * @author  TJ\r
78         * @version 1.4.4 1 Nov, 2001 Removed all User icons - will use the inherited versions and only provide logic for State Icons in this class\r
79         * @version 1.4.4 31 Oct, 2001 Renamed 'check' icons to 'state' icons\r
80         * version 1.4.3 31 Oct, 2001 Increased variations in check Icons: selected/deselected/enabled/disabled\r
81         * version 1.4.2 Altered icon handling: userIcon now defaultIcon, checkIcon is new\r
82  * version 1.4.1\r
83         * @see JToggleButton\r
84         * @see JRadioButton\r
85         * @see JCheckBox\r
86         * @see JRadioButtonMenuItem\r
87         * @see JCheckBoxMenuItem\r
88         * @see BasicIconToggleButtonUI\r
89         * @see BasicIconButtonListener\r
90         * @since 1.4\r
91  */\r
92 public class JIconToggleButton extends JToggleButton {\r
93  /**\r
94   * @see #getUIClassID\r
95   * @see #readObject\r
96   */\r
97  private static final String uiClassID = "BasicIconToggleButtonUI";\r
98 \r
99  /** The state indicator displayed when the button is enabled + selected */\r
100         protected Icon selectedStateIcon = null;\r
101         \r
102         /** The state indicator displayed when the button is disabled + selected */\r
103         protected Icon disabledSelectedStateIcon = null;\r
104         \r
105         /** The state indicator displayed when the button is enabled + unselected */\r
106         protected Icon unselectedStateIcon = null;\r
107         \r
108         /** The state indicator displayed when the button is disabled + unselected */\r
109         protected Icon disabledUnselectedStateIcon = null;\r
110         \r
111  /** Identifies a change to the icon that represents the enabled selected button */\r
112  public static final String SELECTED_STATEICON_CHANGED_PROPERTY = "selectedStateIcon";\r
113 \r
114  /** Identifies a change to the icon that represents the disabled selected button */\r
115  public static final String DISABLED_SELECTED_STATEICON_CHANGED_PROPERTY = "disabledSelectedStateIcon";\r
116         \r
117         /** Identifies a change to the icon that represents the enabled unselected button */\r
118         public static final String UNSELECTED_STATEICON_CHANGED_PROPERTY = "unselectedStateIcon";\r
119         \r
120         /** Identifies a change to the icon that represents the disabled unselected button */\r
121         public static final String DISABLED_UNSELECTED_STATEICON_CHANGED_PROPERTY = "disabledUnselectedStateIcon";\r
122 \r
123         /** Creates new 'empty' JIconToggleButton */\r
124  public JIconToggleButton() {\r
125                 this((String)null, (Icon)null, (Icon)null, false);\r
126  }\r
127 \r
128         /** Creates a new JIconToggleButton with enabled and disabled User icons */\r
129         public JIconToggleButton(Icon user, Icon disabledUser) {\r
130                 this((String)null, user, disabledUser, false);\r
131         }\r
132         \r
133         /** Creates a new JIconToggleButton with Label, enabled and disabled User icons */\r
134  public JIconToggleButton(String text, Icon user, Icon disabledUser, boolean selected) {\r
135                 super(text, user, selected);\r
136                 super.setDisabledIcon(disabledUser);\r
137         }\r
138         \r
139  /**\r
140   * Sets the look and feel object that renders this component.\r
141   *\r
142   * @param ui  the <code>BasicIconToggleButtonUI</code> L&F object\r
143   * @see UIDefaults#getUI\r
144   * @beaninfo\r
145   *        bound: true\r
146   *       hidden: true\r
147   *    attribute: visualUpdate true\r
148   *  description: The UI object that implements the Component's LookAndFeel. \r
149   */\r
150  public void setUI(BasicIconToggleButtonUI ui) {\r
151   // needed for compilations before BasicToggleButtonUI has been compiled\r
152  // public void setUI(BasicToggleButtonUI ui) {\r
153   super.setUI(ui);\r
154  }\r
155     \r
156  /**\r
157   * Resets the UI property with a value from the current look and feel.\r
158   *\r
159   * @see JComponent#updateUI\r
160   */\r
161  public void updateUI() {\r
162                 // clever way of dynamically detecting if JIconToggleButton has become part of the\r
163                 // javax.swing package rather than the org.tjworld.components package\r
164   String className = this.getClass().getName();\r
165                 \r
166                 if(className.startsWith("javax.swing.")) //** Use when this component IS a part of a Look & Feel \r
167                  setUI((BasicIconToggleButtonUI)UIManager.getUI(this));\r
168                 \r
169                 else\r
170                 setUI(BasicIconToggleButtonUI.createUI(this));\r
171    /* use when this component IS NOT a part of any Look & Feel\r
172                  * this makes sure the UIManager doesn't throw an error \r
173                         * when it tries to use the current Look & Feel classloader\r
174                         * to load the UI delegate, because the delegate (BasicIconRadioButtonUI)\r
175                         * isn't included in the Look & Feel package.\r
176                         */\r
177  }\r
178 \r
179  /**\r
180   * Returns a string that specifies the name of the l&f class\r
181   * that renders this component.\r
182   *\r
183   * @return String "ToggleButtonUI"\r
184   * @see JComponent#getUIClassID\r
185   * @see UIDefaults#getUI\r
186   * @beaninfo\r
187   *  description: A string that specifies the name of the L&F class\r
188   */\r
189  public String getUIClassID() {\r
190   return uiClassID;\r
191  }\r
192 \r
193         /** Convenience method to get the currently active state-indicator icon\r
194                 *\r
195                 * @returns The icon valid for the button's current state\r
196                 */\r
197         public Icon getStateIcon() {\r
198                 Icon state = null;\r
199                 if(isSelected()) { // button is selected\r
200                         state = selectedStateIcon; // standard condition\r
201                         if(!isEnabled() && disabledSelectedStateIcon != null) // might be able to indicate a disabled button too\r
202                                 state = disabledSelectedStateIcon;\r
203                 }\r
204                 else { // unselected\r
205                         state = unselectedStateIcon;\r
206                         if(!isEnabled() && disabledUnselectedStateIcon != null) // might be able to indicate a disabled button too\r
207                          state = disabledUnselectedStateIcon;\r
208                 }\r
209                 return state;\r
210         }\r
211         \r
212         /** Sets the enabled selected state icon\r
213                 * Displayed when the button is enabled + selected\r
214                 * @param stateicon The new icon, or null to remove the existing Icon\r
215   * @beaninfo\r
216   *  bound: true\r
217   *  attribute: visualUpdate true\r
218   *  description: The selected state icon for the button.\r
219                 */\r
220  public void setSelectedStateIcon(Icon stateicon) {\r
221                 Icon oldSelStateIcon = selectedStateIcon; // copy the old Icon\r
222                 selectedStateIcon = stateicon; // replace with the new\r
223                 // inform Listeners; revalidate & repaint (when button is enabled)\r
224                 changeIcon(SELECTED_STATEICON_CHANGED_PROPERTY, oldSelStateIcon, selectedStateIcon, true); \r
225         }\r
226         \r
227         /** Gets the enabled selected state icon \r
228                 * @returns The state icon, or null if no icon is set\r
229                 */\r
230         public Icon getSelectedStateIcon() {\r
231                 return selectedStateIcon;\r
232         }\r
233         \r
234         /** Sets the disabled selected state icon\r
235                 * Displayed when the button is disabled + selected\r
236                 * @param stateicon The new icon, or null to remove the existing icon\r
237   * @beaninfo\r
238   *  bound: true\r
239   *  attribute: visualUpdate true\r
240   *  description: The disabled selected state icon for the button.\r
241                 */\r
242         public void setDisabledSelectedStateIcon(Icon stateicon) {\r
243                 Icon oldDisSelStateIcon = disabledSelectedStateIcon; // copy the old Icon\r
244                 disabledSelectedStateIcon = stateicon; // replace with the new\r
245                 // inform Listeners; revalidate & repaint (when button is disabled)\r
246                 changeIcon(DISABLED_SELECTED_STATEICON_CHANGED_PROPERTY, oldDisSelStateIcon, disabledSelectedStateIcon, false);\r
247         }\r
248         \r
249         /** Gets the disabled selected state icon\r
250                 * @returns The disabled icon, or null if no icon is set\r
251                 */\r
252         public Icon getDisabledSelectedStateIcon() {\r
253                 return disabledSelectedStateIcon;\r
254         }\r
255                                 \r
256         /** Sets the enabled unselected state icon\r
257                 * Displayed when the button is enabled + unselected\r
258                 * @param stateicon The new icon, or null to remove the existing icon\r
259   * @beaninfo\r
260   *  bound: true\r
261   *  attribute: visualUpdate true\r
262   *  description: The unselected state icon for the button.\r
263                 */\r
264  public void setUnselectedStateIcon(Icon stateicon) {\r
265                 Icon oldUnselStateIcon = unselectedStateIcon; // copy the old Icon\r
266                 unselectedStateIcon = stateicon; // replace with the new\r
267                 // inform Listeners; revalidate & repaint (when button is enabled)\r
268                 changeIcon(UNSELECTED_STATEICON_CHANGED_PROPERTY, oldUnselStateIcon, unselectedStateIcon, true); \r
269         }\r
270         \r
271         /** Gets the enabled unselected state icon \r
272                 * @returns The state icon, or null if no icon is set\r
273                 */\r
274         public Icon getUnselectedStateIcon() {\r
275                 return unselectedStateIcon;\r
276         }\r
277         \r
278         /** Sets the disabled unselected state icon\r
279                 * Displayed when the button is disabled + unselected\r
280                 * @param stateicon The new icon, or null to remove the existing icon\r
281   * @beaninfo\r
282   *  bound: true\r
283   *  attribute: visualUpdate true\r
284   *  description: The disabled unselected state icon for the button.\r
285                 */\r
286         public void setDisabledUnselectedStateIcon(Icon stateicon) {\r
287                 Icon oldDisUnselStateIcon = disabledUnselectedStateIcon; // copy the old Icon\r
288                 disabledUnselectedStateIcon = stateicon; // replace with the new\r
289                 // inform Listeners; revalidate & repaint (when button is disabled)\r
290                 changeIcon(DISABLED_UNSELECTED_STATEICON_CHANGED_PROPERTY, oldDisUnselStateIcon, disabledUnselectedStateIcon, false);\r
291         }\r
292         \r
293         /** Gets the disabled unselected state icon\r
294                 * @returns The disabled icon, or null if no icon is set\r
295                 */\r
296         public Icon getDisabledUnselectedStateIcon() {\r
297                 return disabledUnselectedStateIcon;\r
298         }\r
299 \r
300  /** Overrides AbstractButton.setIcon()\r
301                 * Overcomes bug whereby the Accessible context causes the wrong newIcon to be passed\r
302                 * with the PropertyChange Event\r
303                 *\r
304   * @param defaultIcon the icon used as the default image\r
305                 * @see AbstractButton#setIcon\r
306   * @beaninfo \r
307   *  bound: true\r
308   *  attribute: visualUpdate true\r
309   *  description: The button's default icon\r
310   */\r
311  public void setIcon(Icon newIcon) {\r
312   Icon oldIcon = getIcon(); // get the old one - can't access the private member Icon directly\r
313   \r
314                 /* Bit of a bodge, can't simply replicate the code in AbstractButton.setIcon()\r
315                         * because members are private.\r
316                         * Because of this, this Button will fire two PropertyChange Events, one from \r
317                         * AbstractButton.setIcon() quickly followed by the corrected PropertyChange from\r
318                         * this method.\r
319                         */\r
320                 super.setIcon(newIcon);\r
321                 \r
322                 // inform Listeners; revalidate & repaint (when button is enabled)\r
323                 changeIcon(AbstractButton.ICON_CHANGED_PROPERTY, oldIcon, newIcon, true);\r
324         }\r
325         \r
326         /** Overrides AbstractButton.setPressedIcon()\r
327                 * Overcomes bug whereby the Accessible context causes the wrong newIcon to be passed\r
328                 * with the PropertyChange Event\r
329                 *\r
330                 * @see AbstractButton#setIcon\r
331   * @param defaultIcon the icon used as the default image\r
332   * @beaninfo\r
333   *  bound: true\r
334   *  attribute: visualUpdate true\r
335   *  description: The pressed icon for the button.\r
336   */\r
337  public void setPressedIcon(Icon newIcon) {\r
338   Icon oldIcon = getPressedIcon();\r
339   \r
340                 /* Bit of a bodge, can't simply replicate the code in AbstractButton.setIcon()\r
341                         * because members are private.\r
342                         * Because of this, this Button will fire two PropertyChange Events, one from \r
343                         * AbstractButton.setPressedIcon() quickly followed by the corrected PropertyChange from\r
344                         * this method.\r
345                         */\r
346                 super.setPressedIcon(newIcon);\r
347                 \r
348                 // inform Listeners; revalidate & repaint (when button is disabled)\r
349                 changeIcon(AbstractButton.PRESSED_ICON_CHANGED_PROPERTY, oldIcon, newIcon, true);\r
350  }\r
351 \r
352         /**\r
353   * Overrides the <code>JComponent.paintComponent()</code> method, then paints\r
354                 * the interior in the same way as the UI delegate would paint a RadioButtonMenuItem,\r
355                 * but without the Accelerator or Arrow.\r
356                 *\r
357         * That is: [stateIcon]   [[userIcon] [Text]]\r
358                 *\r
359   * @param g the <code>Graphics</code> object to protect\r
360   * @see JComponent#paintComponent\r
361   */\r
362   protected void paintComponent(Graphics g) {\r
363    if (ui != null) {\r
364     Graphics scratchGraphics = g.create();\r
365                                 try {\r
366                                         ui.update(scratchGraphics, this);\r
367                                 }\r
368                                 finally {\r
369      scratchGraphics.dispose();\r
370                                 }\r
371    }\r
372   }\r
373                 \r
374         /**\r
375                 * Common functionality required when an Icon is changed\r
376                 *\r
377                 * Update the Accessible Context and might need to revalidate (if the Icon's size has changed)\r
378                 * and repaint (if it's currently on display)\r
379                 *       \r
380                 * @param property The Property Name to fire to registered PropertyChange event listeners \r
381                 * @param oldIcon The Icon being replaced\r
382                 * @param newIcon The replacement Icon\r
383                 * @param enabled true if this Icon is used when the button is enabled, false if used when the button is disabled \r
384                 */\r
385  protected void changeIcon(String property, Icon oldIcon, Icon newIcon, boolean enabled) {\r
386   firePropertyChange(property, oldIcon, newIcon); // inform PropertyChange Event listeners\r
387   if(accessibleContext != null)\r
388    accessibleContext.firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, oldIcon, newIcon);\r
389 \r
390   if(newIcon != oldIcon ) { // don't redo layout or paint if the Icon hasn't altered\r
391    if(newIcon == null || oldIcon == null || newIcon.getIconWidth() != oldIcon.getIconWidth() || newIcon.getIconHeight() != oldIcon.getIconHeight())\r
392                                 revalidate();\r
393                         \r
394    if(isEnabled() == enabled) repaint(); // only cause a repaint when the Icon being changed matches the one currently on display\r
395                 }\r
396         }\r
397         \r
398         /** Reports useful information about this Button\r
399                 * @returns textual information\r
400                 */\r
401         public String toString() {\r
402                 String text = super.toString();\r
403                 if(text.charAt(text.length()-1) == ']') // strip off closing brackets from super's report\r
404                         text = text.substring(0,text.length()-1);\r
405 \r
406                 text += ",selectedStateIcon=";\r
407                 if(selectedStateIcon != null)\r
408                         text += selectedStateIcon.toString();\r
409                 text += ",disabledSelectedStateIcon=";\r
410                 if(disabledSelectedStateIcon != null)\r
411                         text += disabledSelectedStateIcon.toString();\r
412                 text += ",unselectedStateIcon=";\r
413                 if(unselectedStateIcon != null)\r
414                         text += unselectedStateIcon.toString();\r
415                 text += ",disabledUnselectedStateIcon=";\r
416                 if(disabledUnselectedStateIcon != null)\r
417                         text += disabledUnselectedStateIcon.toString();\r
418   text += ",ComponentUI=" + getUIClassID() + "]";\r
419                 return text;\r
420         }\r
421 }\r