Package com.alee.laf.menu

Source Code of com.alee.laf.menu.WebPopupMenuUI

/*
* This file is part of WebLookAndFeel library.
*
* WebLookAndFeel library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* WebLookAndFeel library 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with WebLookAndFeel library.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.alee.laf.menu;

import com.alee.extended.painter.Painter;
import com.alee.extended.painter.PainterSupport;
import com.alee.global.StyleConstants;
import com.alee.laf.WebLookAndFeel;
import com.alee.managers.style.StyleManager;
import com.alee.managers.style.skin.web.PopupStyle;
import com.alee.utils.LafUtils;
import com.alee.utils.ProprietaryUtils;
import com.alee.utils.SwingUtils;
import com.alee.utils.SystemUtils;
import com.alee.utils.laf.ShapeProvider;
import com.alee.utils.laf.Styleable;
import com.alee.utils.swing.BorderMethods;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPopupMenuUI;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

/**
* Custom UI for JPopupMenu component.
*
* @author Mikle Garin
*/

public class WebPopupMenuUI extends BasicPopupMenuUI implements SwingConstants, Styleable, ShapeProvider, BorderMethods
{
    /**
     * UI style settings.
     */
    protected Insets margin = WebPopupMenuStyle.margin;
    protected int menuSpacing = WebPopupMenuStyle.menuSpacing;
    protected boolean fixLocation = WebPopupMenuStyle.fixLocation;

    /**
     * Component painter.
     */
    protected PopupMenuPainter painter;

    /**
     * Menu listeners.
     */
    protected PropertyChangeListener popupMenuTypeUpdater;
    protected PropertyChangeListener orientationChangeListener;
    protected PropertyChangeListener visibilityChangeListener;
    protected PropertyChangeListener jdkSevenFixListener;

    /**
     * Runtime variables.
     */
    protected String styleId = null;
    protected boolean transparent = false;
    protected PopupMenuWay popupMenuWay = null;

    /**
     * Returns an instance of the WebPopupMenuUI for the specified component.
     * This tricky method is used by UIManager to create component UIs when needed.
     *
     * @param c component that will use UI instance
     * @return instance of the WebPopupMenuUI
     */
    @SuppressWarnings ("UnusedParameters")
    public static ComponentUI createUI ( final JComponent c )
    {
        return new WebPopupMenuUI ();
    }

