All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pushingpixels.radiance.component.api.common.JCommandButton Maven / Gradle / Ivy

/*
 * Copyright (c) 2005-2021 Radiance Kirill Grouchnikov. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  o Neither the name of the copyright holder nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.pushingpixels.radiance.component.api.common;

import org.jdesktop.swingx.icon.RadianceIcon;
import org.pushingpixels.radiance.component.api.common.model.*;
import org.pushingpixels.radiance.component.api.common.popup.JCommandPopupMenu;
import org.pushingpixels.radiance.component.api.common.popup.JPopupPanel;
import org.pushingpixels.radiance.component.api.common.popup.PopupPanelCallback;
import org.pushingpixels.radiance.component.api.common.popup.PopupPanelManager;
import org.pushingpixels.radiance.component.api.common.popup.model.AbstractPopupMenuPresentationModel;
import org.pushingpixels.radiance.component.api.common.popup.model.CommandPopupMenuPresentationModel;
import org.pushingpixels.radiance.component.api.common.projection.CommandButtonProjection;
import org.pushingpixels.radiance.component.api.common.projection.CommandPopupMenuProjection;
import org.pushingpixels.radiance.component.api.common.projection.Projection;
import org.pushingpixels.radiance.component.api.ribbon.RibbonApplicationMenu;
import org.pushingpixels.radiance.component.api.ribbon.projection.RibbonApplicationMenuCommandButtonProjection;
import org.pushingpixels.radiance.component.internal.theming.common.ui.RadianceCommandButtonUI;
import org.pushingpixels.radiance.component.internal.ui.common.BasicCommandButtonUI;
import org.pushingpixels.radiance.component.internal.ui.common.CommandButtonUI;
import org.pushingpixels.radiance.component.internal.ui.ribbon.appmenu.RibbonApplicationMenuPanelProjection;
import org.pushingpixels.radiance.theming.api.RadianceThemingCortex;

import javax.accessibility.AccessibleContext;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.util.List;

/**
 * Command button. Note that while this class is a part of public API, it is highly
 * recommended to use the {@link Command} and {@link CommandButtonPresentationModel}
 * instances used to project the command button on screen for any dynamic manipulation
 * of the state.
 *
 * @author Kirill Grouchnikov
 */
public class JCommandButton extends RichTooltipManager.JTrackableComponent {
    public static final int DEFAULT_HORIZONTAL_ALIGNMENT = SwingConstants.CENTER;
    public static final double DEFAULT_GAP_SCALE_FACTOR = 1.0;

    /**
     * The UI class ID string.
     */
    public static final String uiClassID = "CommandButtonUI";

    private Projection projection;
    private Command command;
    private CommandButtonPresentationModel commandPresentation;

    /**
     * Associated icon.
     *
     * @see #setIcon(RadianceIcon)
     * @see #getIcon()
     */
    private RadianceIcon icon;

    /**
     * The button text.
     *
     * @see #setText(String)
     * @see #getText()
     */
    private String text;

    /**
     * The button action model.
     *
     * @see #getActionModel()
     * @see #setActionModel(ActionButtonModel)
     */
    private ActionButtonModel actionModel;

    /**
     * Additional text. It is up to the presentation layer to decide whether to display this
     * text (depending on the layout and available space).
     *
     * @see #setExtraText(String)
     * @see #getExtraText()
     */
    private String extraText;

    /**
     * Current presentation state of this button.
     *
     * @see #setPresentationState(CommandButtonPresentationState)
     * @see #getPresentationState()
     */
    private CommandButtonPresentationState presentationState;

    /**
     * The dimension of the icon of the associated command button in the
     * {@link CommandButtonPresentationState#FIT_TO_ICON} state.
     *
     * @see #getIconDimension()
     * @see #setIconDimension(int)
     */
    private int iconDimension;

    /**
     * Indication whether this button is flat.
     *
     * @see #setFlat(boolean)
     * @see #isFlat()
     */
    private boolean isFlat;

    /**
     * Horizontal alignment of the content.
     *
     * @see #setHorizontalAlignment(int)
     * @see #getHorizontalAlignment()
     */
    private int horizontalAlignment;

    /**
     * Scale factor for horizontal gaps.
     *
     * @see #setHGapScaleFactor(double)
     * @see #getHGapScaleFactor()
     */
    private double hgapScaleFactor;

    /**
     * Scale factor for vertical gaps.
     *
     * @see #setVGapScaleFactor(double)
     * @see #getVGapScaleFactor()
     */
    private double vgapScaleFactor;

    /**
     * Rich tooltip for the action area.
     *
     * @see #setActionRichTooltip(RichTooltip)
     * @see #getRichTooltip(MouseEvent)
     */
    private RichTooltip actionRichTooltip;

    /**
     * Location order kind for buttons placed in command button strips or for
     * buttons that need the visuals of segmented strips.
     *
     * @see #setLocationOrderKind(CommandButtonLocationOrderKind)
     * @see #getLocationOrderKind()
     */
    private CommandButtonLocationOrderKind locationOrderKind;

    /**
     * Action handler for the button.
     */
    private ActionHandler actionHandler;

    /**
     * Key tip for the action area.
     *
     * @see #setActionKeyTip(String)
     * @see #getActionKeyTip()
     */
    private String actionKeyTip;

    /**
     * Enumerates the available values for the location order kind. This is used
     * for buttons placed in command button strips or for buttons that need the
     * visuals of segmented strips.
     *
     * @author Kirill Grouchnikov
     */
    public enum CommandButtonLocationOrderKind {
        /**
         * Indicates that this button is the only button in the strip.
         */
        ONLY,

        /**
         * Indicates that this button is the first button in the strip.
         */
        FIRST,

        /**
         * Indicates that this button is in the middle of the strip.
         */
        MIDDLE,

        /**
         * Indicates that this button is the last button in the strip.
         */
        LAST
    }

    /**
     * Associated popup callback. May be null.
     *
     * @see #setPopupCallback(PopupPanelCallback)
     * @see #getPopupCallback()
     */
    private PopupPanelCallback popupCallback;

    /**
     * The command button kind of this button.
     *
     * @see #setCommandButtonKind(CommandButtonKind)
     * @see #getCommandButtonKind()
     */
    private CommandButtonKind commandButtonKind;

