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.beans.*;
  30 import java.lang.reflect.Method;
  31 
  32 import javax.swing.*;
  33 import javax.swing.border.Border;
  34 import javax.swing.plaf.*;
  35 
  36 import apple.laf.*;
  37 import apple.laf.JRSUIConstants.*;
  38 
  39 import com.apple.laf.AquaUtils.RecyclableSingleton;
  40 import com.apple.laf.AquaUtils.RecyclableSingletonFromDefaultConstructor;
  41 
  42 public class AquaUtilControlSize {
  43     protected final static String CLIENT_PROPERTY_KEY = "JComponent.sizeVariant";
  44     protected final static String SYSTEM_PROPERTY_KEY = "swing.component.sizevariant";
  45 
  46     interface Sizeable {
  47         void applySizeFor(final JComponent c, final Size size);
  48     }
  49 
  50     protected static final RecyclableSingleton<PropertySizeListener> sizeListener = new RecyclableSingletonFromDefaultConstructor<PropertySizeListener>(PropertySizeListener.class);
  51     protected static PropertySizeListener getSizeListener() {
  52         return sizeListener.get();
  53     }
  54 
  55     protected static void addSizePropertyListener(final JComponent c) {
  56         c.addPropertyChangeListener(CLIENT_PROPERTY_KEY, getSizeListener());
  57         PropertySizeListener.applyComponentSize(c, c.getClientProperty(CLIENT_PROPERTY_KEY));
  58     }
  59 
  60     protected static void removeSizePropertyListener(final JComponent c) {
  61         c.removePropertyChangeListener(CLIENT_PROPERTY_KEY, getSizeListener());
  62     }
  63 
  64     private static JRSUIConstants.Size getSizeFromString(final String name) {
  65         if ("regular".equalsIgnoreCase(name)) return Size.REGULAR;
  66         if ("small".equalsIgnoreCase(name)) return Size.SMALL;
  67         if ("mini".equalsIgnoreCase(name)) return Size.MINI;
  68         if ("large".equalsIgnoreCase(name)) return Size.LARGE;
  69         return null;
  70     }
  71 
  72     private static Size getDefaultSize() {
  73         final String sizeProperty = java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction(SYSTEM_PROPERTY_KEY));
  74         final JRSUIConstants.Size size = getSizeFromString(sizeProperty);
  75         if (size != null) return size;
  76         return JRSUIConstants.Size.REGULAR;
  77     }
  78 
  79     protected final static JRSUIConstants.Size defaultSize = getDefaultSize();
  80     protected static JRSUIConstants.Size getUserSizeFrom(final JComponent c) {
  81         final Object sizeProp = c.getClientProperty(CLIENT_PROPERTY_KEY);
  82         if (sizeProp == null) return defaultSize;
  83         final Size size = getSizeFromString(sizeProp.toString());
  84         if (size == null) return Size.REGULAR;
  85         return size;
  86     }
  87 
  88     protected static JRSUIConstants.Size applySizeForControl(final JComponent c, final AquaPainter<? extends JRSUIState> painter) {
  89         final JRSUIConstants.Size sizeFromUser = getUserSizeFrom(c);
  90         final JRSUIConstants.Size size = sizeFromUser == null ? JRSUIConstants.Size.REGULAR : sizeFromUser;
  91         painter.state.set(size);
  92         return size;
  93     }
  94 
  95     protected static Font getFontForSize(final Component c, final JRSUIConstants.Size size) {
  96         final Font initialFont = c.getFont();
  97 
  98         if (size == null || !(initialFont instanceof UIResource)) return initialFont;
  99 
 100         if (size == JRSUIConstants.Size.MINI) return initialFont.deriveFont(AquaFonts.getMiniControlTextFont().getSize2D());
 101         if (size == JRSUIConstants.Size.SMALL) return initialFont.deriveFont(AquaFonts.getSmallControlTextFont().getSize2D());
 102 
 103         return initialFont.deriveFont(AquaFonts.getControlTextFont().getSize2D());
 104     }
 105 
 106     private static void applyBorderForSize(final JComponent c, final Size size) {
 107         final Border border = c.getBorder();
 108         if (!(border instanceof AquaBorder)) return;
 109         final AquaBorder aquaBorder = (AquaBorder)border;
 110 
 111         if (aquaBorder.sizeVariant.size == size) return;
 112         final AquaBorder derivedBorder = aquaBorder.deriveBorderForSize(size);
 113         if (derivedBorder == null) return;
 114 
 115         c.setBorder(derivedBorder);
 116     }
 117 
 118     // call JComponent.getUI() if it exists, then call Sizeable.applySizeFor() if the UI is "Sizeable"
 119     // next best thing to -respondsToSelector: :-P
 120     private static void applyUISizing(final JComponent c, final Size size) {
 121         try {
 122             // see if this component has a "getUI" method
 123             final Class<? extends JComponent> clazz = c.getClass();
 124             final Method getUIMethod = clazz.getMethod("getUI", new Class<?>[0]);
 125 
 126             // see if that UI is one of ours that understands sizing
 127             final Object ui = getUIMethod.invoke(c, new Object[0]);
 128             if (!(ui instanceof Sizeable)) return;
 129 
 130             // size it!
 131             final Sizeable sizeable = (Sizeable)ui;
 132             sizeable.applySizeFor(c, size);
 133         } catch (final Throwable e) { return; }
 134     }
 135 
 136     protected static class PropertySizeListener implements PropertyChangeListener {
 137         public void propertyChange(final PropertyChangeEvent evt) {
 138             final String key = evt.getPropertyName();
 139             if (!CLIENT_PROPERTY_KEY.equalsIgnoreCase(key)) return;
 140 
 141             final Object source = evt.getSource();
 142             if (!(source instanceof JComponent)) return;
 143 
 144             final JComponent c = (JComponent)source;
 145             applyComponentSize(c, evt.getNewValue());
 146         }
 147 
 148         protected static void applyComponentSize(final JComponent c, final Object value) {
 149             Size size = getSizeFromString(value == null ? null : value.toString());
 150             if (size == null) {
 151                 size = getUserSizeFrom(c);
 152                 if (size == Size.REGULAR) return;
 153             }
 154 
 155             applyBorderForSize(c, size);
 156 
 157             applyUISizing(c, size);
 158 
 159             final Font priorFont = c.getFont();
 160             if (!(priorFont instanceof FontUIResource)) return;
 161             c.setFont(getFontForSize(c, size));
 162         }
 163     }
 164 
 165     public static class SizeDescriptor {
 166         SizeVariant regular;
 167         SizeVariant small;
 168         SizeVariant mini;
 169 
 170         public SizeDescriptor(final SizeVariant variant) {
 171             regular = deriveRegular(variant);
 172             small = deriveSmall(new SizeVariant(regular));
 173             mini = deriveMini(new SizeVariant(small));
 174         }
 175 
 176         public SizeVariant deriveRegular(final SizeVariant v) {
 177             v.size = Size.REGULAR;
 178             return v;
 179         }
 180 
 181         public SizeVariant deriveSmall(final SizeVariant v) {
 182             v.size = Size.SMALL;
 183             return v;
 184         }
 185 
 186         public SizeVariant deriveMini(final SizeVariant v) {
 187             v.size = Size.MINI;
 188             return v;
 189         }
 190 
 191         public SizeVariant get(final JComponent c) {
 192             if (c == null) return regular;
 193             return get(getUserSizeFrom(c));
 194         }
 195 
 196         public SizeVariant get(final Size size) {
 197             if (size == Size.REGULAR) return regular;
 198             if (size == Size.SMALL) return small;
 199             if (size == Size.MINI) return mini;
 200             return regular;
 201         }
 202 
 203         public String toString() {
 204             return "regular[" + regular + "] small[" + small + "] mini[" + mini + "]";
 205         }
 206     }
 207 
 208     public static class SizeVariant {
 209         Size size = Size.REGULAR;
 210         Insets insets = new InsetsUIResource(0, 0, 0, 0);
 211         Insets margins = new InsetsUIResource(0, 0, 0, 0);
 212         Float fontSize;
 213         int w = 0;
 214         int h = 0;
 215     //    Integer textBaseline;
 216 
 217         public SizeVariant() { }
 218 
 219         public SizeVariant(final int minWidth, final int minHeight) {
 220             this.w = minWidth;
 221             this.h = minHeight;
 222         }
 223 
 224         public SizeVariant(final SizeVariant desc){
 225             this.size = desc.size;
 226             this.insets = new InsetsUIResource(desc.insets.top, desc.insets.left, desc.insets.bottom, desc.insets.right);
 227             this.margins = new InsetsUIResource(desc.margins.top, desc.margins.left, desc.margins.bottom, desc.margins.right);
 228             this.fontSize = desc.fontSize;
 229             this.w = desc.w;
 230             this.h = desc.h;
 231     //        this.textBaseline = desc.textBaseline;
 232         }
 233 
 234         public SizeVariant replaceInsets(final String insetName) {
 235             this.insets = UIManager.getInsets(insetName);
 236             return this;
 237         }
 238 
 239         public SizeVariant replaceInsets(final Insets i) {
 240             this.insets = new InsetsUIResource(i.top, i.left, i.bottom, i.right);
 241             return this;
 242         }
 243 
 244         public SizeVariant alterInsets(final int top, final int left, final int bottom, final int right) {
 245             insets = generateInsets(insets, top, left, bottom, right);
 246             return this;
 247         }
 248 
 249         public SizeVariant replaceMargins(final String marginName) {
 250             this.margins = UIManager.getInsets(marginName);
 251             return this;
 252         }
 253 
 254         public SizeVariant alterMargins(final int top, final int left, final int bottom, final int right) {
 255             margins = generateInsets(margins, top, left, bottom, right);
 256             return this;
 257         }
 258 
 259         public SizeVariant alterFontSize(final float newSize) {
 260             final float oldSize = fontSize == null ? 0.0f : fontSize.floatValue();
 261             fontSize = new Float(newSize + oldSize);
 262             return this;
 263         }
 264 
 265         public SizeVariant alterMinSize(final int width, final int height) {
 266             this.w += width; this.h += height;
 267             return this;
 268         }
 269 
 270 //        public SizeVariant alterTextBaseline(final int baseline) {
 271 //            final int oldSize = textBaseline == null ? 0 : textBaseline.intValue();
 272 //            textBaseline = new Integer(baseline + oldSize);
 273 //            return this;
 274 //        }
 275 
 276         static Insets generateInsets(final Insets i, final int top, final int left, final int bottom, final int right) {
 277             if (i == null) return new InsetsUIResource(top, left, bottom, right);
 278             i.top += top;
 279             i.left += left;
 280             i.bottom += bottom;
 281             i.right += right;
 282             return i;
 283         }
 284 
 285         public String toString() {
 286             return "insets:" + insets + ", margins:" + margins + ", fontSize:" + fontSize;// + ", textBaseline:" + textBaseline;
 287         }
 288     }
 289 }