    /**
     * Installs UI in the specified component.
     *
     * @param c component for this UI
     */
    @Override
    public void installUI ( final JComponent c )
    {
        super.installUI ( c );

        // Default settings
        transparent = ProprietaryUtils.isWindowTransparencyAllowed ();
        SwingUtils.setOrientation ( popupMenu );
        SwingUtils.setHandlesEnableStateMark ( popupMenu );

        // Applying skin
        StyleManager.applySkin ( popupMenu );

        // Popup menu type updater
        popupMenuTypeUpdater = new PropertyChangeListener ()
        {
            @Override
            public void propertyChange ( final PropertyChangeEvent evt )
            {
                if ( evt.getNewValue () == Boolean.TRUE )
                {
                    // Update menu style
                    final Component invoker = popupMenu.getInvoker ();
                    if ( invoker != null )
                    {
                        if ( invoker instanceof JMenu )
                        {
                            if ( invoker.getParent () instanceof JPopupMenu )
                            {
                                painter.setPopupMenuType ( PopupMenuType.menuBarSubMenu );
                            }
                            else
                            {
                                painter.setPopupMenuType ( PopupMenuType.menuBarMenu );
                            }
                        }
                        else if ( invoker instanceof JComboBox )
                        {
                            painter.setPopupMenuType ( PopupMenuType.comboBoxMenu );
                        }
                        else
                        {
                            painter.setPopupMenuType ( PopupMenuType.customPopupMenu );
                        }
                    }
                    else
                    {
                        painter.setPopupMenuType ( PopupMenuType.customPopupMenu );
                    }
                }
            }
        };
        popupMenu.addPropertyChangeListener ( WebLookAndFeel.VISIBLE_PROPERTY, popupMenuTypeUpdater );

        // Popup orientation change listener
        orientationChangeListener = new PropertyChangeListener ()
        {
            @Override
            public void propertyChange ( final PropertyChangeEvent evt )
            {
                popupMenu.setVisible ( false );
            }
        };
        popupMenu.addPropertyChangeListener ( WebLookAndFeel.ORIENTATION_PROPERTY, orientationChangeListener );

        // Special listeners which set proper popup window opacity when needed
        if ( transparent )
        {
            visibilityChangeListener = new PropertyChangeListener ()
            {
                private Window ancestor;

                @Override
                public void propertyChange ( final PropertyChangeEvent evt )
                {
                    if ( evt.getNewValue () == Boolean.TRUE )
                    {
                        ancestor = SwingUtils.getWindowAncestor ( popupMenu );

                        // Workaround to remove Mac OS X shade around the menu window
                        if ( ancestor instanceof JWindow && SystemUtils.isMac () )
                        {
                            ( ( JWindow ) ancestor ).getRootPane ().putClientProperty ( "Window.shadow", Boolean.FALSE );
                        }

                        // Updating menu opacity state in case menu is in a separate heavy-weight window
                        if ( SwingUtils.isHeavyWeightWindow ( ancestor ) )
                        {
                            ProprietaryUtils.setWindowOpaque ( ancestor, false );
                        }
                    }
                    else
                    {
                        // Restoring menu opacity state in case menu is in a separate heavy-weight window
                        if ( SwingUtils.isHeavyWeightWindow ( ancestor ) )
                        {
                            ProprietaryUtils.setWindowOpaque ( ancestor, true );
                        }
                    }
                }
            };
            popupMenu.addPropertyChangeListener ( WebLookAndFeel.VISIBLE_PROPERTY, visibilityChangeListener );
        }
        else
        {
            // Workaround for menu with non-opaque parent window
            if ( SystemUtils.isJava7orAbove () )
            {
                jdkSevenFixListener = new PropertyChangeListener ()
                {
                    @Override
                    public void propertyChange ( final PropertyChangeEvent evt )
                    {
                        if ( evt.getNewValue () == Boolean.TRUE )
                        {
                            final Window ancestor = SwingUtils.getWindowAncestor ( popupMenu );
                            if ( SwingUtils.isHeavyWeightWindow ( ancestor ) )
                            {
                                final Component parent = ancestor.getParent ();
                                if ( parent != null && parent instanceof Window && !ProprietaryUtils.isWindowOpaque ( ( Window ) parent ) )
                                {
                                    ProprietaryUtils.setWindowOpaque ( ancestor, false );
                                }
                            }
                        }
                    }
                };
                popupMenu.addPropertyChangeListener ( WebLookAndFeel.VISIBLE_PROPERTY, jdkSevenFixListener );
            }
        }
    }

