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 private static final RecyclableSizingIcon regularIcon = new RecyclableSizingIcon(18); 46 private static final RecyclableSizingIcon smallIcon = new RecyclableSizingIcon(16); 47 private static final RecyclableSizingIcon miniIcon = new RecyclableSizingIcon(14); 48 49 protected static class RecyclableSizingIcon extends RecyclableSingleton<Icon> { 50 final int iconSize; RecyclableSizingIcon(final int iconSize)51 public RecyclableSizingIcon(final int iconSize) { this.iconSize = iconSize; } 52 getInstance()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 AquaButtonLabeledUI()60 public AquaButtonLabeledUI() { 61 widgetBorder = getPainter(); 62 } 63 applySizeFor(final JComponent c, final Size newSize)64 public void applySizeFor(final JComponent c, final Size newSize) { 65 super.applySizeFor(c, newSize); 66 widgetBorder = (AquaButtonBorder)widgetBorder.deriveBorderForSize(newSize); 67 } 68 getDefaultIcon(final JComponent c)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 setThemeBorder(final AbstractButton b)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 getPainter()87 protected abstract AquaButtonBorder getPainter(); 88 paint(final Graphics g, final JComponent c)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 */ getPreferredSize(final JComponent c)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 abstract static class LabeledButtonBorder extends AquaButtonBorder { LabeledButtonBorder(final SizeDescriptor sizeDescriptor)225 public LabeledButtonBorder(final SizeDescriptor sizeDescriptor) { 226 super(sizeDescriptor); 227 } 228 LabeledButtonBorder(final LabeledButtonBorder other)229 public LabeledButtonBorder(final LabeledButtonBorder other) { 230 super(other); 231 } 232 233 @Override createPainter()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 doButtonPaint(final AbstractButton b, final ButtonModel model, final Graphics g, final int x, final int y, final int width, final int height)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 getButtonState(final AbstractButton b, final ButtonModel model)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 isIndeterminate(final AbstractButton b)258 static boolean isIndeterminate(final AbstractButton b) { 259 return "indeterminate".equals(b.getClientProperty("JButton.selectedState")); 260 } 261 } 262 } 263