    /**
     * The popup orientation kind of this button.
     *
     * @see #setPopupOrientationKind(CommandButtonPresentationModel.PopupOrientationKind)
     * @see #getPopupOrientationKind()
     */
    private CommandButtonPresentationModel.PopupOrientationKind popupOrientationKind;

    /**
     * The popup horizontal gravity of this button.
     *
     * @see #setPopupHorizontalGravity(CommandButtonPresentationModel.PopupHorizontalGravity)
     * @see #getPopupHorizontalGravity()
     */
    private CommandButtonPresentationModel.PopupHorizontalGravity popupHorizontalGravity;

    /**
     * Indicates the auto-repeat action mode. When the button is not in the
     * auto-repeat action mode, the registered action listeners are activated
     * when the mouse is released (just as with the base {@link AbstractButton}
     * ). When the button is in auto-repeat mode, the registered action
     * listeners are activated when the mouse is pressed. In addition, if the
     * mouse is still pressed after {@link #getAutoRepeatInitialInterval()}, the
     * action listeners will be activated every
     * {@link #getAutoRepeatSubsequentInterval()} until the button is disabled
     * or the mouse is released.
     *
     * @see #autoRepeatInitialInterval
     * @see #autoRepeatSubsequentInterval
     * @see #setAutoRepeatAction(boolean)
     * @see #isAutoRepeatAction()
     */
    private boolean isAutoRepeatAction;

    /**
     * The initial interval for invoking the registered action listeners in the
     * auto-repeat action mode.
     *
     * @see #isAutoRepeatAction
     * @see #autoRepeatSubsequentInterval
     * @see #getAutoRepeatInitialInterval()
     * @see #setAutoRepeatActionIntervals(int, int)
     */
    private int autoRepeatInitialInterval;

    /**
     * The subsequent interval for invoking the registered action listeners in
     * the auto-repeat action mode.
     *
     * @see #isAutoRepeatAction
     * @see #autoRepeatInitialInterval
     * @see #getAutoRepeatSubsequentInterval()
     * @see #setAutoRepeatActionIntervals(int, int)
     */
    private int autoRepeatSubsequentInterval;

    /**
     * Indicates that rollover should result in firing the action. Used in
     * conjunction with the {@link #isAutoRepeatAction} can model quick pan
     * buttons such as breadcrumb bar scrollers.
     *
     * @see #setFireActionOnRollover(boolean)
     * @see #isFireActionOnRollover()
     */
    private boolean isFireActionOnRollover;

    /**
     * Popup model of this button.
     *
     * @see #setPopupModel(PopupButtonModel)
     * @see #getPopupModel()
     */
    private PopupButtonModel popupModel;

    /**
     * Default popup handler for this button.
     */
    private PopupHandler popupHandler;

    /**
     * Rich tooltip for the popup area of this button.
     *
     * @see #setPopupRichTooltip(RichTooltip)
     * @see #getRichTooltip(MouseEvent)
     */
    private RichTooltip popupRichTooltip;

    /**
     * Key tip for the popup area of this button.
     *
     * @see #setPopupKeyTip(String)
     * @see #getPopupKeyTip()
     */
    private String popupKeyTip;

    /**
     * Enumerates the available command button kinds.
     *
     * @author Kirill Grouchnikov
     */
    public enum CommandButtonKind {
        /**
         * Command button that has only action area.
         */
        ACTION_ONLY(true, false),

        /**
         * Command button that has only popup area.
         */
        POPUP_ONLY(false, true),

        /**
         * Command button that has both action and popup areas, with the main
         * text click activating the action.
         */
        ACTION_AND_POPUP_MAIN_ACTION(true, true),

        /**
         * Command button that has both action and popup areas, with the main
         * text click activating the popup.
         */
        ACTION_AND_POPUP_MAIN_POPUP(true, true);

        /**
         * true if the command button kind has an action.
         */
        private boolean hasAction;

        /**
         * true if the command button kind has a popup.
         */
        private boolean hasPopup;

        /**
         * Constructs a new command button kind.
         *
         * @param hasAction Indicates whether the command button kind has an action.
         * @param hasPopup  Indicates whether the command button kind has a popup.
         */
        CommandButtonKind(boolean hasAction, boolean hasPopup) {
            this.hasAction = hasAction;
            this.hasPopup = hasPopup;
        }

        /**
         * Returns indication whether this command button kind has an action.
         *
         * @return true if the command button kind has an action,
         * false otherwise.
         */
        public boolean hasAction() {
            return hasAction;
        }

        /**
         * Returns indication whether this command button kind has a popup.
         *
         * @return true if the command button kind has a popup,
         * false otherwise.
         */
        public boolean hasPopup() {
            return hasPopup;
        }
    }

    /**
     * Extension of the default button model that supports the
     * {@link PopupButtonModel} interface.
     *
     * @author Kirill Grouchnikov
     */
    private static class DefaultPopupButtonModel extends DefaultButtonModel implements PopupButtonModel {
        /**
         * Identifies the "popup showing" bit in the bitmask, which indicates
         * that the visibility status of the associated popup.
         */
        public final static int POPUP_SHOWING = 1 << 8;

        private JCommandButton commandButton;

        /**
         * Creates a new default popup button model.
         */
        public DefaultPopupButtonModel(JCommandButton commandButton) {
            super();
            this.commandButton = commandButton;
        }

        @Override
        public void addPopupActionListener(PopupActionListener l) {
            listenerList.add(PopupActionListener.class, l);
        }

        @Override
        public void removePopupActionListener(PopupActionListener l) {
            listenerList.remove(PopupActionListener.class, l);
        }

        /**
         * Notifies all listeners that have registered interest for notification
         * on this event type.
         *
         * @param e the ActionEvent to deliver to listeners
         * @see EventListenerList
         */
        protected void firePopupActionPerformed(ActionEvent e) {
            // Guaranteed to return a non-null array
            Object[] listeners = listenerList.getListenerList();
            // Process the listeners last to first, notifying
            // those that are interested in this event
            for (int i = listeners.length - 2; i >= 0; i -= 2) {
                if (listeners[i] == PopupActionListener.class) {
                    ((PopupActionListener) listeners[i + 1]).actionPerformed(e);
                }
            }
        }

