2 * BasicIconToggleButtonUI.java
\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
7 * $History: BasicIconToggleButtonUI.java $
\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
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
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
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
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
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
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
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
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
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
64 * ***************** Version 6 *****************
\r
65 * User: Tj Date: 31/10/01 Time: 5:04
\r
66 * Updated in $/VisTA/CID/Components
\r
69 package org.tjworld.components;
\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
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
103 * @see JToggleButton
\r
105 * @see BasicMenuItemUI
\r
106 * @see BasicToggleButtonUI
\r
107 * @see BasicIconButtonListener
\r
111 * version 1.4.1 31 Oct, 2001 Revised to support JIconToggleButton's revised state icons
\r
115 public class BasicIconToggleButtonUI extends BasicToggleButtonUI {
\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
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
123 protected Icon stateIcon = null;
\r
125 /** One global instance of the UI Delegate */
\r
126 private final static BasicIconToggleButtonUI iconToggleButtonUI = new BasicIconToggleButtonUI();
\r
128 /** Masquarades as a ToggleButton so it can fit in with standard Look & Feels */
\r
129 private final static String propertyPrefix = "ToggleButton" + ".";
\r
131 /** Creates new BasicIconToggleButtonUI */
\r
132 public BasicIconToggleButtonUI() {
\r
135 /** Creates a new BasicIconToggleButton
\r
136 * @returns The single static instance of a BasicIconToggleButton
\r
138 public static ComponentUI createUI(JComponent b) {
\r
139 return iconToggleButtonUI;
\r
142 /** Creates a new ButtonListener
\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
147 * Usually called by BasicButtonUI.installListeners(..)
\r
148 * @returns a new listener
\r
150 protected BasicButtonListener createButtonListener(AbstractButton b) {
\r
151 return (BasicButtonListener) new BasicIconButtonListener(b);
\r
154 /** Uses the ToggleButton properties */
\r
155 protected String getPropertyPrefix() {
\r
156 return propertyPrefix;
\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
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
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
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
184 * @returns A JComponent subclass being the analog of the called class
\r
186 public JComponent createCompatibleLandFComponent(JIconToggleButton iconTB) {
\r
187 JToggleButton tb = new JToggleButton(); // create the analog
\r
189 ButtonModel my = iconTB.getModel();
\r
190 ButtonModel tbBM = tb.getModel(); // synchronize it's model
\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
197 tb.setBorder(iconTB.getBorder());
\r
198 tb.setMargin(iconTB.getMargin());
\r
199 tb.setSize(iconTB.getSize());
\r
201 return tb; // analog of JIconToggleButton
\r
205 * Paint the state of the Button, including the optional User Icon functionality indicator.
\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
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
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
221 viewRect.setBounds(0, 0, size.width, size.height);
\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
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
236 Font f = c.getFont();
\r
238 FontMetrics fm = g.getFontMetrics();
\r
240 Icon sIcon = getStateIcon(b); // the user might be providing a custom state indicator
\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
251 g.setColor(b.getBackground());
\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
257 if(lfButtonUI == null)
\r
258 lfButtonUI = new javax.swing.plaf.basic.BasicButtonUI();
\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
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
268 lfButtonUI.paint(g, jc);
\r
270 // paintButtonPressed(g, b); // as at 1.4, BasicButtonUI.paintButtonPressed() returns having done nothing
\r
272 else if(b.isOpaque()) { // fill in the background
\r
273 Insets insets = b.getInsets();
\r
274 Insets margin = b.getMargin();
\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
281 // Paint the state Icon
\r
283 paintStateIcon(g, b, sIcon, stateIconRect);
\r
285 // Paint the User Icon
\r
286 if(b.getIcon() != null)
\r
287 paintUserIcon(g, b, b.getIcon(), userIconRect);
\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
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
304 * Paint the User Icon
\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
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
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
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
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
334 icon = b.getSelectedIcon();
\r
337 else if(b.isRolloverEnabled() && model.isRollover()) // enabled + mouse is over
\r
338 icon = b.getRolloverIcon();
\r
341 icon = b.getIcon();
\r
343 if(icon == null) // if it failed to be set any other way!!!
\r
344 icon = b.getIcon(); // try once more
\r
346 if(icon != null) // only paint if an icon is available
\r
347 icon.paintIcon(b, g, iconRect.x, iconRect.y);
\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
355 * Paint the State (selected) Icon
\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
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
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
370 if(b != null) // only paint it if a component is available
\r
371 state.paintIcon(b, g, iconRect.x, iconRect.y);
\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
378 /** Draw the focus outline
\r
380 * Synchronized to prevent multipe components accessing the shared rectangles
\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
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
394 /** Get the colour used to paint focus
\r
395 * @param c The Component
\r
396 * @returns The focus colour
\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
407 /** Convenience method to get the currently active state-indicator icon
\r
409 * The algorithm for choosing the icon is:
\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
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
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
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
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
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
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
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
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
465 * @returns Button's label Text
\r
467 * @see SwingUtilities#layoutCompoundLabel
\r
468 * @see ComponentOrientation
\r
470 private synchronized String layoutIconToggleButton(
\r
471 JIconToggleButton icontogglebutton,
\r
476 int verticalAlignment,
\r
477 int horizontalAlignment,
\r
478 int verticalTextPosition,
\r
479 int horizontalTextPosition,
\r
489 Rectangle tempRect = new Rectangle(vRect); // calculate only the available width for User Icon + Text
\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
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
499 siRect.width = siRect.height = 0;
\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
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
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
517 lRect.setBounds(uiRect); // combine User Icon & Text rectangles
\r
518 lRect = SwingUtilities.computeUnion(tRect.x, tRect.y, tRect.width, tRect.height, lRect);
\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
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
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
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
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
554 public Dimension getMaximumSize(JComponent c) {
\r
555 return getPreferredSize(c);
\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
564 public synchronized Dimension getPreferredSize(JComponent c) {
\r
565 JIconToggleButton b = (JIconToggleButton) c; // so we can access it's methods & attributes
\r
567 Font font = b.getFont();
\r
568 FontMetrics fm = b.getToolkit().getFontMetrics(font); // deprecated but required in this instance
\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
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
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
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
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
596 if(pref.width%2 == 0) // adjust to ensure the focus dash line is drawn correctly
\r
598 if(pref.height%2 == 0)
\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
606 return pref.getSize();
\r