1 /*
   2  * Copyright (c) 2002, 2013, 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 sun.awt.X11;
  26 
  27 import java.awt.*;
  28 import java.awt.peer.*;
  29 import java.awt.event.*;
  30 
  31 import sun.awt.AWTAccessor;
  32 
  33 public class XMenuItemPeer implements MenuItemPeer {
  34 
  35     /************************************************
  36      *
  37      * Data members
  38      *
  39      ************************************************/
  40 
  41     /*
  42      * Primary members
  43      */
  44 
  45     /**
  46      * Window that this item belongs to.
  47      */
  48     private XBaseMenuWindow container;
  49 
  50     /**
  51      * Target MenuItem. Note that 'target' member
  52      * in XWindow is required for dispatching events.
  53      * This member is only used for accessing its fields
  54      * and firing ActionEvent & ItemEvent
  55      */
  56     private MenuItem target;
  57 
  58     /*
  59      * Mapping to window
  60      */
  61 
  62     /**
  63      * Rectangle occupied by menu item in container's
  64      * coordinates. Filled by map(...) function from
  65      * XBaseMenuWindow.map()
  66      */
  67     private Rectangle bounds;
  68 
  69     /**
  70      * Point in container's coordinate system used as
  71      * origin by drawText.
  72      */
  73     private Point textOrigin;
  74 
  75     /*
  76      * Size constants
  77      */
  78     private final static int SEPARATOR_WIDTH = 20;
  79     private final static int SEPARATOR_HEIGHT = 5;
  80 
  81     /************************************************
  82      *
  83      * Text Metrics
  84      *
  85      ************************************************/
  86 
  87     /**
  88      * Text metrics are filled in calcTextMetrics function
  89      * and reset in resetTextMetrics function. Text metrics
  90      * contain calculated dimensions of various components of
  91      * menu item.
  92      */
  93     private TextMetrics textMetrics;
  94 
  95     static class TextMetrics implements Cloneable {
  96         /*
  97          * Calculated text size members
  98          */
  99         private Dimension textDimension;
 100         private int shortcutWidth;
 101         private int textBaseline;
 102 
 103         TextMetrics(Dimension textDimension, int shortcutWidth, int textBaseline) {
 104             this.textDimension = textDimension;
 105             this.shortcutWidth = shortcutWidth;
 106             this.textBaseline = textBaseline;
 107         }
 108 
 109         public Object clone() {
 110             try {
 111                 return super.clone();
 112             } catch (CloneNotSupportedException ex) {
 113                 throw new InternalError(ex);
 114             }
 115         }
 116 
 117         Dimension getTextDimension() {
 118             return this.textDimension;
 119         }
 120 
 121         int getShortcutWidth() {
 122             return this.shortcutWidth;
 123         }
 124 
 125         int getTextBaseline() {
 126             return this.textBaseline;
 127         }
 128     }
 129 
 130     /************************************************
 131      *
 132      * Construction
 133      *
 134      ************************************************/
 135     XMenuItemPeer(MenuItem target) {
 136         this.target = target;
 137     }
 138 
 139     /************************************************
 140      *
 141      * Implementaion of interface methods
 142      *
 143      ************************************************/
 144 
 145     /*
 146      * From MenuComponentPeer
 147      */
 148     public void dispose() {
 149         //Empty function
 150     }
 151 
 152     public void setFont(Font font) {
 153         resetTextMetrics();
 154         repaintIfShowing();
 155     }
 156     /*
 157      * From MenuItemPeer
 158      */
 159     public void setLabel(String label) {
 160         resetTextMetrics();
 161         repaintIfShowing();
 162     }
 163 
 164     public void setEnabled(boolean enabled) {
 165         repaintIfShowing();
 166     }
 167 
 168     /**
 169      * DEPRECATED:  Replaced by setEnabled(boolean).
 170      * @see java.awt.peer.MenuItemPeer
 171      */
 172     public void enable() {
 173         setEnabled( true );
 174     }
 175 
 176     /**
 177      * DEPRECATED:  Replaced by setEnabled(boolean).
 178      * @see java.awt.peer.MenuItemPeer
 179      */
 180     public void disable() {
 181         setEnabled( false );
 182     }
 183 
 184     /************************************************
 185      *
 186      * Access to target's fields
 187      *
 188      ************************************************/
 189 
 190     MenuItem getTarget() {
 191         return this.target;
 192     }
 193 
 194     Font getTargetFont() {
 195         if (target == null) {
 196             return XWindow.getDefaultFont();
 197         }
 198         return AWTAccessor.getMenuComponentAccessor().getFont_NoClientCode(target);
 199     }
 200 
 201     String getTargetLabel() {
 202         if (target == null) {
 203             return "";
 204         }
 205         String label = AWTAccessor.getMenuItemAccessor().getLabel(target);
 206         return (label == null) ? "" : label;
 207     }
 208 
 209     boolean isTargetEnabled() {
 210         if (target == null) {
 211             return false;
 212         }
 213         return AWTAccessor.getMenuItemAccessor().isEnabled(target);
 214     }
 215 
 216     /**
 217      * Returns true if item and all its parents are enabled
 218      * This function is used to fix
 219      * 6184485: Popup menu is not disabled on XToolkit even when calling setEnabled (false)
 220      */
 221     boolean isTargetItemEnabled() {
 222         if (target == null) {
 223             return false;
 224         }
 225         return AWTAccessor.getMenuItemAccessor().isItemEnabled(target);
 226     }
 227 
 228     String getTargetActionCommand() {
 229         if (target == null) {
 230             return "";
 231         }
 232         return AWTAccessor.getMenuItemAccessor().getActionCommandImpl(target);
 233     }
 234 
 235     MenuShortcut getTargetShortcut() {
 236         if (target == null) {
 237             return null;
 238         }
 239         return AWTAccessor.getMenuItemAccessor().getShortcut(target);
 240     }
 241 
 242     String getShortcutText() {
 243         //Fix for 6180413: shortcuts should not be displayed for any of the menuitems in a popup menu
 244         if (container == null) {
 245             return null;
 246         }
 247         if (container.getRootMenuWindow() instanceof XPopupMenuPeer) {
 248             return null;
 249         }
 250         MenuShortcut sc = getTargetShortcut();
 251         //TODO:This can potentially call user code
 252         return (sc == null) ? null : sc.toString();
 253     }
 254 
 255 
 256     /************************************************
 257      *
 258      * Basic manipulations
 259      *
 260      ************************************************/
 261 
 262     /**
 263      * This function is called when filling item vectors
 264      * in XMenuWindow & XMenuBar. We need it because peers
 265      * are created earlier than windows.
 266      * @param container the window that this item belongs to.
 267      */
 268     void setContainer(XBaseMenuWindow container) {
 269         synchronized(XBaseMenuWindow.getMenuTreeLock()) {
 270             this.container = container;
 271         }
 272     }
 273 
 274     /**
 275      * returns the window that this item belongs to
 276      */
 277     XBaseMenuWindow getContainer() {
 278         return this.container;
 279     }
 280 
 281     /************************************************
 282      *
 283      * Overridable behaviour
 284      *
 285      ************************************************/
 286 
 287     /**
 288      * This function should be overriden simply to
 289      * return false in inherited classes.
 290      */
 291     boolean isSeparator() {
 292         boolean r = (getTargetLabel().equals("-"));
 293         return r;
 294     }
 295 
 296     /************************************************
 297      *
 298      * Utility functions
 299      *
 300      ************************************************/
 301 
 302     /**
 303      * Returns true if container exists and is showing
 304      */
 305     boolean isContainerShowing() {
 306         if (container == null) {
 307             return false;
 308         }
 309         return container.isShowing();
 310     }
 311 
 312     /**
 313      * Repaints item if it is showing
 314      */
 315     void repaintIfShowing() {
 316         if (isContainerShowing()) {
 317             container.postPaintEvent();
 318         }
 319     }
 320 
 321     /**
 322      * This function is invoked when the user clicks
 323      * on menu item.
 324      * @param when the timestamp of action event
 325      */
 326     void action(long when) {
 327         if (!isSeparator() && isTargetItemEnabled()) {
 328             XWindow.postEventStatic(new ActionEvent(target, ActionEvent.ACTION_PERFORMED,
 329                                                     getTargetActionCommand(), when,
 330                                                     0));
 331         }
 332     }
 333     /************************************************
 334      *
 335      * Text metrics
 336      *
 337      ************************************************/
 338 
 339     /**
 340      * Returns text metrics of menu item.
 341      * This function does not use any locks
 342      * and is guaranteed to return some value
 343      * (possibly actual, possibly expired)
 344      */
 345     TextMetrics getTextMetrics() {
 346         TextMetrics textMetrics = this.textMetrics;
 347         if (textMetrics == null) {
 348             textMetrics = calcTextMetrics();
 349             this.textMetrics = textMetrics;
 350         }
 351         return textMetrics;
 352     }
 353 
 354     /**
 355      * Returns dimensions of item's label.
 356      * This function does not use any locks
 357      * Returns actual or expired  value
 358      * or null if error occurs
 359      */
 360     /*Dimension getTextDimension() {
 361         TextMetrics textMetrics = this.textMetrics;
 362         if (textMetrics == null) {
 363             textMetrics = calcTextMetrics();
 364             this.textMetrics = textMetrics;
 365         }
 366         return (textMetrics != null) ? textMetrics.textDimension : null;
 367         }*/
 368 
 369     /**
 370      * Returns width of item's shortcut label,
 371      * 0 if item has no shortcut.
 372      * The height of shortcut can be deternimed
 373      * from text dimensions.
 374      * This function does not use any locks
 375      * and is guaranteed to return some value
 376      * (possibly actual, possibly expired)
 377      */
 378     /*int getShortcutWidth() {
 379         TextMetrics textMetrics = this.textMetrics;
 380         if (textMetrics == null) {
 381             textMetrics = calcTextMetrics();
 382             this.textMetrics = textMetrics;
 383         }
 384         return (textMetrics != null) ? textMetrics.shortcutWidth : 0;
 385     }
 386 
 387     int getTextBaseline() {
 388         TextMetrics textMetrics = this.textMetrics;
 389         if (textMetrics == null) {
 390             textMetrics = calcTextMetrics();
 391             this.textMetrics = textMetrics;
 392         }
 393         return (textMetrics != null) ? textMetrics.textBaseline : 0;
 394         }*/
 395 
 396     TextMetrics calcTextMetrics() {
 397         if (container == null) {
 398             return null;
 399         }
 400         if (isSeparator()) {
 401             return new TextMetrics(new Dimension(SEPARATOR_WIDTH, SEPARATOR_HEIGHT), 0, 0);
 402         }
 403         Graphics g = container.getGraphics();
 404         if (g == null) {
 405             return null;
 406         }
 407         try {
 408             g.setFont(getTargetFont());
 409             FontMetrics fm = g.getFontMetrics();
 410             String str = getTargetLabel();
 411             int width = fm.stringWidth(str);
 412             int height = fm.getHeight();
 413             Dimension textDimension = new Dimension(width, height);
 414             int textBaseline = fm.getHeight() - fm.getAscent();
 415             String sc = getShortcutText();
 416             int shortcutWidth = (sc == null) ? 0 : fm.stringWidth(sc);
 417             return new TextMetrics(textDimension, shortcutWidth, textBaseline);
 418         } finally {
 419             g.dispose();
 420         }
 421     }
 422 
 423     void resetTextMetrics() {
 424         textMetrics = null;
 425         if (container != null) {
 426             container.updateSize();
 427         }
 428     }
 429 
 430     /************************************************
 431      *
 432      * Mapping utility functions
 433      *
 434      ************************************************/
 435 
 436     /**
 437      * Sets mapping of item to window.
 438      * @param bounds bounds of item in container's coordinates
 439      * @param textOrigin point for drawString in container's coordinates
 440      * @see XBaseMenuWindow#map()
 441      */
 442     void map(Rectangle bounds, Point textOrigin) {
 443         this.bounds = bounds;
 444         this.textOrigin = textOrigin;
 445     }
 446 
 447     /**
 448      * returns bounds of item that were previously set by map() function
 449      */
 450     Rectangle getBounds() {
 451         return bounds;
 452     }
 453 
 454     /**
 455      * returns origin of item's text that was previously set by map() function
 456      */
 457     Point getTextOrigin() {
 458         return textOrigin;
 459     }
 460 
 461 }