        @Override
        public void setPressed(boolean b) {
            if ((isPressed() == b) || !isEnabled()) {
                return;
            }

            if (b) {
                stateMask |= PRESSED;
            } else {
                stateMask &= ~PRESSED;
            }

            if (isPressed() && isArmed()) {
                // fire the popup action on button press and not on button
                // release - like the comboboxes
                int modifiers = 0;
                AWTEvent currentEvent = EventQueue.getCurrentEvent();
                if (currentEvent instanceof InputEvent) {
                    modifiers = ((InputEvent) currentEvent).getModifiersEx();
                } else if (currentEvent instanceof ActionEvent) {
                    modifiers = ((ActionEvent) currentEvent).getModifiers();
                }
                this.commandButton.getUI().setInnerFocusOnAction(false);
                firePopupActionPerformed(new ActionEvent(this,
                        ActionEvent.ACTION_PERFORMED, getActionCommand(),
                        EventQueue.getMostRecentEventTime(), modifiers));
            }

            fireStateChanged();
        }

        @Override
        public boolean isPopupShowing() {
            return (stateMask & POPUP_SHOWING) != 0;
        }

        @Override
        public void setPopupShowing(boolean b) {
            // System.out.println(this.isPopupShowing() + "-->" + b);
            if (this.isPopupShowing() == b) {
                return;
            }

            if (b) {
                stateMask |= POPUP_SHOWING;
            } else {
                stateMask &= ~POPUP_SHOWING;
            }

            fireStateChanged();
        }
    }

    @SuppressWarnings("unchecked")
    public JCommandButton(Projection projection) {
        this.projection = projection;
        this.command = projection.getContentModel();
        this.commandPresentation = projection.getPresentationModel();

        this.setText(command.getText());
        this.setExtraText(command.getExtraText());

        this.setIcon((command.getIconFactory() != null)
                ? command.getIconFactory().createNewIcon()
                : null);
        RadianceThemingCortex.ComponentScope.setIconFilterStrategies(this,
                commandPresentation.getActiveIconFilterStrategy(),
                commandPresentation.getEnabledIconFilterStrategy(),
                commandPresentation.getDisabledIconFilterStrategy());

        boolean hasAction = (command.getAction() != null);

        if (hasAction) {
            this.addCommandListener(command.getAction());
            this.setActionRichTooltip(command.getActionRichTooltip());
            this.setActionKeyTip(commandPresentation.getActionKeyTip());
        }

        if (!commandPresentation.isToDismissPopupsOnActivation()) {
            this.putClientProperty(BasicCommandButtonUI.DONT_DISPOSE_POPUPS, Boolean.TRUE);
        }

        this.setPresentationState(commandPresentation.getPresentationState());
        this.setHorizontalAlignment(commandPresentation.getHorizontalAlignment());
        this.setHGapScaleFactor(commandPresentation.getHorizontalGapScaleFactor());
        this.setVGapScaleFactor(commandPresentation.getVerticalGapScaleFactor());
        this.setFlat(commandPresentation.isFlat());
        this.setFocusable(commandPresentation.isFocusable());
        if (commandPresentation.getIconDimension() != null) {
            this.setIconDimension(commandPresentation.getIconDimension());
        }

        this.actionHandler = new ActionHandler();
        this.setOpaque(false);

        ActionButtonModel actionButtonModel = new ActionRepeatableButtonModel(this);
        actionButtonModel.setEnabled(command.isActionEnabled());
        actionButtonModel.setSelected(command.isToggleSelected());
        this.setActionModel(actionButtonModel);

        // important - handler creation must be done before setting
        // the popup model so that it can be registered to track the
        // changes
        this.popupHandler = new PopupHandler();
        PopupButtonModel popupButtonModel = new DefaultPopupButtonModel(this);
        popupButtonModel.setEnabled(command.isSecondaryEnabled());
        this.setPopupModel(popupButtonModel);

        boolean hasPopup = (command.getSecondaryContentModel() != null);

        if (hasPopup) {
            if (command.getSecondaryContentModel() != null) {
                CommandMenuContentModel popupMenuContentModel = command.getSecondaryContentModel();
                AbstractPopupMenuPresentationModel popupMenuPresentationModel =
                        commandPresentation.getPopupMenuPresentationModel();
                if (popupMenuContentModel instanceof RibbonApplicationMenu) {
                    RibbonApplicationMenuCommandButtonProjection ribbonApplicationMenuProjection =
                            (RibbonApplicationMenuCommandButtonProjection) this.projection;
                    if (popupMenuPresentationModel == null) {
                        popupMenuPresentationModel = CommandPopupMenuPresentationModel.builder().build();
                    }
                    RibbonApplicationMenuPanelProjection menuPanelProjection =
                            new RibbonApplicationMenuPanelProjection(
                                    (RibbonApplicationMenu) popupMenuContentModel,
                                    (CommandPopupMenuPresentationModel) popupMenuPresentationModel);
                    menuPanelProjection.setCommandOverlays(
                            ribbonApplicationMenuProjection.getCommandOverlays());
                    menuPanelProjection.setSecondaryLevelCommandPresentationState(
                            ribbonApplicationMenuProjection.getSecondaryLevelCommandPresentationState());
                    this.setPopupCallback(commandButton -> menuPanelProjection.buildComponent());
                } else if (popupMenuContentModel != null) {
                    CommandButtonProjection commandProjection =
                            (CommandButtonProjection) this.projection;
                    if (popupMenuPresentationModel == null) {
                        popupMenuPresentationModel = CommandPopupMenuPresentationModel.builder().build();
                    }
                    CommandPopupMenuProjection commandPopupMenuProjection =
                            new CommandPopupMenuProjection(popupMenuContentModel,
                                    (CommandPopupMenuPresentationModel) popupMenuPresentationModel);
                    commandPopupMenuProjection.setCommandOverlays(
                            this.projection.getCommandOverlays());
                    if (commandProjection.getPopupMenuSupplier() != null) {
                        commandPopupMenuProjection.setComponentSupplier(
                                (Projection.ComponentSupplier) commandProjection.getPopupMenuSupplier());
                    }
                    if (commandProjection.getPopupMenuCustomizer() != null) {
                        commandPopupMenuProjection.setComponentCustomizer(
                                (Projection.ComponentCustomizer) commandProjection.getPopupMenuCustomizer());
                    }
                    this.setPopupCallback(commandButton -> {
                        JPopupPanel result = commandPopupMenuProjection.buildComponent();
                        result.applyComponentOrientation(JCommandButton.this.getComponentOrientation());
                        return result;
                    });
                }
            }
            this.setPopupRichTooltip(command.getSecondaryRichTooltip());
            this.setPopupKeyTip(commandPresentation.getPopupKeyTip());
        }

        if (hasAction && hasPopup) {
            this.setCommandButtonKind(commandPresentation.isTextClickAction()
                    ? JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION
                    : JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_POPUP);
        } else if (hasPopup) {
            this.setCommandButtonKind(JCommandButton.CommandButtonKind.POPUP_ONLY);
        } else {
            this.setCommandButtonKind(JCommandButton.CommandButtonKind.ACTION_ONLY);
        }

        if (commandPresentation.isAutoRepeatAction()) {
            this.setAutoRepeatAction(true);
            if (commandPresentation.hasAutoRepeatIntervalsSet()) {
                this.setAutoRepeatActionIntervals(
                        commandPresentation.getAutoRepeatInitialInterval(),
                        commandPresentation.getAutoRepeatSubsequentInterval());
            }
        }

        this.setFireActionOnRollover(commandPresentation.isFireActionOnRollover());
        this.getActionModel().setFireActionOnPress(commandPresentation.isFireActionOnPress());

        this.setPopupOrientationKind(commandPresentation.getPopupOrientationKind());
        this.setPopupHorizontalGravity(commandPresentation.getPopupHorizontalGravity());

        if (command.getActionPreview() != null) {
            this.getActionModel().addChangeListener(new ChangeListener() {
                boolean wasRollover = false;

                @Override
                public void stateChanged(ChangeEvent e) {
                    boolean isRollover = getActionModel().isRollover();
                    if (wasRollover && !isRollover) {
                        command.getActionPreview().onCommandPreviewCanceled(command);
                    }
                    if (!wasRollover && isRollover) {
                        command.getActionPreview().onCommandPreviewActivated(command);
                    }
                    wasRollover = isRollover;
                }
            });
        }

        this.updateUI();
    }

