Visual Color Imbalance Detector: Reconstructed project directories and files
[VistaCID.git] / org / tjworld / components / BasicIconToggleButtonUI.java
1 /*\r
2         * BasicIconToggleButtonUI.java\r
3  *\r
4         * Created on 29 October 2001, 17:41\r
5         * $Header: /VisTA/CID/Components/BasicIconToggleButtonUI.java 16    7/11/01 19:46 Tj $\r
6         *\r
7         * $History: BasicIconToggleButtonUI.java $\r
8  * \r
9  * *****************  Version 16  *****************\r
10  * User: Tj           Date: 7/11/01    Time: 19:46\r
11  * Updated in $/VisTA/CID/Components\r
12  * Exploring how to implement correct L&F ButtonUI.paintButtonPressed()\r
13  * service\r
14  * \r
15  * *****************  Version 15  *****************\r
16  * User: Tj           Date: 7/11/01    Time: 16:51\r
17  * Updated in $/VisTA/CID/Components\r
18  * Verified propertyPrefix being read\r
19  * \r
20  * *****************  Version 14  *****************\r
21  * User: Tj           Date: 2/11/01    Time: 3:19\r
22  * Updated in $/VisTA/CID/Components\r
23  * Fixed problems in layoutIconToggleButton() that prevented\r
24  * layoutCompoundLabel inserting ellipsis into truncated label text\r
25  * \r
26  * *****************  Version 13  *****************\r
27  * User: Tj           Date: 2/11/01    Time: 2:20\r
28  * Updated in $/VisTA/CID/Components\r
29  * Moved from paintStateIcon(...) to sub-class overrides, the algorithms\r
30  * to detect the type of sub-class to pass to paintIcon(...)\r
31  * \r
32  * *****************  Version 12  *****************\r
33  * User: Tj           Date: 2/11/01    Time: 1:04\r
34  * Updated in $/VisTA/CID/Components\r
35  * Added kludge to paintStateIcon() to prevent L&F exceptions in some\r
36  * cases\r
37  * \r
38  * *****************  Version 11  *****************\r
39  * User: Tj           Date: 1/11/01    Time: 22:53\r
40  * Updated in $/VisTA/CID/Components\r
41  * Ready for Bean Testing\r
42  * \r
43  * *****************  Version 10  *****************\r
44  * User: Tj           Date: 1/11/01    Time: 21:40\r
45  * Updated in $/VisTA/CID/Components\r
46  * Revised and extended Javadoc\r
47  * \r
48  * *****************  Version 9  *****************\r
49  * User: Tj           Date: 1/11/01    Time: 0:46\r
50  * Updated in $/VisTA/CID/Components\r
51  * Partially revised to remove excessive State-Icon logic, needs User-Icon\r
52  * logic revising too\r
53  * \r
54  * *****************  Version 8  *****************\r
55  * User: Tj           Date: 31/10/01   Time: 14:27\r
56  * Updated in $/VisTA/CID/Components\r
57  * Added createButtonListener(), Tested OK\r
58  * \r
59  * *****************  Version 7  *****************\r
60  * User: Tj           Date: 31/10/01   Time: 14:06\r
61  * Updated in $/VisTA/CID/Components\r
62  * Added GraphicsDebug support\r
63  * \r
64  * *****************  Version 6  *****************\r
65  * User: Tj           Date: 31/10/01   Time: 5:04\r
66  * Updated in $/VisTA/CID/Components\r
67         */\r
68 \r
69  package org.tjworld.components;\r
70         \r
71         import java.awt.Graphics;\r
72         import java.awt.Rectangle;\r
73         import java.awt.Font;\r
74         import java.awt.FontMetrics;\r
75         import java.awt.Dimension;\r
76         import java.awt.Insets;\r
77         import java.awt.Color;\r
78         import javax.swing.JComponent;\r
79         import javax.swing.Icon;\r
80         import javax.swing.AbstractButton;\r
81         import javax.swing.JToggleButton;\r
82         import javax.swing.JRadioButton;\r
83         import javax.swing.JCheckBox;\r
84         import javax.swing.ButtonModel;\r
85         import javax.swing.SwingUtilities;\r
86         import javax.swing.UIManager;\r
87         import javax.swing.plaf.ButtonUI;\r
88         import javax.swing.plaf.basic.BasicButtonUI;\r
89         import javax.swing.plaf.basic.BasicToggleButtonUI;\r
90         import javax.swing.plaf.basic.BasicHTML;\r
91         import javax.swing.plaf.basic.BasicButtonListener;\r
92  import javax.swing.plaf.UIResource;\r
93  import javax.swing.plaf.ComponentUI;\r
94  import javax.swing.text.View;\r
95         import javax.swing.DebugGraphics;\r
96         import org.tjworld.components.JIconToggleButton;\r
97         import org.tjworld.components.BasicIconButtonListener;\r
98 \r
99 /**\r
100  * Draws a Toggle button that incorporates a State Icon and a User Icon to visually indicate functionality\r
101         * in the same way that BasicMenuItemUI does for JMenuItem components.\r
102         *\r
103         * @see JToggleButton\r
104         * @see JMenuItem\r
105         * @see BasicMenuItemUI\r
106         * @see BasicToggleButtonUI\r
107         * @see BasicIconButtonListener\r
108         *\r
109         * @author  TJ\r
110         * @version 1.4.2\r
111         * version 1.4.1 31 Oct, 2001 Revised to support JIconToggleButton's revised state icons\r
112  * version 1.4.0\r
113         * @since 1.4\r
114  */\r
115 public class BasicIconToggleButtonUI extends BasicToggleButtonUI {\r
116         \r
117         /** Holds the L&F specific selected indicator icon, unless the component installs it's\r
118                 * own selectedStateIcon, when this UI delegate gets notified by a PropertyChange Event.\r
119                 * \r
120                 * Not used by the standard Toggle Button (which uses a border to indicate state). Used by\r
121                 * sub-classes such as JIconRadioButton and JIconCheckBox.\r
122                 */\r
123         protected Icon stateIcon = null;\r
124         \r
125         /** One global instance of the UI Delegate */\r
126         private final static BasicIconToggleButtonUI iconToggleButtonUI = new BasicIconToggleButtonUI();\r
127 \r
128  /** Masquarades as a ToggleButton so it can fit in with standard Look & Feels */\r
129         private final static String propertyPrefix = "ToggleButton" + ".";\r
130 \r
131         /** Creates new BasicIconToggleButtonUI */\r
132  public BasicIconToggleButtonUI() {\r
133  }\r
134 \r
135  /** Creates a new BasicIconToggleButton \r
136                 * @returns The single static instance of a BasicIconToggleButton\r
137                 */\r
138  public static ComponentUI createUI(JComponent b) {\r
139   return iconToggleButtonUI;\r
140  }\r
141 \r
142         /** Creates a new ButtonListener\r
143                 *\r
144                 * Overrides BasicButtonUI.createButtonListener(...) and returns a listener that\r
145                 * is on the look-out for changes to the State and User icons.\r
146                 *\r
147                 * Usually called by BasicButtonUI.installListeners(..)\r
148                 * @returns a new listener\r
149                 */\r
150         protected BasicButtonListener createButtonListener(AbstractButton b) {\r
151                 return (BasicButtonListener) new BasicIconButtonListener(b);\r
152         }\r
153 \r
154  /** Uses the ToggleButton properties */\r
155  protected String getPropertyPrefix() {\r
156   return propertyPrefix;\r
157  }\r
158 \r
159  /* paint() is much faster when the Rectangles aren't being dynamically created on each call,\r
160                 * so paint() is synchronized to ensure two components don't try to use the \r
161                 * rectangles at the same time!\r
162                 */\r
163  private static Rectangle zeroRect = new Rectangle(0,0,0,0); // no-size\r
164         private static Rectangle stateIconRect = new Rectangle(); // the button-state indicator\r
165  private static Rectangle userIconRect = new Rectangle(); // the function indicator\r
166  private static Rectangle textRect = new Rectangle(); // the label\r
167  private static Rectangle labelRect = new Rectangle(); // contains userIcon and Text\r
168  private static Rectangle viewRect = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE); // the paintable area excluding border/insets/margins\r
169         \r
170         /** Reset the shared rectangles to zero ready for a new set of calculations */\r
171  private synchronized void resetRectangles() {\r
172   stateIconRect.setBounds(zeroRect);\r
173   userIconRect.setBounds(zeroRect);\r
174   textRect.setBounds(zeroRect);\r
175                 labelRect.setBounds(zeroRect);\r
176   viewRect.setBounds(0,0,Short.MAX_VALUE, Short.MAX_VALUE);\r
177  }\r
178 \r
179         /**\r
180                 * Creates a standard Java component that is an analog of the specialist JICon... components,\r
181                 * such that the paint() method can then implement the correct L&F paintButtonPressed() method\r
182                 * Should be overridden in subclasses to return the correct analog\r
183                 *\r
184                 * @returns A JComponent subclass being the analog of the called class\r
185                 */\r
186  public JComponent createCompatibleLandFComponent(JIconToggleButton iconTB) {\r
187                 JToggleButton tb = new JToggleButton(); // create the analog\r
188 \r
189                 ButtonModel my = iconTB.getModel();\r
190                 ButtonModel tbBM = tb.getModel(); // synchronize it's model\r
191 \r
192                 tbBM.setArmed(my.isArmed());\r
193                 tbBM.setPressed(my.isPressed());\r
194                 tbBM.setSelected(my.isSelected());\r
195                 tbBM.setRollover(my.isRollover());\r
196                 \r
197                 tb.setBorder(iconTB.getBorder());\r
198                 tb.setMargin(iconTB.getMargin());\r
199                 tb.setSize(iconTB.getSize());\r
200                 \r
201                 return tb; // analog of JIconToggleButton\r
202         }\r
203         \r
204  /**\r
205                 * Paint the state of the Button, including the optional User Icon functionality indicator.\r
206                 *\r
207                 * Using some sneaky tricks, the component ensures it gets painted according to the rules\r
208                 * of the currently installed Look and Feel, including when it is selected/armed/pressed.\r
209                 *\r
210                 * @param g The Graphics context to draw into\r
211                 * @param c The (JIconToggleButton) component to draw\r
212                 * @see JIconToggleButton\r
213                 * @see BasicToggleButtonUI\r
214                 */\r
215  public synchronized void paint(Graphics g, JComponent c) {\r
216   JIconToggleButton b = (JIconToggleButton) c;\r
217   ButtonModel model = b.getModel();\r
218          Dimension size = b.getSize();\r
219                 \r
220                 resetRectangles();\r
221   viewRect.setBounds(0, 0, size.width, size.height);\r
222 \r
223                 // graphics debugging info\r
224                 if((c.getDebugGraphicsOptions() & DebugGraphics.LOG_OPTION) == DebugGraphics.LOG_OPTION)\r
225                         System.out.println("JIconToggleButtonUI.paint() at " + viewRect);\r
226 \r
227   // stateIcon should be on the left edge UNLESS there is a border\r
228                 if(b.isBorderPainted() && b.getBorder() != null) {\r
229                 Insets i = b.getInsets();\r
230    viewRect.x += i.left; // calculate the drawable area\r
231    viewRect.y += i.top;\r
232    viewRect.width -= (i.right + viewRect.x);\r
233    viewRect.height -= (i.bottom + viewRect.y);\r
234                 }\r
235                 \r
236                 Font f = c.getFont();\r
237   g.setFont(f);\r
238   FontMetrics fm = g.getFontMetrics();\r
239 \r
240                 Icon sIcon = getStateIcon(b); // the user might be providing a custom state indicator\r
241                 \r
242   // layout the text and icons\r
243   String text = layoutIconToggleButton(\r
244    b, fm, sIcon, b.getIcon(), b.getText(), \r
245    b.getVerticalAlignment(), b.getHorizontalAlignment(),\r
246    b.getVerticalTextPosition(), b.getHorizontalTextPosition(),\r
247    viewRect, stateIconRect, userIconRect, textRect, labelRect,\r
248           b.getText() == null ? 0 : b.getIconTextGap(),\r
249                         b.getIconTextGap());\r
250 \r
251   g.setColor(b.getBackground());\r
252 \r
253   if(model.isArmed() && model.isPressed() || model.isSelected()) {\r
254                         JComponent jc = createCompatibleLandFComponent(b); // identify the ComponentUI delegate responsible for painting this type of Button (BasicToggleButtonUI, BasicRadioButtonUI, BasicCheckBoxUI)\r
255                         BasicButtonUI lfButtonUI = (BasicButtonUI) UIManager.getUI(jc); // create an instance of the L&F's ComponentUI delegate \r
256                         \r
257    if(lfButtonUI == null)\r
258                          lfButtonUI = new javax.swing.plaf.basic.BasicButtonUI();\r
259                         \r
260                         /* call the delegate, which has nothing to paint but the background, hopefully\r
261                                 * jc will be of an analog type, created by calls to createCompatibleLandFComponent()\r
262                                 * in this class or subclasses\r
263                                 *\r
264                                 * This ensures the component performs paintButtonPressed() according to the\r
265                                 * installed L&F. paintButtonPressed() is protected and therefore cannot be\r
266                                 * called directly from here\r
267                                 */\r
268    lfButtonUI.paint(g, jc); \r
269    \r
270                         // paintButtonPressed(g, b); // as at 1.4, BasicButtonUI.paintButtonPressed() returns having done nothing\r
271                 }\r
272          else if(b.isOpaque()) { // fill in the background\r
273           Insets insets = b.getInsets();\r
274           Insets margin = b.getMargin();\r
275             \r
276           g.fillRect(insets.left - margin.left, insets.top - margin.top, \r
277                   size.width - (insets.left-margin.left) - (insets.right - margin.right),\r
278                   size.height - (insets.top-margin.top) - (insets.bottom - margin.bottom));\r
279          }\r
280         \r
281                 // Paint the state Icon\r
282   if(sIcon != null)\r
283                         paintStateIcon(g, b, sIcon, stateIconRect);\r
284         \r
285   // Paint the User Icon\r
286   if(b.getIcon() != null)\r
287                         paintUserIcon(g, b, b.getIcon(), userIconRect);\r
288         \r
289   // Draw the Text\r
290   if(text != null && !text.equals("")) { // don't both trying to draw nothing\r
291    View v = (View) c.getClientProperty(BasicHTML.propertyKey);\r
292    if (v != null) // it's HTML formatted text\r
293     v.paint(g, textRect);\r
294    else // plain Unicode text\r
295     paintText(g, (AbstractButton) b, textRect, text); // make sure to use the 1.4 version of BasicButtonUI.paintText()\r
296   }\r
297         \r
298   // draw the dashed focus line, make sure the 'label' focus is drawn around both Text and User Icon\r
299   if (b.isFocusPainted() && b.hasFocus())\r
300           paintFocus(g, b, labelRect);\r
301  }\r
302 \r
303         /**\r
304                 * Paint the User Icon\r
305                 *\r
306                 * @param g The Graphics context to draw into\r
307                 * @param b The button that requires the Icon to be drawn\r
308                 * @param iconRect The bounds within in which to paint the Icon\r
309                 */\r
310  protected synchronized void paintUserIcon(Graphics g, JIconToggleButton b, Icon userIcon, Rectangle iconRect) {\r
311   ButtonModel model = b.getModel();\r
312   Icon icon = userIcon;\r
313                 \r
314   if(!model.isEnabled()) { // disabled button\r
315           if(model.isSelected()) // disabled + selected\r
316     icon = b.getDisabledSelectedIcon();\r
317           else // disabled + not selected\r
318     icon = b.getDisabledIcon();\r
319   }\r
320                 // alternatively, various forms of enabled button\r
321   else if(model.isPressed() && model.isArmed()) { // enabled + pressed\r
322    icon = b.getPressedIcon();\r
323    if(icon == null) // no Pressed Icon, so use Selected Icon\r
324                   icon = b.getSelectedIcon();\r
325                 }\r
326   \r
327                 else if(model.isSelected()) { // enabled + selected\r
328    if(b.isRolloverEnabled() && model.isRollover()) { // enabled + selected + mouse is over\r
329                   icon = b.getRolloverSelectedIcon();\r
330                   if (icon == null) // no RolloverSelected Icon, so use Selected Icon\r
331                    icon = b.getSelectedIcon();\r
332                  }\r
333    else \r
334                                 icon = b.getSelectedIcon();\r
335                 }\r
336   \r
337                 else if(b.isRolloverEnabled() && model.isRollover()) // enabled + mouse is over\r
338    icon = b.getRolloverIcon();\r
339                 \r
340                 else // enabled\r
341    icon = b.getIcon();\r
342                 \r
343                 if(icon == null) // if it failed to be set any other way!!!\r
344    icon = b.getIcon(); // try once more\r
345                         \r
346   if(icon != null) // only paint if an icon is available\r
347    icon.paintIcon(b, g, iconRect.x, iconRect.y);\r
348 \r
349                 // graphics debugging info\r
350                 if((b.getDebugGraphicsOptions() & DebugGraphics.LOG_OPTION) == DebugGraphics.LOG_OPTION)\r
351                         System.out.println("Painting User Icon at " + iconRect);\r
352  }\r
353 \r
354 /**\r
355                 * Paint the State (selected) Icon\r
356                 *\r
357                 * @param g The Graphics context to draw into\r
358                 * @param b The button that requires the Icon to be drawn\r
359                 * @param iconRect The bounds within in which to paint the Icon\r
360                 */\r
361  protected synchronized void paintStateIcon(Graphics g, JIconToggleButton b, Icon state, Rectangle iconRect) {\r
362                 /* remember that L&Fs might be drawing this rather than BITBLTing pixel arrays (GIF/JPGs etc.)\r
363                         * also beware that the button is converted to a type that the L&F's can recast to other types in their Icon paint\r
364                         * methods.\r
365                         * Metal L&F (Java 1.4 beta 2) will throw an exception because it casts to a JRadioButton when a JIconToggleButton\r
366                         * is passed without conversion to paintIcon(...) for JIconRadioButton & JIconCheckBox.\r
367                         * It could get away with casting to an AbstractButton and save my woes!\r
368                  */\r
369                 \r
370                 if(b != null) // only paint it if a component is available\r
371                  state.paintIcon(b, g, iconRect.x, iconRect.y); \r
372 \r
373                 // graphics debugging info\r
374                 if((b.getDebugGraphicsOptions() & DebugGraphics.LOG_OPTION) == DebugGraphics.LOG_OPTION)\r
375                         System.out.println("Painting State Icon at " + iconRect);\r
376  }\r
377 \r
378  /** Draw the focus outline\r
379                 *\r
380                 * Synchronized to prevent multipe components accessing the shared rectangles\r
381                 *\r
382                 * @param g The Graphics context\r
383                 * @param b The Button\r
384                 * @param labelRect The rectangle containing the area to be painted \r
385                 */\r
386         protected synchronized void paintFocus(Graphics g, JIconToggleButton b, Rectangle labelRect) {\r
387   g.setColor(getFocusColor((JComponent)b));\r
388          g.drawRect(labelRect.x-1, labelRect.y-1,labelRect.width+1, labelRect.height+1);\r
389                 // graphics debugging info\r
390                 if((b.getDebugGraphicsOptions() & DebugGraphics.LOG_OPTION) == DebugGraphics.LOG_OPTION)\r
391    System.out.println("Painting focus at" + labelRect);\r
392  }\r
393 \r
394  /** Get the colour used to paint focus\r
395                 * @param c The Component\r
396                 * @returns The focus colour\r
397                 */\r
398  protected Color getFocusColor(JComponent c) {\r
399                 Color focusColor = UIManager.getColor(getPropertyPrefix() + "focus");\r
400                 if(focusColor == null) // no custom focus colour set in this L & F, so try using one that is in the Basic L&F that most L&F's inherit from\r
401                         focusColor = UIManager.getColor(getPropertyPrefix() + "textHighlightText");\r
402                 if(focusColor == null) // still none, so use the component's foreground colour\r
403                         focusColor = c.getForeground();\r
404          return focusColor;\r
405  }\r
406         \r
407         /** Convenience method to get the currently active state-indicator icon\r
408                 *\r
409                 * The algorithm for choosing the icon is:\r
410                 *\r
411                 * If the button is selected, use the JIconToggleButton component's selectedStateIcon, unless\r
412                 * it is null, in which case use the default L&F state icon (which dynamically paints itself\r
413                 * depending on what state the button is in). Further, if the button is also disabled, use the\r
414                 * component's disabledSelectedStateIcon unless it is null.\r
415                 *\r
416                 * If the button is unselected, use the JIConToggleButton component's unselectedStateIcon, unless\r
417                 * it is null, in which case use the default L&F state icon (see above). Further, if the button is\r
418                 * also disabled, use the component's disabledUnselectedStateIcon unless it is null.\r
419                 *\r
420                 * @param b The button whose indicator icon is required\r
421                 * @returns The icon valid for the button's current state, or null if no icon is to be displayed\r
422                 */\r
423         public Icon getStateIcon(JIconToggleButton b) {\r
424                 Icon temp = null, state = null;\r
425                 if(b.isSelected()) { // button is selected\r
426                         state = b.getSelectedStateIcon(); // User-provided icon\r
427                         if(state == null) state = stateIcon; // otherwise the L&F default (which draws itself based on button state)\r
428                         \r
429                         if(!b.isEnabled()) // disabled button\r
430                                 if((temp = b.getDisabledSelectedStateIcon()) != null) // might be a user-provided icon\r
431                                         state = temp; // if so, use it  \r
432                 }\r
433                 else { // unselected\r
434                         state = ((JIconToggleButton)b).getUnselectedStateIcon(); // might be a user-provided icon\r
435                         if(state == null) state = stateIcon; // otherwise use the L&F default\r
436                         \r
437                         if(!b.isEnabled()) // disabled + unselected\r
438                                 if((temp = b.getDisabledUnselectedStateIcon()) != null) // might be a user-provided Icon\r
439                                         state = temp; // if so, use it          \r
440         }\r
441                 return state;\r
442         }\r
443         \r
444  /** \r
445   * Compute and return the location of the icon origins, the \r
446   * location of origin of the text baseline, and a possibly clipped\r
447   * version of the compound labels string.  Locations are computed\r
448   * relative to the vRect rectangle. \r
449                 *\r
450                 * @param icontogglebutton The Button\r
451                 * @param fm The Text's font measurements (metrics)\r
452                 * @param stateIcon The State indicator Icon to use, or null\r
453                 * @param userIcon The User's functionality indicator Icon, or null\r
454                 * @param text The button's label text\r
455                 * @param verticalAlignment Vertical position of the icons and text (see SwingUtilities.layoutCompoundLabel)\r
456                 * @param horizontalAlignment Horizontal position of User Icon and Text label\r
457                 * @param vRect Bounds of area within which all icons and text label must be drawn\r
458                 * @param siRect Bounds within which State indicator Icon must be drawn\r
459                 * @param uiRect Bounds within which User functionality Icon must be drawn\r
460                 * @param tRect Bounds within which Text label must be drawn\r
461                 * @param lRect Bounds of User Icon and Text label (used for determining focus painting)\r
462                 * @param textIconGap Pixels between User Icon and Text label\r
463                 * @param iconButtonGap Pixels on either side of State indicator Icon\r
464                 *\r
465                 * @returns Button's label Text\r
466                 *\r
467                 * @see SwingUtilities#layoutCompoundLabel\r
468                 * @see ComponentOrientation\r
469   */\r
470  private synchronized String layoutIconToggleButton(\r
471          JIconToggleButton icontogglebutton,\r
472   FontMetrics fm,\r
473   Icon stateIcon,\r
474   Icon userIcon,\r
475   String text,\r
476   int verticalAlignment,\r
477   int horizontalAlignment,\r
478   int verticalTextPosition,\r
479   int horizontalTextPosition,\r
480   Rectangle vRect, \r
481   Rectangle siRect, \r
482   Rectangle uiRect,\r
483   Rectangle tRect,\r
484                 Rectangle lRect,\r
485   int textIconGap,\r
486                 int iconButtonGap\r
487  ) {\r
488                 \r
489                 Rectangle tempRect = new Rectangle(vRect); // calculate only the available width for User Icon + Text\r
490 \r
491   if (stateIcon != null) { // Initialize the stateIcon bounds rectangle's width & height\r
492                 siRect.width = stateIcon.getIconWidth();\r
493                 siRect.height = stateIcon.getIconHeight();\r
494 \r
495                         // this should help layoutCompoundLabel() decide to use ellipsis (...) in the text when it's too long to fit\r
496                         tempRect.width -= siRect.width + iconButtonGap; // deduct width of state Icon plus 2 lots of gap\r
497          }\r
498                 else\r
499                  siRect.width = siRect.height = 0;\r
500             \r
501                 // layout the User Icon and Text\r
502                 text = SwingUtilities.layoutCompoundLabel((JComponent)icontogglebutton, fm, text, userIcon, verticalAlignment, horizontalAlignment,\r
503                  verticalTextPosition, horizontalTextPosition, tempRect, uiRect, tRect, textIconGap);\r
504                                 \r
505  // now add back the deducted wdith as x-axis offsets to prevent state Icon being overwritten by User Icon and Text!\r
506   if(icontogglebutton.getComponentOrientation().isLeftToRight()) {\r
507    siRect.x = vRect.x; // position the State Icon\r
508                         tRect.x += iconButtonGap + siRect.width; // shift the Text..\r
509    uiRect.x += iconButtonGap + siRect.width; // ...and the User Icon along to compensate\r
510   }\r
511   else {\r
512    siRect.x = vRect.x + vRect.width - siRect.width; // position the State Icon\r
513    tRect.x -= iconButtonGap + siRect.width; // shift the Text...\r
514    uiRect.x -= iconButtonGap + siRect.width; // ...and the User Icon along to compensate\r
515   }\r
516 \r
517                 lRect.setBounds(uiRect); // combine User Icon & Text rectangles\r
518                 lRect = SwingUtilities.computeUnion(tRect.x, tRect.y, tRect.width, tRect.height, lRect);\r
519 \r
520   // Align the check and user icons vertically with the center of the label rect\r
521   uiRect.y = lRect.y + (lRect.height/2) - (uiRect.height/2);\r
522   siRect.y = lRect.y + (lRect.height/2) - (siRect.height/2);\r
523 \r
524                 // graphics debugging info\r
525                 if((icontogglebutton.getDebugGraphicsOptions() & DebugGraphics.LOG_OPTION) == DebugGraphics.LOG_OPTION) {\r
526                         System.out.println(this.getClass().getName() + ".layoutIconToggleButton(...)");\r
527                         System.out.println("viewRect=" + viewRect);\r
528                         System.out.println("stateRect=" + stateIconRect);\r
529                         System.out.println("iconRect=" + userIconRect);\r
530                         System.out.println("textRect=" + textRect);\r
531                         System.out.println("labelRect=" + labelRect);\r
532                 }\r
533   \r
534                 return text;\r
535  }\r
536 \r
537         /** Calculate the smallest size that includes all icons\r
538                 * @param c The button\r
539                 * @returns minimum dimensions required by the Button (usually the same as getPreferredSize())\r
540                 * @see #getPreferredSize\r
541                 * @see #getMaximumSize\r
542                 */\r
543         public synchronized Dimension getMinimumSize(JComponent c) {\r
544                 // return getPreferredSize(c);\r
545                 return new Dimension(stateIconRect.width + userIconRect.width + c.getInsets().left + c.getInsets().right + 3*((JIconToggleButton)c).getIconTextGap(), userIconRect.height + c.getInsets().top + c.getInsets().bottom );\r
546  }\r
547 \r
548         /** Calculate the maximum size that includes all icons and text unclipped\r
549                 * @param c The Button\r
550                 * @returns maximum dimensions tolerated by the Button (usually the same as getPreferredSize())\r
551                 * @see #getPreferredSize\r
552                 * @see #getMinimumSize\r
553                 */\r
554         public Dimension getMaximumSize(JComponent c) {\r
555                 return getPreferredSize(c);\r
556         }\r
557 \r
558         /** Calculate the optimum size that includes all icons and text unclipped\r
559                 * @param c The button\r
560                 * @returns Preferred size of Button\r
561                 * @see #getMinimumSize\r
562                 * @see #getMaximumSize\r
563                 */\r
564         public synchronized Dimension getPreferredSize(JComponent c) {\r
565   JIconToggleButton b = (JIconToggleButton) c; // so we can access it's methods & attributes\r
566 \r
567   Font font = b.getFont();\r
568   FontMetrics fm = b.getToolkit().getFontMetrics(font); // deprecated but required in this instance\r
569                 \r
570                 Icon sIcon = b.getStateIcon(); // the user might be providing a custom state indicator\r
571                 sIcon = sIcon != null ? sIcon : getStateIcon(b); // otherwise use this UI delegate default\r
572                 \r
573   resetRectangles();\r
574 \r
575   // layout the text and icons\r
576   String text = layoutIconToggleButton(\r
577    b, fm, sIcon, b.getIcon(), b.getText(), \r
578    b.getVerticalAlignment(), b.getHorizontalAlignment(),\r
579    b.getVerticalTextPosition(), b.getHorizontalTextPosition(),\r
580    viewRect, stateIconRect, userIconRect, textRect, labelRect,\r
581           b.getText() == null ? 0 : b.getIconTextGap(),\r
582                         b.getIconTextGap());\r
583 \r
584   // The preferred size of the button is the size of the text and icons plus the button's insets.\r
585                 Rectangle pref = new Rectangle(labelRect); \r
586                 pref.width += stateIconRect.width += 2*b.getIconTextGap();\r
587 \r
588                 Insets insets = b.getInsets(); // either from the border, or from a super\r
589   pref.width += insets.left + insets.right; // include the border\r
590   pref.height += insets.top + insets.bottom;\r
591                 \r
592                 insets = b.getMargin(); // between border and label\r
593   pref.width += insets.left + insets.right; // include the margin\r
594   pref.height += insets.top + insets.bottom;\r
595 \r
596                 if(pref.width%2 == 0) // adjust to ensure the focus dash line is drawn correctly\r
597                          pref.width++;\r
598                 if(pref.height%2 == 0)\r
599                          pref.height++;\r
600 \r
601                 // graphics debugging info\r
602                 if((c.getDebugGraphicsOptions() & DebugGraphics.LOG_OPTION) == DebugGraphics.LOG_OPTION) {\r
603                         System.out.println(this.getClass().getName() + ".getPreferredSize(...)");\r
604                         System.out.println("preferred=" + pref);\r
605                 }\r
606   return pref.getSize();\r
607         }\r
608 }\r