1 /*
   2  * Copyright (c) 1997, 2006, 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.sun.java.swing.plaf.windows;
  27 
  28 import javax.swing.plaf.basic.*;
  29 import javax.swing.border.*;
  30 import javax.swing.plaf.*;
  31 import javax.swing.*;
  32 
  33 import java.awt.*;
  34 
  35 import static com.sun.java.swing.plaf.windows.TMSchema.*;
  36 import static com.sun.java.swing.plaf.windows.TMSchema.Part.*;
  37 import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
  38 import sun.awt.AppContext;
  39 
  40 
  41 /**
  42  * Windows button.
  43  * <p>
  44  * <strong>Warning:</strong>
  45  * Serialized objects of this class will not be compatible with
  46  * future Swing releases.  The current serialization support is appropriate
  47  * for short term storage or RMI between applications running the same
  48  * version of Swing.  A future release of Swing will provide support for
  49  * long term persistence.
  50  *
  51  * @author Jeff Dinkins
  52  *
  53  */
  54 public class WindowsButtonUI extends BasicButtonUI
  55 {
  56     protected int dashedRectGapX;
  57     protected int dashedRectGapY;
  58     protected int dashedRectGapWidth;
  59     protected int dashedRectGapHeight;
  60 
  61     protected Color focusColor;
  62 
  63     private boolean defaults_initialized = false;
  64 
  65     private static final Object WINDOWS_BUTTON_UI_KEY = new Object();
  66 
  67     // ********************************
  68     //          Create PLAF
  69     // ********************************
  70     public static ComponentUI createUI(JComponent c) {
  71         AppContext appContext = AppContext.getAppContext();
  72         WindowsButtonUI windowsButtonUI =
  73                 (WindowsButtonUI) appContext.get(WINDOWS_BUTTON_UI_KEY);
  74         if (windowsButtonUI == null) {
  75             windowsButtonUI = new WindowsButtonUI();
  76             appContext.put(WINDOWS_BUTTON_UI_KEY, windowsButtonUI);
  77         }
  78         return windowsButtonUI;
  79     }
  80 
  81 
  82     // ********************************
  83     //            Defaults
  84     // ********************************
  85     protected void installDefaults(AbstractButton b) {
  86         super.installDefaults(b);
  87         if(!defaults_initialized) {
  88             String pp = getPropertyPrefix();
  89             dashedRectGapX = UIManager.getInt(pp + "dashedRectGapX");
  90             dashedRectGapY = UIManager.getInt(pp + "dashedRectGapY");
  91             dashedRectGapWidth = UIManager.getInt(pp + "dashedRectGapWidth");
  92             dashedRectGapHeight = UIManager.getInt(pp + "dashedRectGapHeight");
  93             focusColor = UIManager.getColor(pp + "focus");
  94             defaults_initialized = true;
  95         }
  96 
  97         XPStyle xp = XPStyle.getXP();
  98         if (xp != null) {
  99             b.setBorder(xp.getBorder(b, getXPButtonType(b)));
 100             LookAndFeel.installProperty(b, "rolloverEnabled", Boolean.TRUE);
 101         }
 102     }
 103 
 104     protected void uninstallDefaults(AbstractButton b) {
 105         super.uninstallDefaults(b);
 106         defaults_initialized = false;
 107     }
 108 
 109     protected Color getFocusColor() {
 110         return focusColor;
 111     }
 112 
 113     // ********************************
 114     //         Paint Methods
 115     // ********************************
 116 
 117     /**
 118      * Overridden method to render the text without the mnemonic
 119      */
 120     protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) {
 121         WindowsGraphicsUtils.paintText(g, b, textRect, text, getTextShiftOffset());
 122     }
 123 
 124     protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, Rectangle textRect, Rectangle iconRect){
 125 
 126         // focus painted same color as text on Basic??
 127         int width = b.getWidth();
 128         int height = b.getHeight();
 129         g.setColor(getFocusColor());
 130         BasicGraphicsUtils.drawDashedRect(g, dashedRectGapX, dashedRectGapY,
 131                                           width - dashedRectGapWidth, height - dashedRectGapHeight);
 132     }
 133 
 134     protected void paintButtonPressed(Graphics g, AbstractButton b){
 135         setTextShiftOffset();
 136     }
 137 
 138     // ********************************
 139     //          Layout Methods
 140     // ********************************
 141     public Dimension getPreferredSize(JComponent c) {
 142         Dimension d = super.getPreferredSize(c);
 143 
 144         /* Ensure that the width and height of the button is odd,
 145          * to allow for the focus line if focus is painted
 146          */
 147         AbstractButton b = (AbstractButton)c;
 148         if (d != null && b.isFocusPainted()) {
 149             if(d.width % 2 == 0) { d.width += 1; }
 150             if(d.height % 2 == 0) { d.height += 1; }
 151         }
 152         return d;
 153     }
 154 
 155 
 156     /* These rectangles/insets are allocated once for all
 157      * ButtonUI.paint() calls.  Re-using rectangles rather than
 158      * allocating them in each paint call substantially reduced the time
 159      * it took paint to run.  Obviously, this method can't be re-entered.
 160      */
 161     private Rectangle viewRect = new Rectangle();
 162 
 163     public void paint(Graphics g, JComponent c) {
 164         if (XPStyle.getXP() != null) {
 165             WindowsButtonUI.paintXPButtonBackground(g, c);
 166         }
 167         super.paint(g, c);
 168     }
 169 
 170     static Part getXPButtonType(AbstractButton b) {
 171         if(b instanceof JCheckBox) {
 172             return Part.BP_CHECKBOX;
 173         }
 174         if(b instanceof JRadioButton) {
 175             return Part.BP_RADIOBUTTON;
 176         }
 177         boolean toolbar = (b.getParent() instanceof JToolBar);
 178         return toolbar ? Part.TP_BUTTON : Part.BP_PUSHBUTTON;
 179     }
 180 
 181     static State getXPButtonState(AbstractButton b) {
 182         Part part = getXPButtonType(b);
 183         ButtonModel model = b.getModel();
 184         State state = State.NORMAL;
 185         switch (part) {
 186         case BP_RADIOBUTTON:
 187             /* falls through */
 188         case BP_CHECKBOX:
 189             if (! model.isEnabled()) {
 190                 state = (model.isSelected()) ? State.CHECKEDDISABLED
 191                     : State.UNCHECKEDDISABLED;
 192             } else if (model.isPressed() && model.isArmed()) {
 193                 state = (model.isSelected()) ? State.CHECKEDPRESSED
 194                     : State.UNCHECKEDPRESSED;
 195             } else if (model.isRollover()) {
 196                 state = (model.isSelected()) ? State.CHECKEDHOT
 197                     : State.UNCHECKEDHOT;
 198             } else {
 199                 state = (model.isSelected()) ? State.CHECKEDNORMAL
 200                     : State.UNCHECKEDNORMAL;
 201             }
 202             break;
 203         case BP_PUSHBUTTON:
 204             /* falls through */
 205         case TP_BUTTON:
 206             boolean toolbar = (b.getParent() instanceof JToolBar);
 207             if (toolbar) {
 208                 if (model.isArmed() && model.isPressed()) {
 209                     state = State.PRESSED;
 210                 } else if (!model.isEnabled()) {
 211                     state = State.DISABLED;
 212                 } else if (model.isSelected() && model.isRollover()) {
 213                     state = State.HOTCHECKED;
 214                 } else if (model.isSelected()) {
 215                     state = State.CHECKED;
 216                 } else if (model.isRollover()) {
 217                     state = State.HOT;
 218                 } else if (b.hasFocus()) {
 219                     state = State.HOT;
 220                 }
 221             } else {
 222                 if ((model.isArmed() && model.isPressed())
 223                       || model.isSelected()) {
 224                     state = State.PRESSED;
 225                 } else if (!model.isEnabled()) {
 226                     state = State.DISABLED;
 227                 } else if (model.isRollover() || model.isPressed()) {
 228                     state = State.HOT;
 229                 } else if (b instanceof JButton
 230                            && ((JButton)b).isDefaultButton()) {
 231                     state = State.DEFAULTED;
 232                 } else if (b.hasFocus()) {
 233                     state = State.HOT;
 234                 }
 235             }
 236             break;
 237         default :
 238             state = State.NORMAL;
 239         }
 240 
 241         return state;
 242     }
 243 
 244     static void paintXPButtonBackground(Graphics g, JComponent c) {
 245         AbstractButton b = (AbstractButton)c;
 246 
 247         XPStyle xp = XPStyle.getXP();
 248 
 249         Part part = getXPButtonType(b);
 250 
 251         if (b.isContentAreaFilled() && b.getBorder() != null
 252                 && b.isBorderPainted() && xp != null) {
 253 
 254             Skin skin = xp.getSkin(b, part);
 255 
 256             State state = getXPButtonState(b);
 257             Dimension d = c.getSize();
 258             int dx = 0;
 259             int dy = 0;
 260             int dw = d.width;
 261             int dh = d.height;
 262 
 263             Border border = c.getBorder();
 264             Insets insets;
 265             if (border != null) {
 266                 // Note: The border may be compound, containing an outer
 267                 // opaque border (supplied by the application), plus an
 268                 // inner transparent margin border. We want to size the
 269                 // background to fill the transparent part, but stay
 270                 // inside the opaque part.
 271                 insets = WindowsButtonUI.getOpaqueInsets(border, c);
 272             } else {
 273                 insets = c.getInsets();
 274             }
 275             if (insets != null) {
 276                 dx += insets.left;
 277                 dy += insets.top;
 278                 dw -= (insets.left + insets.right);
 279                 dh -= (insets.top + insets.bottom);
 280             }
 281             skin.paintSkin(g, dx, dy, dw, dh, state);
 282         }
 283     }
 284 
 285     /**
 286      * returns - b.getBorderInsets(c) if border is opaque
 287      *         - null if border is completely non-opaque
 288      *         - somewhere inbetween if border is compound and
 289      *              outside border is opaque and inside isn't
 290      */
 291     private static Insets getOpaqueInsets(Border b, Component c) {
 292         if (b == null) {
 293             return null;
 294         }
 295         if (b.isBorderOpaque()) {
 296             return b.getBorderInsets(c);
 297         } else if (b instanceof CompoundBorder) {
 298             CompoundBorder cb = (CompoundBorder)b;
 299             Insets iOut = getOpaqueInsets(cb.getOutsideBorder(), c);
 300             if (iOut != null && iOut.equals(cb.getOutsideBorder().getBorderInsets(c))) {
 301                 // Outside border is opaque, keep looking
 302                 Insets iIn = getOpaqueInsets(cb.getInsideBorder(), c);
 303                 if (iIn == null) {
 304                     // Inside is non-opaque, use outside insets
 305                     return iOut;
 306                 } else {
 307                     // Found non-opaque somewhere in the inside (which is
 308                     // also compound).
 309                     return new Insets(iOut.top + iIn.top, iOut.left + iIn.left,
 310                                       iOut.bottom + iIn.bottom, iOut.right + iIn.right);
 311                 }
 312             } else {
 313                 // Outside is either all non-opaque or has non-opaque
 314                 // border inside another compound border
 315                 return iOut;
 316             }
 317         } else {
 318             return null;
 319         }
 320     }
 321 }