    /**
     * Returns the UI delegate for this button.
     *
     * @return The UI delegate for this button.
     */
    public CommandButtonUI getUI() {
        return (CommandButtonUI) ui;
    }

    public CommandButtonProjection getProjection() {
        return (CommandButtonProjection) this.projection;
    }

    /**
     * Sets new presentation state for this button. Fires a
     * presentationState property change event.
     *
     * @param state New presentation state.
     * @see #getPresentationState()
     */
    public void setPresentationState(CommandButtonPresentationState state) {
        CommandButtonPresentationState old = this.presentationState;
        this.presentationState = state;

        this.firePropertyChange("presentationState", old, this.presentationState);
    }

    /**
     * Returns the associated icon.
     *
     * @return The associated icon.
     * @see #setIcon(RadianceIcon)
     */
    public RadianceIcon getIcon() {
        return icon;
    }

    /**
     * Sets new icon for this button. Fires an icon property change
     * event.
     *
     * @param defaultIcon New default icon for this button.
     * @see #getIcon()
     */
    public void setIcon(RadianceIcon defaultIcon) {
        RadianceIcon oldValue = this.icon;
        this.icon = defaultIcon;

        firePropertyChange("icon", oldValue, defaultIcon);
        if (defaultIcon != oldValue) {
            if (defaultIcon == null || oldValue == null
                    || defaultIcon.getIconWidth() != oldValue.getIconWidth()
                    || defaultIcon.getIconHeight() != oldValue.getIconHeight()) {
                revalidate();
            }
            repaint();
        }
    }

    /**
     * Return the current presentation state of this button.
     *
     * @return The current presentation state of this button.
     * @see #setPresentationState(CommandButtonPresentationState)
     */
    public CommandButtonPresentationState getPresentationState() {
        return this.presentationState;
    }

    /**
     * Returns the extra text of this button.
     *
     * @return Extra text of this button.
     * @see #setExtraText(String)
     */
    public String getExtraText() {
        return this.extraText;
    }

