1 /* 2 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.apple.laf; 27 28 import java.awt.*; 29 import java.awt.image.BufferedImage; 30 31 import javax.swing.*; 32 import javax.swing.border.Border; 33 import javax.swing.plaf.UIResource; 34 import javax.swing.plaf.basic.BasicHTML; 35 import javax.swing.text.View; 36 37 import apple.laf.*; 38 import apple.laf.JRSUIConstants.*; 39 import apple.laf.JRSUIState.ValueState; 40 41 import com.apple.laf.AquaUtilControlSize.*; 42 import com.apple.laf.AquaUtils.RecyclableSingleton; 43 44 public abstract class AquaButtonLabeledUI extends AquaButtonToggleUI implements Sizeable { 45 protected static RecyclableSizingIcon regularIcon = new RecyclableSizingIcon(18); 46 protected static RecyclableSizingIcon smallIcon = new RecyclableSizingIcon(16); 47 protected static RecyclableSizingIcon miniIcon = new RecyclableSizingIcon(14); 48 49 protected static class RecyclableSizingIcon extends RecyclableSingleton<Icon> { 50 final int iconSize; 51 public RecyclableSizingIcon(final int iconSize) { this.iconSize = iconSize; } 52 53 protected Icon getInstance() { 54 return new ImageIcon(new BufferedImage(iconSize, iconSize, BufferedImage.TYPE_INT_ARGB_PRE)); 55 } 56 } 57 58 protected AquaButtonBorder widgetBorder; 59 60 public AquaButtonLabeledUI() { 61 widgetBorder = getPainter(); 62 } 63 64 public void applySizeFor(final JComponent c, final Size newSize) { 65 super.applySizeFor(c, newSize); 66 widgetBorder = (AquaButtonBorder)widgetBorder.deriveBorderForSize(newSize); 67 } 68 69 public Icon getDefaultIcon(final JComponent c) { 70 final Size componentSize = AquaUtilControlSize.getUserSizeFrom(c); 71 if (componentSize == Size.REGULAR) return regularIcon.get(); 72 if (componentSize == Size.SMALL) return smallIcon.get(); 73 if (componentSize == Size.MINI) return miniIcon.get(); 74 return regularIcon.get(); 75 } 76 77 protected void setThemeBorder(final AbstractButton b) { 78 super.setThemeBorder(b); 79 80 Border border = b.getBorder(); 81 if (border == null || border instanceof UIResource) { 82 // Set the correct border 83 b.setBorder(AquaButtonBorder.getBevelButtonBorder()); 84 } 85 } 86 87 protected abstract AquaButtonBorder getPainter(); 88 89 public synchronized void paint(final Graphics g, final JComponent c) { 90 final AbstractButton b = (AbstractButton)c; 91 final ButtonModel model = b.getModel(); 92 93 final Font f = c.getFont(); 94 g.setFont(f); 95 final FontMetrics fm = g.getFontMetrics(); 96 97 Dimension size = b.getSize(); 98 99 final Insets i = c.getInsets(); 100 101 Rectangle viewRect = new Rectangle(b.getWidth(), b.getHeight()); 102 Rectangle iconRect = new Rectangle(); 103 Rectangle textRect = new Rectangle(); 104 105 Icon altIcon = b.getIcon(); 106 107 final boolean isCellEditor = c.getParent() instanceof CellRendererPane; 108 109 // This was erroneously removed to fix [3155996] but really we wanted the controls to just be 110 // opaque. So we put this back in to fix [3179839] (radio buttons not being translucent) 111 if (b.isOpaque() || isCellEditor) { 112 g.setColor(b.getBackground()); 113 g.fillRect(0, 0, size.width, size.height); 114 } 115 116 // only do this if borders are on! 117 if (((AbstractButton)c).isBorderPainted() && !isCellEditor) { 118 final Border border = c.getBorder(); 119 if (border instanceof AquaButtonBorder) { 120 ((AquaButtonBorder)border).paintButton(c, g, viewRect.x, viewRect.y, viewRect.width, viewRect.height); 121 } 122 } 123 124 viewRect.x = i.left; 125 viewRect.y = i.top; 126 viewRect.width = b.getWidth() - (i.right + viewRect.x); 127 viewRect.height = b.getHeight() - (i.bottom + viewRect.y); 128 129 // normal size ?? 130 // at some point we substitute the small icon instead of the normal icon 131 // we should base this on height. Use normal unless we are under a certain size 132 // see our button code! 133 134 final String text = SwingUtilities.layoutCompoundLabel(c, fm, b.getText(), altIcon != null ? altIcon : getDefaultIcon(b), b.getVerticalAlignment(), b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), viewRect, iconRect, textRect, b.getText() == null ? 0 : b.getIconTextGap()); 135 136 // fill background 137 138 // draw the native radio button stuff here. 139 if (altIcon == null) { 140 widgetBorder.paintButton(c, g, iconRect.x, iconRect.y, iconRect.width, iconRect.height); 141 } else { 142 // Paint the button 143 if (!model.isEnabled()) { 144 if (model.isSelected()) { 145 altIcon = b.getDisabledSelectedIcon(); 146 } else { 147 altIcon = b.getDisabledIcon(); 148 } 149 } else if (model.isPressed() && model.isArmed()) { 150 altIcon = b.getPressedIcon(); 151 if (altIcon == null) { 152 // Use selected icon 153 altIcon = b.getSelectedIcon(); 154 } 155 } else if (model.isSelected()) { 156 if (b.isRolloverEnabled() && model.isRollover()) { 157 altIcon = b.getRolloverSelectedIcon(); 158 if (altIcon == null) { 159 altIcon = b.getSelectedIcon(); 160 } 161 } else { 162 altIcon = b.getSelectedIcon(); 163 } 164 } else if (b.isRolloverEnabled() && model.isRollover()) { 165 altIcon = b.getRolloverIcon(); 166 } 167 168 if (altIcon == null) { 169 altIcon = b.getIcon(); 170 } 171 172 altIcon.paintIcon(c, g, iconRect.x, iconRect.y); 173 } 174 175 // Draw the Text 176 if (text != null) { 177 final View v = (View)c.getClientProperty(BasicHTML.propertyKey); 178 if (v != null) { 179 v.paint(g, textRect); 180 } else { 181 paintText(g, b, textRect, text); 182 } 183 } 184 } 185 186 /** 187 * The preferred size of the button 188 */ 189 public Dimension getPreferredSize(final JComponent c) { 190 if (c.getComponentCount() > 0) { return null; } 191 192 final AbstractButton b = (AbstractButton)c; 193 194 final String text = b.getText(); 195 196 Icon buttonIcon = b.getIcon(); 197 if (buttonIcon == null) { 198 buttonIcon = getDefaultIcon(b); 199 } 200 201 final Font font = b.getFont(); 202 final FontMetrics fm = b.getFontMetrics(font); 203 204 Rectangle prefViewRect = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE); 205 Rectangle prefIconRect = new Rectangle(); 206 Rectangle prefTextRect = new Rectangle(); 207 208 SwingUtilities.layoutCompoundLabel(c, fm, text, buttonIcon, b.getVerticalAlignment(), b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), prefViewRect, prefIconRect, prefTextRect, text == null ? 0 : b.getIconTextGap()); 209 210 // find the union of the icon and text rects (from Rectangle.java) 211 final int x1 = Math.min(prefIconRect.x, prefTextRect.x); 212 final int x2 = Math.max(prefIconRect.x + prefIconRect.width, prefTextRect.x + prefTextRect.width); 213 final int y1 = Math.min(prefIconRect.y, prefTextRect.y); 214 final int y2 = Math.max(prefIconRect.y + prefIconRect.height, prefTextRect.y + prefTextRect.height); 215 int width = x2 - x1; 216 int height = y2 - y1; 217 218 Insets prefInsets = b.getInsets(); 219 width += prefInsets.left + prefInsets.right; 220 height += prefInsets.top + prefInsets.bottom; 221 return new Dimension(width, height); 222 } 223 224 public static abstract class LabeledButtonBorder extends AquaButtonBorder { 225 public LabeledButtonBorder(final SizeDescriptor sizeDescriptor) { 226 super(sizeDescriptor); 227 } 228 229 public LabeledButtonBorder(final LabeledButtonBorder other) { 230 super(other); 231 } 232 233 @Override 234 protected AquaPainter<? extends JRSUIState> createPainter() { 235 final AquaPainter<ValueState> painter = AquaPainter.create(JRSUIStateFactory.getLabeledButton()); 236 painter.state.set(AlignmentVertical.CENTER); 237 painter.state.set(AlignmentHorizontal.CENTER); 238 return painter; 239 } 240 241 protected void doButtonPaint(final AbstractButton b, final ButtonModel model, final Graphics g, final int x, final int y, final int width, final int height) { 242 painter.state.set(AquaUtilControlSize.getUserSizeFrom(b)); 243 ((ValueState)painter.state).setValue(model.isSelected() ? isIndeterminate(b) ? 2 : 1 : 0); // 2=mixed, 1=on, 0=off 244 super.doButtonPaint(b, model, g, x, y, width, height); 245 } 246 247 protected State getButtonState(final AbstractButton b, final ButtonModel model) { 248 final State state = super.getButtonState(b, model); 249 250 if (state == State.INACTIVE) return State.INACTIVE; 251 if (state == State.DISABLED) return State.DISABLED; 252 if (model.isArmed() && model.isPressed()) return State.PRESSED; 253 if (model.isSelected()) return State.ACTIVE; 254 255 return state; 256 } 257 258 static boolean isIndeterminate(final AbstractButton b) { 259 return "indeterminate".equals(b.getClientProperty("JButton.selectedState")); 260 } 261 } 262 }