    /**
     * Uninstalls UI from the specified component.
     *
     * @param c component with this UI
     */
    @Override
    public void uninstallUI ( final JComponent c )
    {
        // Removing listeners
        popupMenu.removePropertyChangeListener ( WebLookAndFeel.VISIBLE_PROPERTY, popupMenuTypeUpdater );
        popupMenuTypeUpdater = null;
        popupMenu.removePropertyChangeListener ( WebLookAndFeel.ORIENTATION_PROPERTY, orientationChangeListener );
        orientationChangeListener = null;
        if ( transparent )
        {
            popupMenu.removePropertyChangeListener ( WebLookAndFeel.VISIBLE_PROPERTY, visibilityChangeListener );
            visibilityChangeListener = null;
        }
        else
        {
            // Workaround for menu with non-opaque parent window
            if ( SystemUtils.isJava7orAbove () )
            {
                popupMenu.removePropertyChangeListener ( WebLookAndFeel.VISIBLE_PROPERTY, jdkSevenFixListener );
                jdkSevenFixListener = null;
            }
        }

        // Uninstalling applied skin
        StyleManager.removeSkin ( popupMenu );

        super.uninstallUI ( c );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getStyleId ()
    {
        return styleId;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setStyleId ( final String id )
    {
        this.styleId = id;
        StyleManager.applySkin ( popupMenu );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Shape provideShape ()
    {
        return LafUtils.getWebBorderShape ( popupMenu, 0, StyleConstants.smallRound );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updateBorder ()
    {
        LafUtils.updateBorder ( popupMenu, margin, painter );
    }

    /**
     * Returns popup menu content margin.
     *
     * @return popup menu content margin
     */
    public Insets getMargin ()
    {
        return margin;
    }

    /**
     * Sets popup menu content margin.
     *
     * @param margin new popup menu content margin
     */
    public void setMargin ( final Insets margin )
    {
        this.margin = margin;
        updateBorder ();
    }

    /**
     * Returns spacing between menubar popup menus.
     *
     * @return spacing between menubar popup menus
     */
    public int getMenuSpacing ()
    {
        return menuSpacing;
    }

    /**
     * Sets spacing between menubar popup menus.
     *
     * @param spacing new spacing between menubar popup menus
     */
    public void setMenuSpacing ( final int spacing )
    {
        this.menuSpacing = spacing;
        if ( painter != null )
        {
            painter.setMenuSpacing ( spacing );
        }
    }

    /**
     * Returns whether popup menu should try to fix its initial location when displayed or not.
     *
     * @return true if popup menu should try to fix its initial location when displayed, false otherwise
     */
    public boolean isFixLocation ()
    {
        return fixLocation;
    }

    /**
     * Sets whether popup menu should try to fix its initial location when displayed or not.
     *
     * @param fix whether popup menu should try to fix its initial location when displayed or not
     */
    public void setFixLocation ( final boolean fix )
    {
        this.fixLocation = fix;
        if ( painter != null )
        {
            painter.setFixLocation ( fix );
        }
    }

    /**
     * Assists popup menu to allow it choose the best position relative to invoker.
     * Its value nullified right after first usage to avoid popup menu display issues in future.
     *
     * @param way approximate popup menu display way
     */
    public void setPopupMenuWay ( final PopupMenuWay way )
    {
        this.popupMenuWay = way;
        if ( painter != null )
        {
            painter.setPopupMenuWay ( way );
        }
    }

    /**
     * Returns popup style.
     *
     * @return popup style
     */
    public PopupStyle getPopupStyle ()
    {
        final PopupStyle popupStyle = StyleManager.getPainterPropertyValue ( popupMenu, "popupStyle" );
        return popupStyle != null ? popupStyle : WebPopupMenuStyle.popupStyle;
    }

    /**
     * Sets popup style.
     *
     * @param style new popup style
     */
    public void setPopupStyle ( final PopupStyle style )
    {
        StyleManager.setCustomPainterProperty ( popupMenu, "popupStyle", style );
    }

    /**
     * Returns popup border color.
     *
     * @return popup border color
     */
    public Color getBorderColor ()
    {
        final Color borderColor = StyleManager.getPainterPropertyValue ( popupMenu, "borderColor" );
        return borderColor != null ? borderColor : WebPopupMenuStyle.borderColor;
    }

    /**
     * Sets popup border color.
     *
     * @param color new popup border color
     */
    public void setBorderColor ( final Color color )
    {
        StyleManager.setCustomPainterProperty ( popupMenu, "borderColor", color );
    }

    /**
     * Returns decoration corners rounding.
     *
     * @return decoration corners rounding
     */
    public int getRound ()
    {
        final Integer round = StyleManager.getPainterPropertyValue ( popupMenu, "round" );
        return round != null ? round : WebPopupMenuStyle.round;
    }

    /**
     * Sets decoration corners rounding.
     *
     * @param round decoration corners rounding
     */
    public void setRound ( final int round )
    {
        StyleManager.setCustomPainterProperty ( popupMenu, "round", round );
    }

    /**
     * Returns decoration shade width.
     *
     * @return decoration shade width
     */
    public int getShadeWidth ()
    {
        final Integer shadeWidth = StyleManager.getPainterPropertyValue ( popupMenu, "shadeWidth" );
        return shadeWidth != null ? shadeWidth : WebPopupMenuStyle.shadeWidth;
    }

    /**
     * Sets decoration shade width.
     *
     * @param shadeWidth decoration shade width
     */
    public void setShadeWidth ( final int shadeWidth )
    {
        StyleManager.setCustomPainterProperty ( popupMenu, "shadeWidth", shadeWidth );
    }

    /**
     * Returns popup shade transparency.
     *
     * @return popup shade transparency
     */
    public float getShadeTransparency ()
    {
        final Float shadeTransparency = StyleManager.getPainterPropertyValue ( popupMenu, "shadeTransparency" );
        return shadeTransparency != null ? shadeTransparency : WebPopupMenuStyle.shadeTransparency;
    }

    /**
     * Sets popup shade transparency.
     *
     * @param opacity new popup shade transparency
     */
    public void setShadeTransparency ( final float opacity )
    {
        StyleManager.setCustomPainterProperty ( popupMenu, "shadeTransparency", opacity );
    }

    /**
     * Returns popup dropdown style corner width.
     *
     * @return popup dropdown style corner width
     */
    public int getCornerWidth ()
    {
        final Integer cornerWidth = StyleManager.getPainterPropertyValue ( popupMenu, "cornerWidth" );
        return cornerWidth != null ? cornerWidth : WebPopupMenuStyle.cornerWidth;
    }

    /**
     * Sets popup dropdown style corner width.
     *
     * @param width popup dropdown style corner width
     */
    public void setCornerWidth ( final int width )
    {
        StyleManager.setCustomPainterProperty ( popupMenu, "cornerWidth", width );
    }

    /**
     * Returns dropdown corner alignment.
     *
     * @return dropdown corner alignment
     */
    public int getCornerAlignment ()
    {
        final Integer cornerAlignment = StyleManager.getPainterPropertyValue ( popupMenu, "cornerAlignment" );
        return cornerAlignment != null ? cornerAlignment : WebPopupMenuStyle.cornerAlignment;
    }

    /**
     * Sets dropdown corner alignment.
     *
     * @param cornerAlignment dropdown corner alignment
     */
    public void setCornerAlignment ( final int cornerAlignment )
    {
        StyleManager.setCustomPainterProperty ( popupMenu, "cornerAlignment", cornerAlignment );
    }

    /**
     * Returns popup background transparency.
     *
     * @return popup background transparency
     */
    public float getTransparency ()
    {
        final Float transparency = StyleManager.getPainterPropertyValue ( popupMenu, "transparency" );
        return transparency != null ? transparency : WebPopupMenuStyle.transparency;
    }

    /**
     * Sets popup background transparency.
     *
     * @param transparency popup background transparency
     */
    public void setTransparency ( final float transparency )
    {
        StyleManager.setCustomPainterProperty ( popupMenu, "transparency", transparency );
    }

    /**
     * Returns popup menu painter.
     *
     * @return popup menu painter
     */
    public Painter getPainter ()
    {
        return LafUtils.getAdaptedPainter ( painter );
    }

    /**
     * Sets popup menu painter.
     * Pass null to remove popup menu painter.
     *
     * @param painter new popup menu painter
     */
    public void setPainter ( final Painter painter )
    {
        // Creating adaptive painter if required
        final PopupMenuPainter properPainter =
                LafUtils.getProperPainter ( painter, PopupMenuPainter.class, AdaptivePopupMenuPainter.class );

        // Properly updating painter
        PainterSupport.uninstallPainter ( popupMenu, this.painter );
        final Painter oldPainter = this.painter;
        this.painter = properPainter;
        applyPainterSettings ( properPainter );
        PainterSupport.installPainter ( popupMenu, properPainter );

        // Firing painter change event
        // This is made using reflection because required method is protected within Component class
        LafUtils.firePainterChanged ( popupMenu, oldPainter, properPainter );
    }

    /**
     * Applies UI settings to this specific painter.
     *
     * @param painter popup menu painter
     */
    protected void applyPainterSettings ( final PopupMenuPainter painter )
    {
        if ( painter != null )
        {
            // UI settings
            painter.setMenuSpacing ( menuSpacing );
            painter.setFixLocation ( fixLocation );

            // Runtime variables
            painter.setTransparent ( transparent );
            painter.setPopupMenuWay ( popupMenuWay );
        }
    }

    /**
     * Returns the {@code Popup} that will be responsible for displaying the {@code JPopupMenu}.
     * Also does necessary modifications to popup coordinates in case they are actually required.
     *
     * @param popup JPopupMenu requesting Popup
     * @param x     screen x location Popup is to be shown at
     * @param y     screen y location Popup is to be shown at
     * @return Popup that will show the JPopupMenu
     */
    @Override
    public Popup getPopup ( final JPopupMenu popup, int x, int y )
    {
        if ( painter != null )
        {
            // Updating painter settings
            painter.setMenuSpacing ( menuSpacing );
            painter.setFixLocation ( fixLocation );
            painter.setPopupMenuWay ( popupMenuWay );

            // Preparing popup menu for display
            final Point fixed = painter.preparePopupMenu ( popup, popup.getInvoker (), x, y );
            if ( fixLocation )
            {
                x = fixed.x;
                y = fixed.y;
            }

            // Resetting preferred popup menu display way
            popupMenuWay = null;
        }

        return super.getPopup ( popup, x, y );
    }

    /**
     * Paints popup menu decorations.
     * The whole painting process is delegated to installed painter class.
     *
     * @param g graphics context
     * @param c popup menu component
     */
    @Override
    public void paint ( final Graphics g, final JComponent c )
    {
        if ( painter != null )
        {
            painter.paint ( ( Graphics2D ) g, SwingUtils.size ( c ), c );
        }
    }
}
TOP

Related Classes of com.alee.laf.menu.WebPopupMenuUI

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.