    /**
     * Sets the extra text for this button. Fires an extraText
     * property change event.
     *
     * @param extraText Extra text for this button.
     * @see #getExtraText()
     */
    public void setExtraText(String extraText) {
        String oldValue = this.extraText;
        this.extraText = extraText;
        firePropertyChange("extraText", oldValue, extraText);

        if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
                    AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
                    oldValue, extraText);
        }
        if ((extraText == null) || (oldValue == null) || !extraText.equals(oldValue)) {
            revalidate();
            repaint();
        }
    }

    /**
     * Returns the text of this button.
     *
     * @return The text of this button.
     * @see #setText(String)
     */
    public String getText() {
        return this.text;
    }

    /**
     * Sets the new text for this button. Fires a text property
     * change event.
     *
     * @param text The new text for this button.
     * @see #getText()
     */
    public void setText(String text) {
        String oldValue = this.text;
        this.text = text;
        firePropertyChange("text", oldValue, text);

        if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
                    AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
                    oldValue, text);
        }
        if ((text == null) || (oldValue == null) || !text.equals(oldValue)) {
            revalidate();
            repaint();
        }
    }

    /**
     * Updates the dimension of the icon of the associated command button in the
     * {@link CommandButtonPresentationState#FIT_TO_ICON} state. Fires an
     * iconDimension property change event.
     *
     * @param dimension New dimension of the icon of the associated command button in
     *                  the {@link CommandButtonPresentationState#FIT_TO_ICON} state.
     * @see #getIconDimension()
     */
    public void setIconDimension(int dimension) {
        if (this.iconDimension != dimension) {
            int old = this.iconDimension;
            this.iconDimension = dimension;
            this.firePropertyChange("iconDimension", old, this.iconDimension);
        }
    }

    /**
     * Returns the dimension of the icon of the associated command button in the
     * {@link CommandButtonPresentationState#FIT_TO_ICON} state.
     *
     * @return The dimension of the icon of the associated command button in the
     * {@link CommandButtonPresentationState#FIT_TO_ICON} state.
     * @see #setIconDimension(int)
     */
    public int getIconDimension() {
        return this.iconDimension;
    }

    /**
     * Returns indication whether this button has flat appearance.
     *
     * @return true if this button has flat appearance,
     * false otherwise.
     * @see #setFlat(boolean)
     */
    public boolean isFlat() {
        return this.isFlat;
    }

    /**
     * Sets the flat appearance of this button. Fires a flat
     * property change event.
     *
     * @param isFlat If true, this button will have flat appearance,
     *               otherwise this button will not have flat appearance.
     * @see #isFlat()
     */
    public void setFlat(boolean isFlat) {
        boolean old = this.isFlat;
        this.isFlat = isFlat;
        if (old != this.isFlat) {
            this.firePropertyChange("flat", old, this.isFlat);
        }

        if (old != isFlat) {
            repaint();
        }
    }

    /**
     * Returns the action model for this button.
     *
     * @return The action model for this button.
     * @see #setActionModel(ActionButtonModel)
     */
    public ActionButtonModel getActionModel() {
        return this.actionModel;
    }

    /**
     * Sets the new action model for this button. Fires an
     * actionModel property change event.
     *
     * @param newModel The new action model for this button.
     * @see #getActionModel()
     */
    public void setActionModel(ActionButtonModel newModel) {
        ButtonModel oldModel = getActionModel();

        if (oldModel != null) {
            oldModel.removeChangeListener(this.actionHandler);
            oldModel.removeActionListener(this.actionHandler);
        }

        actionModel = newModel;

        if (newModel != null) {
            newModel.addChangeListener(this.actionHandler);
            newModel.addActionListener(this.actionHandler);
        }

        firePropertyChange("actionModel", oldModel, newModel);
        if (newModel != oldModel) {
            revalidate();
            repaint();
        }
    }

    /**
     * Adds the specified command listener to this button.
     *
     * @param l Command listener to add.
     * @see #removeCommandListener(CommandAction)
     */
    public void addCommandListener(CommandAction l) {
        this.listenerList.add(CommandAction.class, l);
    }

    /**
     * Removes the specified command listener from this button.
     *
     * @param l Command listener to remove.
     * @see #addCommandListener(CommandAction)
     */
    public void removeCommandListener(CommandAction l) {
        this.listenerList.remove(CommandAction.class, l);
    }

    /**
     * Adds the specified change listener to this button.
     *
     * @param l Change listener to add.
     * @see #removeChangeListener(ChangeListener)
     */
    public void addChangeListener(ChangeListener l) {
        this.listenerList.add(ChangeListener.class, l);
    }

    /**
     * Removes the specified change listener from this button.
     *
     * @param l Change listener to remove.
     * @see #addChangeListener(ChangeListener)
     */
    public void removeChangeListener(ChangeListener l) {
        this.listenerList.remove(ChangeListener.class, l);
    }

    /**
     * Default action handler for this button.
     *
     * @author Kirill Grouchnikov
     */
    class ActionHandler implements ActionListener, ChangeListener {
        public void stateChanged(ChangeEvent e) {
            fireStateChanged();
            repaint();
        }

        public void actionPerformed(ActionEvent event) {
            fireActionPerformed(event);
        }
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type. The event instance is lazily created.
     *
     * @see EventListenerList
     */
    protected void fireStateChanged() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        ChangeEvent ce = new ChangeEvent(this);
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == ChangeListener.class) {
                // Lazily create the event:
                ((ChangeListener) listeners[i + 1]).stateChanged(ce);
            }
        }
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type. The event instance is lazily created using the
     * event parameter.
     *
     * @param event the ActionEvent object
     * @see EventListenerList
     */
    protected void fireActionPerformed(ActionEvent event) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        CommandActionEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == CommandAction.class) {
                // Lazily create the event:
                if (e == null) {
                    String actionCommand = event.getActionCommand();
                    e = new CommandActionEvent(this, ActionEvent.ACTION_PERFORMED, this.command,
                            actionCommand, event.getWhen(), event.getModifiers());
                }
                ((CommandAction) listeners[i + 1]).commandActivated(e);
            }
        }

        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == ActionListener.class) {
                // Lazily create the event:
                if (e == null) {
                    String actionCommand = event.getActionCommand();
                    e = new CommandActionEvent(this, ActionEvent.ACTION_PERFORMED, this.command,
                            actionCommand, event.getWhen(), event.getModifiers());
                }
                ((ActionListener) listeners[i + 1]).actionPerformed(e);
            }
        }
    }

    /**
     * Sets new horizontal alignment for the content of this button. Fires a
     * horizontalAlignment property change event.
     *
     * @param alignment New horizontal alignment for the content of this button.
     * @see #getHorizontalAlignment()
     */
    public void setHorizontalAlignment(int alignment) {
        if (alignment == this.horizontalAlignment) {
            return;
        }
        int oldValue = this.horizontalAlignment;
        this.horizontalAlignment = alignment;
        firePropertyChange("horizontalAlignment", oldValue, this.horizontalAlignment);
        repaint();
    }

    /**
     * Returns the horizontal alignment for the content of this button.
     *
     * @return The horizontal alignment for the content of this button.
     * @see #setHorizontalAlignment(int)
     */
    public int getHorizontalAlignment() {
        return this.horizontalAlignment;
    }

    /**
     * Sets new horizontal gap scale factor for the content of this button.
     * Fires an hgapScaleFactor property change event.
     *
     * @param hgapScaleFactor New horizontal gap scale factor for the content of this
     *                        button.
     * @see #getHGapScaleFactor()
     * @see #setVGapScaleFactor(double)
     * @see #setGapScaleFactor(double)
     */
    public void setHGapScaleFactor(double hgapScaleFactor) {
        if (hgapScaleFactor == this.hgapScaleFactor)
            return;
        double oldValue = this.hgapScaleFactor;
        this.hgapScaleFactor = hgapScaleFactor;
        firePropertyChange("hgapScaleFactor", oldValue, this.hgapScaleFactor);
        if (this.hgapScaleFactor != oldValue) {
            revalidate();
            repaint();
        }
    }

    /**
     * Sets new vertical gap scale factor for the content of this button. Fires
     * a vgapScaleFactor property change event.
     *
     * @param vgapScaleFactor New vertical gap scale factor for the content of this button.
     * @see #getVGapScaleFactor()
     * @see #setHGapScaleFactor(double)
     * @see #setGapScaleFactor(double)
     */
    public void setVGapScaleFactor(double vgapScaleFactor) {
        if (vgapScaleFactor == this.vgapScaleFactor)
            return;
        double oldValue = this.vgapScaleFactor;
        this.vgapScaleFactor = vgapScaleFactor;
        firePropertyChange("vgapScaleFactor", oldValue, this.vgapScaleFactor);
        if (this.vgapScaleFactor != oldValue) {
            revalidate();
            repaint();
        }
    }

    /**
     * Sets new gap scale factor for the content of this button.
     *
     * @param gapScaleFactor New gap scale factor for the content of this button.
     * @see #getHGapScaleFactor()
     * @see #getVGapScaleFactor()
     */
    public void setGapScaleFactor(double gapScaleFactor) {
        setHGapScaleFactor(gapScaleFactor);
        setVGapScaleFactor(gapScaleFactor);
    }

    /**
     * Returns the horizontal gap scale factor for the content of this button.
     *
     * @return The horizontal gap scale factor for the content of this button.
     * @see #setHGapScaleFactor(double)
     * @see #setGapScaleFactor(double)
     * @see #getVGapScaleFactor()
     */
    public double getHGapScaleFactor() {
        return this.hgapScaleFactor;
    }

    /**
     * Returns the vertical gap scale factor for the content of this button.
     *
     * @return The vertical gap scale factor for the content of this button.
     * @see #setVGapScaleFactor(double)
     * @see #setGapScaleFactor(double)
     * @see #getHGapScaleFactor()
     */
    public double getVGapScaleFactor() {
        return this.vgapScaleFactor;
    }

    /**
     * Programmatically perform an action "click". This does the same thing as
     * if the user had pressed and released the action area of the button.
     */
    public void doActionClick() {
        Dimension size = getSize();
        ButtonModel actionModel = this.getActionModel();
        actionModel.setArmed(true);
        actionModel.setPressed(true);
        paintImmediately(new Rectangle(0, 0, size.width, size.height));
        try {
            Thread.sleep(100);
        } catch (InterruptedException ie) {
        }
        actionModel.setPressed(false);
        actionModel.setArmed(false);
    }

    /**
     * Sets the rich tooltip for the action area of this button.
     *
     * @param richTooltip Rich tooltip for the action area of this button.
     * @see #getRichTooltip(MouseEvent)
     */
    public void setActionRichTooltip(RichTooltip richTooltip) {
        // Don't remove the next line - we need to instantiate the RichTooltipManager
        // so that it starts tracking mouse events for displaying rich tooltips
        RichTooltipManager.sharedInstance();
        this.actionRichTooltip = richTooltip;
    }

    @Override
    public void setToolTipText(String text) {
        throw new UnsupportedOperationException("Use rich tooltip APIs");
    }

    /**
     * Returns the location order kind for buttons placed in command button
     * strips or for buttons that need the visuals of segmented strips.
     *
     * @return The location order kind for buttons placed in command button
     * strips or for buttons that need the visuals of segmented strips.
     * @see #setLocationOrderKind(CommandButtonLocationOrderKind)
     */
    public CommandButtonLocationOrderKind getLocationOrderKind() {
        return this.locationOrderKind;
    }

    /**
     * Sets the location order kind for buttons placed in command button strips
     * or for buttons that need the visuals of segmented strips. Fires a
     * locationOrderKind property change event.
     *
     * @param locationOrderKind The location order kind for buttons placed in command button
     *                          strips or for buttons that need the visuals of segmented
     *                          strips.
     * @see #getLocationOrderKind()
     */
    public void setLocationOrderKind(CommandButtonLocationOrderKind locationOrderKind) {
        CommandButtonLocationOrderKind old = this.locationOrderKind;
        if (old != locationOrderKind) {
            this.locationOrderKind = locationOrderKind;
            this.firePropertyChange("locationOrderKind", old, this.locationOrderKind);
        }
    }

    /**
     * Returns the key tip for the action area of this button.
     *
     * @return The key tip for the action area of this button.
     * @see #setActionKeyTip(String)
     */
    public String getActionKeyTip() {
        return this.actionKeyTip;
    }

    /**
     * Sets the key tip for the action area of this button. Fires an
     * actionKeyTip property change event.
     *
     * @param actionKeyTip The key tip for the action area of this button.
     * @see #getActionKeyTip()
     */
    public void setActionKeyTip(String actionKeyTip) {
        String old = this.actionKeyTip;
        this.actionKeyTip = actionKeyTip;
        this.firePropertyChange("actionKeyTip", old, this.actionKeyTip);
    }

    /**
     * Returns the command button kind of this button.
     *
     * @return Command button kind of this button.
     * @see #setCommandButtonKind(CommandButtonKind)
     */
    public CommandButtonKind getCommandButtonKind() {
        return this.commandButtonKind;
    }

    /**
     * Sets the kind for this button. Fires a commandButtonKind
     * property change event.
     *
     * @param commandButtonKind The new button kind.
     * @see #getCommandButtonKind()
     */
    public void setCommandButtonKind(CommandButtonKind commandButtonKind) {
        CommandButtonKind old = this.commandButtonKind;
        this.commandButtonKind = commandButtonKind;
        if (old != this.commandButtonKind) {
            firePropertyChange("commandButtonKind", old, this.commandButtonKind);
        }
    }

    /**
     * Returns the popup orientation kind of this button.
     *
     * @return Popup orientation kind of this button.
     * @see #setPopupOrientationKind(CommandButtonPresentationModel.PopupOrientationKind)
     */
    public CommandButtonPresentationModel.PopupOrientationKind getPopupOrientationKind() {
        return this.popupOrientationKind;
    }

    /**
     * Sets the popup orientation for this button. Fires a
     * popupOrientationKind property change event.
     *
     * @param popupOrientationKind The new popup orientation kind.
     * @see #getPopupOrientationKind()
     */
    public void setPopupOrientationKind(
            CommandButtonPresentationModel.PopupOrientationKind popupOrientationKind) {
        CommandButtonPresentationModel.PopupOrientationKind old = this.popupOrientationKind;
        this.popupOrientationKind = popupOrientationKind;
        if (old != this.popupOrientationKind) {
            firePropertyChange("popupOrientationKind", old, this.popupOrientationKind);
        }
    }

    /**
     * Returns the popup horizontal gravity of this button.
     *
     * @return Popup horizontal gravity of this button.
     * @see #setPopupHorizontalGravity(CommandButtonPresentationModel.PopupHorizontalGravity)
     */
    public CommandButtonPresentationModel.PopupHorizontalGravity getPopupHorizontalGravity() {
        return this.popupHorizontalGravity;
    }

    /**
     * Sets the popup horizontal gravity for this button. Fires a
     * popupHorizontalGravity property change event.
     *
     * @param popupHorizontalGravity The new popup horizontal gravity.
     * @see #getPopupHorizontalGravity()
     */
    public void setPopupHorizontalGravity(
            CommandButtonPresentationModel.PopupHorizontalGravity popupHorizontalGravity) {
        CommandButtonPresentationModel.PopupHorizontalGravity old = this.popupHorizontalGravity;
        this.popupHorizontalGravity = popupHorizontalGravity;
        if (old != this.popupHorizontalGravity) {
            firePropertyChange("popupHorizontalGravity", old, this.popupHorizontalGravity);
        }
    }

    @Override
    public void updateUI() {
        setUI(RadianceCommandButtonUI.createUI(this));
    }

    @Override
    public String getUIClassID() {
        return uiClassID;
    }

    /**
     * Returns the associated popup callback.
     *
     * @return The associated popup callback.
     * @see #setPopupCallback(PopupPanelCallback)
     */
    public PopupPanelCallback getPopupCallback() {
        return this.popupCallback;
    }

    /**
     * Sets new popup callback for this button.
     *
     * @param popupCallback New popup callback for this button.
     * @see #getPopupCallback()
     */
    public void setPopupCallback(PopupPanelCallback popupCallback) {
        this.popupCallback = popupCallback;

        if (command != null) {
            boolean hasAction = (command.getAction() != null);
            boolean hasPopup = (this.popupCallback != null);

            if (hasPopup) {
                this.setPopupRichTooltip(command.getSecondaryRichTooltip());
                this.setPopupKeyTip(commandPresentation.getPopupKeyTip());
            }

            if (hasAction && hasPopup) {
                this.setCommandButtonKind(commandPresentation.isTextClickAction()
                        ? JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION
                        : JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_POPUP);
            } else if (hasPopup) {
                this.setCommandButtonKind(JCommandButton.CommandButtonKind.POPUP_ONLY);
            } else {
                this.setCommandButtonKind(JCommandButton.CommandButtonKind.ACTION_ONLY);
            }
        }
    }

    /**
     * Sets the auto-repeat action indication.
     *
     * @param isAutoRepeatAction If true, pressing the button will activate
     *                           auto-repeat action mode. When the button is not in the
     *                           auto-repeat action mode, the registered action listeners are
     *                           activated when the mouse is released (just as with the base
     *                           {@link AbstractButton}). When the button is in auto-repeat
     *                           mode, the registered action listeners are activated when the
     *                           mouse is pressed. In addition, is the mouse is still pressed
     *                           after {@link #getAutoRepeatInitialInterval()}, the action
     *                           listeners will be activated every
     *                           {@link #getAutoRepeatSubsequentInterval()} until the button is
     *                           disabled or the mouse is released.
     * @see #setAutoRepeatActionIntervals(int, int)
     * @see #isAutoRepeatAction()
     */
    public void setAutoRepeatAction(boolean isAutoRepeatAction) {
        this.isAutoRepeatAction = isAutoRepeatAction;
    }

    /**
     * Sets the intervals for the auto-repeat action mode.
     *
     * @param initial    The initial interval for invoking the registered action
     *                   listeners in the auto-repeat action mode.
     * @param subsequent The subsequent interval for invoking the registered action
     *                   listeners in the auto-repeat action mode.
     * @see #setAutoRepeatAction(boolean)
     * @see #isAutoRepeatAction()
     * @see #getAutoRepeatInitialInterval()
     * @see #getAutoRepeatSubsequentInterval()
     */
    public void setAutoRepeatActionIntervals(int initial, int subsequent) {
        this.autoRepeatInitialInterval = initial;
        this.autoRepeatSubsequentInterval = subsequent;
    }

    /**
     * Returns indication whether the button is in auto-repeat action mode.
     *
     * @return true if the button is in auto-repeat action mode,
     * false otherwise.
     * @see #setAutoRepeatAction(boolean)
     * @see #setAutoRepeatActionIntervals(int, int)
     * @see #getAutoRepeatInitialInterval()
     * @see #getAutoRepeatSubsequentInterval()
     */
    public boolean isAutoRepeatAction() {
        return this.isAutoRepeatAction;
    }

    /**
     * Returns the initial interval for invoking the registered action listeners
     * in the auto-repeat action mode.
     *
     * @return The initial interval for invoking the registered action listeners
     * in the auto-repeat action mode.
     * @see #setAutoRepeatActionIntervals(int, int)
     * @see #setAutoRepeatAction(boolean)
     * @see #isAutoRepeatAction()
     * @see #getAutoRepeatSubsequentInterval()
     */
    public int getAutoRepeatInitialInterval() {
        return autoRepeatInitialInterval;
    }

    /**
     * Returns the subsequent interval for invoking the registered action
     * listeners in the auto-repeat action mode.
     *
     * @return The subsequent interval for invoking the registered action
     * listeners in the auto-repeat action mode.
     * @see #setAutoRepeatActionIntervals(int, int)
     * @see #setAutoRepeatAction(boolean)
     * @see #isAutoRepeatAction()
     * @see #getAutoRepeatInitialInterval()
     */
    public int getAutoRepeatSubsequentInterval() {
        return autoRepeatSubsequentInterval;
    }

    /**
     * Sets action-on-rollover mode. When this mode is on, button will fire
     * action events when it gets rollover (instead of press). Combine with
     * {@link #setAutoRepeatAction(boolean)} passing true to get
     * auto-repeat action fired on rollover (useful for quicker manipulation of
     * scroller buttons, for example).
     *
     * @param isFireActionOnRollover If true, the button is moved into the
     *                               action-on-rollover mode.
     * @see #isFireActionOnRollover()
     */
    public void setFireActionOnRollover(boolean isFireActionOnRollover) {
        this.isFireActionOnRollover = isFireActionOnRollover;
    }

    /**
     * Returns indication whether this button is in action-on-rollover mode.
     *
     * @return true if this button is in action-on-rollover mode,
     * false otherwise.
     * @see #setFireActionOnRollover(boolean)
     */
    public boolean isFireActionOnRollover() {
        return this.isFireActionOnRollover;
    }

    /**
     * Returns the popup model of this button.
     *
     * @return The popup model of this button.
     * @see #setPopupModel(PopupButtonModel)
     */
    public PopupButtonModel getPopupModel() {
        return this.popupModel;
    }

    /**
     * Sets the new popup model for this button. Fires a popupModel
     * property change event.
     *
     * @param newModel The new popup model for this button.
     * @see #getPopupModel()
     */
    public void setPopupModel(PopupButtonModel newModel) {
        PopupButtonModel oldModel = getPopupModel();

        if (oldModel != null) {
            oldModel.removeChangeListener(this.popupHandler);
            oldModel.removeActionListener(this.popupHandler);
        }

        this.popupModel = newModel;

        if (newModel != null) {
            newModel.addChangeListener(this.popupHandler);
            newModel.addActionListener(this.popupHandler);
        }

        firePropertyChange("popupModel", oldModel, newModel);
        if (newModel != oldModel) {
            revalidate();
            repaint();
        }
    }

    @Override
    public void setEnabled(boolean b) {
        if (this.popupModel != null) {
            if (!b && this.popupModel.isRollover()) {
                this.popupModel.setRollover(false);
            }
        }
        if (this.actionModel != null) {
            if (!b && this.actionModel.isRollover()) {
                this.actionModel.setRollover(false);
            }
        }
        super.setEnabled(b);
        if (this.popupModel != null) {
            this.popupModel.setEnabled(b);
        }
        if (this.actionModel != null) {
            this.actionModel.setEnabled(b);
        }
    }

    /**
     * Default popup handler.
     *
     * @author Kirill Grouchnikov
     */
    class PopupHandler implements PopupActionListener, ChangeListener {
        public void stateChanged(ChangeEvent e) {
            fireStateChanged();
            repaint();
        }

        public void actionPerformed(ActionEvent event) {
            firePopupActionPerformed(event);
        }
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type. The event instance is lazily created using the
     * event parameter.
     *
     * @param event the ActionEvent object
     * @see EventListenerList
     */
    protected void firePopupActionPerformed(ActionEvent event) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        ActionEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == PopupActionListener.class) {
                // Lazily create the event:
                if (e == null) {
                    String actionCommand = event.getActionCommand();
                    e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
                            actionCommand, event.getWhen(), event
                            .getModifiers());
                }
                ((PopupActionListener) listeners[i + 1]).actionPerformed(e);
            }
        }
    }

    /**
     * Sets the rich tooltip for the popup area of this button.
     *
     * @param richTooltip Rich tooltip for the popup area of this button.
     * @see #getRichTooltip(MouseEvent)
     * @see #setActionRichTooltip(RichTooltip)
     */
    public void setPopupRichTooltip(RichTooltip richTooltip) {
        // Don't remove the next line - we need to instantiate the RichTooltipManager
        // so that it starts tracking mouse events for displaying rich tooltips
        RichTooltipManager.sharedInstance();
        this.popupRichTooltip = richTooltip;
    }

    @Override
    public RichTooltip getRichTooltip(MouseEvent event) {
        CommandButtonUI ui = this.getUI();
        if (ui.getLayoutInfo().actionClickArea.contains(event.getPoint())) {
            return this.actionRichTooltip;
        }
        if (ui.getLayoutInfo().popupClickArea.contains(event.getPoint())) {
            return this.popupRichTooltip;
        }
        return null;
    }

    /**
     * Returns the key tip for the popup area of this button.
     *
     * @return The key tip for the popup area of this button.
     * @see #setPopupKeyTip(String)
     * @see #getActionKeyTip()
     */
    public String getPopupKeyTip() {
        return this.popupKeyTip;
    }

    /**
     * Sets the key tip for the popup area of this button. Fires a
     * popupKeyTip property change event.
     *
     * @param popupKeyTip The key tip for the popup area of this button.
     * @see #getPopupKeyTip()
     * @see #setActionKeyTip(String)
     */
    public void setPopupKeyTip(String popupKeyTip) {
        String old = this.popupKeyTip;
        this.popupKeyTip = popupKeyTip;
        this.firePropertyChange("popupKeyTip", old, this.popupKeyTip);
    }

    /**
     * Programmatically perform a "click" on the popup area. This does the same
     * thing as if the user had pressed and released the popup area of the
     * button.
     */
    public void doPopupClick() {
        Dimension size = getSize();
        PopupButtonModel popupModel = this.getPopupModel();
        popupModel.setArmed(true);
        popupModel.setPressed(true);
        paintImmediately(new Rectangle(0, 0, size.width, size.height));
        try {
            Thread.sleep(100);
        } catch (InterruptedException ie) {
        }
        popupModel.setPressed(false);
        popupModel.setArmed(false);
        popupModel.setPopupShowing(true);
        paintImmediately(new Rectangle(0, 0, size.width, size.height));
    }

    public boolean isPopupVisible() {
        List popupInfos =
                PopupPanelManager.defaultManager().getShownPath();
        for (PopupPanelManager.PopupInfo popupInfo : popupInfos) {
            if (popupInfo.getPopupOriginator() == this) {
                return true;
            }
        }
        return false;
    }

    /**
     * Adds a rollover action listener that will be called when the rollover
     * state of this button becomes active.
     *
     * @param l The rollover action listener to add.
     * @see #removeRolloverActionListener(RolloverActionListener)
     */
    public void addRolloverActionListener(RolloverActionListener l) {
        this.listenerList.add(RolloverActionListener.class, l);
    }

    /**
     * Removes the specified rollover action listener.
     *
     * @param l The listener to remove.
     * @see #addRolloverActionListener(RolloverActionListener)
     */
    public void removeRolloverActionListener(RolloverActionListener l) {
        this.listenerList.remove(RolloverActionListener.class, l);
    }

    /**
     * Programmatically perform a "rollover" on the action area. This does the
     * same thing as if the user had moved the mouse over the action area of the
     * button.
     */
    public void doActionRollover() {
        ActionEvent ae = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
                this.getActionModel().getActionCommand());
        // Guaranteed to return a non-null array
        RolloverActionListener[] listeners = this.getListeners(RolloverActionListener.class);
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 1; i >= 0; i--) {
            (listeners[i]).actionPerformed(ae);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy