1 /*
   2  * Copyright (c) 2002, 2010, 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 package javax.swing.plaf.synth;
  26 
  27 import java.awt.*;
  28 import java.beans.PropertyChangeEvent;
  29 import java.beans.PropertyChangeListener;
  30 import javax.swing.*;
  31 import javax.swing.plaf.*;
  32 import javax.swing.plaf.basic.*;
  33 import sun.swing.MenuItemLayoutHelper;
  34 
  35 
  36 /**
  37  * Provides the Synth L&F UI delegate for
  38  * {@link javax.swing.JMenuItem}.
  39  *
  40  * @author Georges Saab
  41  * @author David Karlton
  42  * @author Arnaud Weber
  43  * @author Fredrik Lagerblad
  44  * @since 1.7
  45  */
  46 public class SynthMenuItemUI extends BasicMenuItemUI implements
  47                                    PropertyChangeListener, SynthUI {
  48     private SynthStyle style;
  49     private SynthStyle accStyle;
  50 
  51     /**
  52      * Creates a new UI object for the given component.
  53      *
  54      * @param c component to create UI object for
  55      * @return the UI object
  56      */
  57     public static ComponentUI createUI(JComponent c) {
  58         return new SynthMenuItemUI();
  59     }
  60 
  61     /**
  62      * @inheritDoc
  63      */
  64     @Override
  65     public void uninstallUI(JComponent c) {
  66         super.uninstallUI(c);
  67         // Remove values from the parent's Client Properties.
  68         JComponent p = MenuItemLayoutHelper.getMenuItemParent((JMenuItem) c);
  69         if (p != null) {
  70             p.putClientProperty(
  71                     SynthMenuItemLayoutHelper.MAX_ACC_OR_ARROW_WIDTH, null);
  72         }
  73     }
  74 
  75     /**
  76      * @inheritDoc
  77      */
  78     @Override
  79     protected void installDefaults() {
  80         updateStyle(menuItem);
  81     }
  82 
  83     /**
  84      * @inheritDoc
  85      */
  86     @Override
  87     protected void installListeners() {
  88         super.installListeners();
  89         menuItem.addPropertyChangeListener(this);
  90     }
  91 
  92     private void updateStyle(JMenuItem mi) {
  93         SynthContext context = getContext(mi, ENABLED);
  94         SynthStyle oldStyle = style;
  95 
  96         style = SynthLookAndFeel.updateStyle(context, this);
  97         if (oldStyle != style) {
  98             String prefix = getPropertyPrefix();
  99 
 100             Object value = style.get(context, prefix + ".textIconGap");
 101             if (value != null) {
 102                 LookAndFeel.installProperty(mi, "iconTextGap", value);
 103             }
 104             defaultTextIconGap = mi.getIconTextGap();
 105 
 106             if (menuItem.getMargin() == null ||
 107                          (menuItem.getMargin() instanceof UIResource)) {
 108                 Insets insets = (Insets)style.get(context, prefix + ".margin");
 109 
 110                 if (insets == null) {
 111                     // Some places assume margins are non-null.
 112                     insets = SynthLookAndFeel.EMPTY_UIRESOURCE_INSETS;
 113                 }
 114                 menuItem.setMargin(insets);
 115             }
 116             acceleratorDelimiter = style.getString(context, prefix +
 117                                             ".acceleratorDelimiter", "+");
 118 
 119             arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
 120 
 121             checkIcon = style.getIcon(context, prefix + ".checkIcon");
 122             if (oldStyle != null) {
 123                 uninstallKeyboardActions();
 124                 installKeyboardActions();
 125             }
 126         }
 127         context.dispose();
 128 
 129         SynthContext accContext = getContext(mi, Region.MENU_ITEM_ACCELERATOR,
 130                                              ENABLED);
 131 
 132         accStyle = SynthLookAndFeel.updateStyle(accContext, this);
 133         accContext.dispose();
 134     }
 135 
 136     /**
 137      * @inheritDoc
 138      */
 139     @Override
 140     protected void uninstallDefaults() {
 141         SynthContext context = getContext(menuItem, ENABLED);
 142         style.uninstallDefaults(context);
 143         context.dispose();
 144         style = null;
 145 
 146         SynthContext accContext = getContext(menuItem,
 147                                      Region.MENU_ITEM_ACCELERATOR, ENABLED);
 148         accStyle.uninstallDefaults(accContext);
 149         accContext.dispose();
 150         accStyle = null;
 151 
 152         super.uninstallDefaults();
 153     }
 154 
 155     /**
 156      * @inheritDoc
 157      */
 158     @Override
 159     protected void uninstallListeners() {
 160         super.uninstallListeners();
 161         menuItem.removePropertyChangeListener(this);
 162     }
 163 
 164     /**
 165      * @inheritDoc
 166      */
 167     @Override
 168     public SynthContext getContext(JComponent c) {
 169         return getContext(c, getComponentState(c));
 170     }
 171 
 172     SynthContext getContext(JComponent c, int state) {
 173         return SynthContext.getContext(SynthContext.class, c,
 174                     SynthLookAndFeel.getRegion(c), style, state);
 175     }
 176 
 177     SynthContext getContext(JComponent c, Region region) {
 178         return getContext(c, region, getComponentState(c, region));
 179     }
 180 
 181     private SynthContext getContext(JComponent c, Region region, int state) {
 182         return SynthContext.getContext(SynthContext.class, c,
 183                                        region, accStyle, state);
 184     }
 185 
 186     private int getComponentState(JComponent c) {
 187         int state;
 188 
 189         if (!c.isEnabled()) {
 190             state = DISABLED;
 191         }
 192         else if (menuItem.isArmed()) {
 193             state = MOUSE_OVER;
 194         }
 195         else {
 196             state = SynthLookAndFeel.getComponentState(c);
 197         }
 198         if (menuItem.isSelected()) {
 199             state |= SELECTED;
 200         }
 201         return state;
 202     }
 203 
 204     private int getComponentState(JComponent c, Region region) {
 205         return getComponentState(c);
 206     }
 207 
 208     /**
 209      * @inheritDoc
 210      */
 211     @Override
 212     protected Dimension getPreferredMenuItemSize(JComponent c,
 213                                                      Icon checkIcon,
 214                                                      Icon arrowIcon,
 215                                                      int defaultTextIconGap) {
 216         SynthContext context = getContext(c);
 217         SynthContext accContext = getContext(c, Region.MENU_ITEM_ACCELERATOR);
 218         Dimension value = SynthGraphicsUtils.getPreferredMenuItemSize(
 219                 context, accContext, c, checkIcon, arrowIcon,
 220                 defaultTextIconGap, acceleratorDelimiter,
 221                 MenuItemLayoutHelper.useCheckAndArrow(menuItem),
 222                 getPropertyPrefix());
 223         context.dispose();
 224         accContext.dispose();
 225         return value;
 226     }
 227 
 228 
 229     /**
 230      * Notifies this UI delegate to repaint the specified component.
 231      * This method paints the component background, then calls
 232      * the {@link #paint(SynthContext,Graphics)} method.
 233      *
 234      * <p>In general, this method does not need to be overridden by subclasses.
 235      * All Look and Feel rendering code should reside in the {@code paint} method.
 236      *
 237      * @param g the {@code Graphics} object used for painting
 238      * @param c the component being painted
 239      * @see #paint(SynthContext,Graphics)
 240      */
 241     @Override
 242     public void update(Graphics g, JComponent c) {
 243         SynthContext context = getContext(c);
 244 
 245         SynthLookAndFeel.update(context, g);
 246         paintBackground(context, g, c);
 247         paint(context, g);
 248         context.dispose();
 249     }
 250 
 251     /**
 252      * Paints the specified component according to the Look and Feel.
 253      * <p>This method is not used by Synth Look and Feel.
 254      * Painting is handled by the {@link #paint(SynthContext,Graphics)} method.
 255      *
 256      * @param g the {@code Graphics} object used for painting
 257      * @param c the component being painted
 258      * @see #paint(SynthContext,Graphics)
 259      */
 260     @Override
 261     public void paint(Graphics g, JComponent c) {
 262         SynthContext context = getContext(c);
 263 
 264         paint(context, g);
 265         context.dispose();
 266     }
 267 
 268     /**
 269      * Paints the specified component.
 270      *
 271      * @param context context for the component being painted
 272      * @param g the {@code Graphics} object used for painting
 273      * @see #update(Graphics,JComponent)
 274      */
 275     protected void paint(SynthContext context, Graphics g) {
 276         SynthContext accContext = getContext(menuItem,
 277                                              Region.MENU_ITEM_ACCELERATOR);
 278 
 279         // Refetch the appropriate check indicator for the current state
 280         String prefix = getPropertyPrefix();
 281         Icon checkIcon = style.getIcon(context, prefix + ".checkIcon");
 282         Icon arrowIcon = style.getIcon(context, prefix + ".arrowIcon");
 283         SynthGraphicsUtils.paint(context, accContext, g, checkIcon, arrowIcon,
 284               acceleratorDelimiter, defaultTextIconGap, getPropertyPrefix());
 285         accContext.dispose();
 286     }
 287 
 288     void paintBackground(SynthContext context, Graphics g, JComponent c) {
 289         SynthGraphicsUtils.paintBackground(context, g, c);
 290     }
 291 
 292     /**
 293      * @inheritDoc
 294      */
 295     @Override
 296     public void paintBorder(SynthContext context, Graphics g, int x,
 297                             int y, int w, int h) {
 298         context.getPainter().paintMenuItemBorder(context, g, x, y, w, h);
 299     }
 300 
 301     /**
 302      * @inheritDoc
 303      */
 304     @Override
 305     public void propertyChange(PropertyChangeEvent e) {
 306         if (SynthLookAndFeel.shouldUpdateStyle(e)) {
 307             updateStyle((JMenuItem)e.getSource());
 308         }
 309     }
 310 }