jdk/src/solaris/classes/sun/awt/X11/XMenuItemPeer.java
author ohair
Tue, 25 May 2010 15:58:33 -0700
changeset 5506 202f599c92aa
parent 4214 0fa32d38146b
child 10419 12c063b39232
permissions -rw-r--r--
6943119: Rebrand source copyright notices Reviewed-by: darcy, weijun

/*
 * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package sun.awt.X11;

import java.awt.*;
import java.awt.peer.*;
import java.awt.event.*;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import sun.awt.SunToolkit;

public class XMenuItemPeer implements MenuItemPeer {

    /************************************************
     *
     * Data members
     *
     ************************************************/

    /*
     * Primary members
     */

    /**
     * Window that this item belongs to.
     */
    private XBaseMenuWindow container;

    /**
     * Target MenuItem. Note that 'target' member
     * in XWindow is required for dispatching events.
     * This member is only used for accessing its fields
     * and firing ActionEvent & ItemEvent
     */
    private MenuItem target;

    /*
     * Mapping to window
     */

    /**
     * Rectange occupied by menu item in container's
     * coordinates. Filled by map(...) function from
     * XBaseMenuWindow.map()
     */
    private Rectangle bounds;

    /**
     * Point in container's coordinate system used as
     * origin by drawText.
     */
    private Point textOrigin;

    /*
     * Size constants
     */
    private final static int SEPARATOR_WIDTH = 20;
    private final static int SEPARATOR_HEIGHT = 5;

    /*
     * MenuItem's fields & methods
     */
    private final static Field f_enabled;
    private final static Field f_label;
    private final static Field f_shortcut;
    private final static Method m_getFont;
    private final static Method m_isItemEnabled;
    private final static Method m_getActionCommand;
    static {
        f_enabled = SunToolkit.getField(MenuItem.class, "enabled");
        f_label = SunToolkit.getField(MenuItem.class, "label");
        f_shortcut = SunToolkit.getField(MenuItem.class, "shortcut");

        m_getFont = SunToolkit.getMethod(MenuComponent.class, "getFont_NoClientCode", null);
        m_getActionCommand = SunToolkit.getMethod(MenuItem.class, "getActionCommandImpl", null);
        m_isItemEnabled = SunToolkit.getMethod(MenuItem.class, "isItemEnabled", null);
    }
    /************************************************
     *
     * Text Metrics
     *
     ************************************************/

    /**
     * Text metrics are filled in calcTextMetrics function
     * and reset in resetTextMetrics function. Text metrics
     * contain calculated dimensions of various components of
     * menu item.
     */
    private TextMetrics textMetrics;

    static class TextMetrics implements Cloneable {
        /*
         * Calculated text size members
         */
        private Dimension textDimension;
        private int shortcutWidth;
        private int textBaseline;

        TextMetrics(Dimension textDimension, int shortcutWidth, int textBaseline) {
            this.textDimension = textDimension;
            this.shortcutWidth = shortcutWidth;
            this.textBaseline = textBaseline;
        }

        public Object clone() {
            try {
                return super.clone();
            } catch (CloneNotSupportedException ex) {
                throw new InternalError();
            }
        }

        Dimension getTextDimension() {
            return this.textDimension;
        }

        int getShortcutWidth() {
            return this.shortcutWidth;
        }

        int getTextBaseline() {
            return this.textBaseline;
        }
    }

    /************************************************
     *
     * Construction
     *
     ************************************************/
    XMenuItemPeer(MenuItem target) {
        this.target = target;
    }

    /************************************************
     *
     * Implementaion of interface methods
     *
     ************************************************/

    /*
     * From MenuComponentPeer
     */
    public void dispose() {
        //Empty function
    }

    public void setFont(Font font) {
        resetTextMetrics();
        repaintIfShowing();
    }
    /*
     * From MenuItemPeer
     */
    public void setLabel(String label) {
        resetTextMetrics();
        repaintIfShowing();
    }

    public void setEnabled(boolean enabled) {
        repaintIfShowing();
    }

    /**
     * DEPRECATED:  Replaced by setEnabled(boolean).
     * @see java.awt.peer.MenuItemPeer
     */
    public void enable() {
        setEnabled( true );
    }

    /**
     * DEPRECATED:  Replaced by setEnabled(boolean).
     * @see java.awt.peer.MenuItemPeer
     */
    public void disable() {
        setEnabled( false );
    }

    /************************************************
     *
     * Access to target's fields
     *
     ************************************************/

    MenuItem getTarget() {
        return this.target;
    }

    Font getTargetFont() {
        if (target == null) {
            return XWindow.getDefaultFont();
        }
        try {
            return (Font)m_getFont.invoke(target, new Object[0]);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return XWindow.getDefaultFont();
    }

    String getTargetLabel() {
        if (target == null) {
            return "";
        }
        try {
            String label = (String)f_label.get(target);
            return (label == null) ? "" : label;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return "";
    }

    boolean isTargetEnabled() {
        if (target == null) {
            return false;
        }
        try {
            return f_enabled.getBoolean(target);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Returns true if item and all its parents are enabled
     * This function is used to fix
     * 6184485: Popup menu is not disabled on XToolkit even when calling setEnabled (false)
     */
    boolean isTargetItemEnabled() {
        if (target == null) {
            return false;
        }
        try {
            return ((Boolean)m_isItemEnabled.invoke(target, new Object[0])).booleanValue();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return false;
    }

    String getTargetActionCommand() {
        if (target == null) {
            return "";
        }
        try {
            return (String) m_getActionCommand.invoke(target,(Object[]) null);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return "";
    }

    MenuShortcut getTargetShortcut() {
        if (target == null) {
            return null;
        }
        try {
            return (MenuShortcut)f_shortcut.get(target);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    String getShortcutText() {
        //Fix for 6180413: shortcuts should not be displayed for any of the menuitems in a popup menu
        if (container == null) {
            return null;
        }
        if (container.getRootMenuWindow() instanceof XPopupMenuPeer) {
            return null;
        }
        MenuShortcut sc = getTargetShortcut();
        //TODO:This can potentially call user code
        return (sc == null) ? null : sc.toString();
    }


    /************************************************
     *
     * Basic manipulations
     *
     ************************************************/

    /**
     * This function is called when filling item vectors
     * in XMenuWindow & XMenuBar. We need it because peers
     * are created earlier than windows.
     * @param container the window that this item belongs to.
     */
    void setContainer(XBaseMenuWindow container) {
        synchronized(XBaseMenuWindow.getMenuTreeLock()) {
            this.container = container;
        }
    }

    /**
     * returns the window that this item belongs to
     */
    XBaseMenuWindow getContainer() {
        return this.container;
    }

    /************************************************
     *
     * Overridable behaviour
     *
     ************************************************/

    /**
     * This function should be overriden simply to
     * return false in inherited classes.
     */
    boolean isSeparator() {
        boolean r = (getTargetLabel().equals("-"));
        return r;
    }

    /************************************************
     *
     * Utility functions
     *
     ************************************************/

    /**
     * Returns true if container exists and is showing
     */
    boolean isContainerShowing() {
        if (container == null) {
            return false;
        }
        return container.isShowing();
    }

    /**
     * Repaints item if it is showing
     */
    void repaintIfShowing() {
        if (isContainerShowing()) {
            container.postPaintEvent();
        }
    }

    /**
     * This function is invoked when the user clicks
     * on menu item.
     * @param when the timestamp of action event
     */
    void action(long when) {
        if (!isSeparator() && isTargetItemEnabled()) {
            XWindow.postEventStatic(new ActionEvent(target, ActionEvent.ACTION_PERFORMED,
                                                    getTargetActionCommand(), when,
                                                    0));
        }
    }
    /************************************************
     *
     * Text metrics
     *
     ************************************************/

    /**
     * Returns text metrics of menu item.
     * This function does not use any locks
     * and is guaranteed to return some value
     * (possibly actual, possibly expired)
     */
    TextMetrics getTextMetrics() {
        TextMetrics textMetrics = this.textMetrics;
        if (textMetrics == null) {
            textMetrics = calcTextMetrics();
            this.textMetrics = textMetrics;
        }
        return textMetrics;
    }

    /**
     * Returns dimensions of item's label.
     * This function does not use any locks
     * Returns actual or expired  value
     * or null if error occurs
     */
    /*Dimension getTextDimension() {
        TextMetrics textMetrics = this.textMetrics;
        if (textMetrics == null) {
            textMetrics = calcTextMetrics();
            this.textMetrics = textMetrics;
        }
        return (textMetrics != null) ? textMetrics.textDimension : null;
        }*/

    /**
     * Returns width of item's shortcut label,
     * 0 if item has no shortcut.
     * The height of shortcut can be deternimed
     * from text dimensions.
     * This function does not use any locks
     * and is guaranteed to return some value
     * (possibly actual, possibly expired)
     */
    /*int getShortcutWidth() {
        TextMetrics textMetrics = this.textMetrics;
        if (textMetrics == null) {
            textMetrics = calcTextMetrics();
            this.textMetrics = textMetrics;
        }
        return (textMetrics != null) ? textMetrics.shortcutWidth : 0;
    }

    int getTextBaseline() {
        TextMetrics textMetrics = this.textMetrics;
        if (textMetrics == null) {
            textMetrics = calcTextMetrics();
            this.textMetrics = textMetrics;
        }
        return (textMetrics != null) ? textMetrics.textBaseline : 0;
        }*/

    TextMetrics calcTextMetrics() {
        if (container == null) {
            return null;
        }
        if (isSeparator()) {
            return new TextMetrics(new Dimension(SEPARATOR_WIDTH, SEPARATOR_HEIGHT), 0, 0);
        }
        Graphics g = container.getGraphics();
        if (g == null) {
            return null;
        }
        try {
            g.setFont(getTargetFont());
            FontMetrics fm = g.getFontMetrics();
            String str = getTargetLabel();
            int width = fm.stringWidth(str);
            int height = fm.getHeight();
            Dimension textDimension = new Dimension(width, height);
            int textBaseline = fm.getHeight() - fm.getAscent();
            String sc = getShortcutText();
            int shortcutWidth = (sc == null) ? 0 : fm.stringWidth(sc);
            return new TextMetrics(textDimension, shortcutWidth, textBaseline);
        } finally {
            g.dispose();
        }
    }

    void resetTextMetrics() {
        textMetrics = null;
        if (container != null) {
            container.updateSize();
        }
    }

    /************************************************
     *
     * Mapping utility functions
     *
     ************************************************/

    /**
     * Sets mapping of item to window.
     * @param bounds bounds of item in container's coordinates
     * @param textOrigin point for drawString in container's coordinates
     * @see XBaseMenuWindow.map()
     */
    void map(Rectangle bounds, Point textOrigin) {
        this.bounds = bounds;
        this.textOrigin = textOrigin;
    }

    /**
     * returns bounds of item that were previously set by map() function
     */
    Rectangle getBounds() {
        return bounds;
    }

    /**
     * returns origin of item's text that was previously set by map() function
     */
    Point getTextOrigin() {
        return textOrigin;
    }

}