edu.ku.brc.ui.DropDownButton.java Source code

Java tutorial

Introduction

Here is the source code for edu.ku.brc.ui.DropDownButton.java

Source

/* Copyright (C) 2015, University of Kansas Center for Research
 * 
 * Specify Software Project, specify@ku.edu, Biodiversity Institute,
 * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA
 * 
 * This program 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 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package edu.ku.brc.ui;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.looks.plastic.PlasticLookAndFeel;

/**
 * 
 * XXX NOTE: This only drops menu down and doesn't check for where it is on the screen
 * 
 * (Adpated from an example by santhosh kumar - santhosh@in.fiorano.com)
 *
 * @code_status Complete
 * 
 * @author rods  
 *
 */
@SuppressWarnings("serial")
public class DropDownButton extends JPanel
        implements ChangeListener, PopupMenuListener, ActionListener, PropertyChangeListener {
    protected static BasicStroke lineStroke = new BasicStroke(2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
    protected static Color focusColor = null;

    protected Color hoverColor = UIHelper.getHoverColor();

    protected EmptyBorder emptyBorder = null;
    protected Border focusBorder = null;
    protected boolean hasFocus = false;

    protected JButton mainBtn;
    protected JButton arrowBtn = null;
    protected boolean popupVisible = false;
    protected String statusBarHintText = null;
    protected boolean overrideButtonBorder = false;
    protected List<JComponent> menus = null;
    protected List<ActionListener> listeners = new ArrayList<ActionListener>();

    protected boolean isHovering = false;
    protected JComponent popupAnchorComponent = null;

    protected static ImageIcon dropDownArrow;

    static {
        dropDownArrow = IconManager.getIcon("DropDownArrow");
    }

    // this class needs a mouse tracker to pop down the menu when the mouse isn't over it or the button

    /**
     * Default Constructor.
     */
    public DropDownButton() {
        super();

        init(null, null, null, false);
    }

    public DropDownButton(final boolean addArrow) {
        super();

        init(null, null, null, addArrow);
    }

    /**
     * Constructor with only an icon.
     * @param icon the icon
     */
    public DropDownButton(final ImageIcon icon, final boolean addArrow) {
        init(null, icon, null, addArrow);
    }

    public void setFont(final Font font) {
        if (mainBtn != null) {
            mainBtn.setFont(font);
        }
    }

    /**
     * Creates a toolbar item with label and icon and their positions.
     * @param label label of the toolbar item
     * @param icon the icon
     * @param toolTip the tooltip text that has already been localized
     * @param textPosition the position of the text as related to the icon
     * @param indicates whether an arrow btn should be added
     */
    public DropDownButton(String label, ImageIcon icon, String toolTip, int horzTextPosition,
            final boolean addArrow) {
        this(label, icon, toolTip, horzTextPosition, SwingConstants.CENTER, addArrow);
    }

    /**
     * Creates a toolbar item with label and icon and their positions.
     * @param label label of the toolbar item
     * @param icon the icon
     * @param toolTip the tooltip text that has already been localized
     * @param horzTextPosition the horizontal position of the text as related to the icon
     * @param vertTextPosition the vertical position of the text as related to the icon
     * @param indicates whether an arrow btn should be added
     */
    public DropDownButton(final String label, ImageIcon icon, final String toolTip, final int horzTextPosition,
            final int vertTextPosition, final boolean addArrow) {
        init(label, icon, toolTip, addArrow);

        mainBtn.setHorizontalTextPosition(horzTextPosition);
        mainBtn.setVerticalTextPosition(vertTextPosition);
    }

    /**
     * Creates a toolbar item with label and icon and their positions and menu items to be added.
     * The Items MUST be of class JSeparator or JMenuItem.
     * @param label label of the toolbar item
     * @param icon the icon
     * @param vertTextPosition the position of the text as related to the icon
     * @param menus the list of menu items and separators
     */
    public DropDownButton(final String label, final ImageIcon icon, final int vertTextPosition,
            final List<JComponent> menus) {
        this.menus = menus;

        init(label, icon, null, menus != null && menus.size() > 0);

        mainBtn.setVerticalTextPosition(vertTextPosition);

        if (vertTextPosition == SwingConstants.BOTTOM || vertTextPosition == SwingConstants.TOP) {
            mainBtn.setHorizontalTextPosition(SwingConstants.CENTER);
        }
    }

    /**
     * Initializes the internal UI.
     */
    /**
     * @param label
     * @param icon
     * @param toolTip
     */
    protected void init(final String label, final ImageIcon icon, final String toolTip, final boolean addArrowBtn) {
        setOpaque(false);

        FocusListener focusListener = UIHelper.isMacOS() ? createFocusListener() : null;
        MouseInputAdapter mouseInputAdapter = createMouseInputAdapter();

        mainBtn = createLabelBtn(label, icon, toolTip, this, focusListener, mouseInputAdapter, this, this,
                overrideButtonBorder);
        arrowBtn = createArrowBtn(mouseInputAdapter);
        mainBtn.setOpaque(false);

        popupAnchorComponent = mainBtn;

        PanelBuilder pb = new PanelBuilder(new FormLayout("p:g" + (addArrowBtn ? ",p" : ""), "f:p:g"), this);
        CellConstraints cc = new CellConstraints();

        pb.add(mainBtn, cc.xy(1, 1));
        if (addArrowBtn) {
            pb.add(arrowBtn, cc.xy(2, 1));
        }

        if (UIHelper.isMacOS()) {
            focusBorder = new MacBtnBorder();
            emptyBorder = new EmptyBorder(focusBorder.getBorderInsets(this));

        } else {
            if (UIManager.getLookAndFeel() instanceof PlasticLookAndFeel) {
                focusColor = PlasticLookAndFeel.getFocusColor();
            } else {
                focusColor = UIManager.getColor("Button.focus");
            }
            if (focusColor == null) {
                focusColor = Color.DARK_GRAY;
            }

            //focusBorder = new LineBorder(focusColor, 1, true);
            //emptyBorder = new EmptyBorder(focusBorder.getBorderInsets(this));
        }

        if (!overrideButtonBorder) {
            setBorder(emptyBorder);
        }

        addMouseListener(mouseInputAdapter);
        addMouseMotionListener(mouseInputAdapter);
    }

    /**
     * @return
     */
    public FocusListener createFocusListener() {
        return new FocusListener() {
            public void focusGained(FocusEvent e) {
                hasFocus = true;
                setBorder(focusBorder);
                repaint();
            }

            public void focusLost(FocusEvent e) {
                hasFocus = false;
                setBorder(emptyBorder);
                repaint();
            }
        };
    }

    /**
     * @return
     */
    public MouseInputAdapter createMouseInputAdapter() {
        return new MouseInputAdapter() {
            @Override
            public void mouseEntered(MouseEvent e) {
                if (DropDownButton.this.isEnabled()) {
                    isHovering = true;
                    if (statusBarHintText != null) {
                        UIRegistry.displayStatusBarText(statusBarHintText);
                    }

                    arrowBtn.setEnabled(getPopMenuSize() > 0 && isEnabled());
                    repaint();
                }
            }

            @Override
            public void mouseExited(MouseEvent e) {
                isHovering = false;
                if (DropDownButton.this.isEnabled()) {
                    UIRegistry.displayStatusBarText(null);

                    if (popupVisible) {
                        popupVisible = false;

                        mainBtn.getModel().setRollover(false);
                        arrowBtn.getModel().setSelected(false);
                    }
                }
                repaint();
            }
        };
    }

    /**
     * @param label
     * @param icon
     * @param toolTip
     * @param changeListener
     * @param focusListener
     * @param mouseInputAdapter
     * @param al
     * @param pcl
     * @param overRideButtonBorder
     * @return
     */
    public static JButton createLabelBtn(final String label, final ImageIcon icon, final String toolTip,
            final ChangeListener changeListener, final FocusListener focusListener,
            final MouseInputAdapter mouseInputAdapter, final ActionListener al, final PropertyChangeListener pcl,
            final boolean overRideButtonBorder) {
        JButton btn = UIHelper.createButton(label, icon);
        btn.setOpaque(false);
        btn.addFocusListener(focusListener);

        if (!overRideButtonBorder) {
            btn.setBorder(new EmptyBorder(3, 6, 3, 4));
        }

        btn.setIconTextGap(1);
        btn.setMargin(new Insets(0, 0, 0, 0));
        btn.getModel().addChangeListener(changeListener);
        btn.setHorizontalTextPosition(SwingConstants.RIGHT);
        btn.setVerticalTextPosition(SwingConstants.CENTER);
        btn.addPropertyChangeListener("enabled", pcl);

        if (toolTip != null) {
            btn.setToolTipText(toolTip);
        }

        btn.addMouseListener(mouseInputAdapter);
        btn.addMouseMotionListener(mouseInputAdapter);
        btn.addActionListener(al);

        return btn;
    }

    /**
     * @param mouseInputAdapter
     * @return
     */
    public JButton createArrowBtn(final MouseInputAdapter mouseInputAdapter) {
        JButton arwBtn = new JButton(dropDownArrow);
        arwBtn.setOpaque(false);
        arwBtn.setBorder(new EmptyBorder(4, 4, 4, 4)); // T,L,B,R
        arwBtn.getModel().addChangeListener(this);
        arwBtn.addActionListener(this);
        arwBtn.setMargin(new Insets(3, 3, 3, 3));
        arwBtn.setFocusPainted(false);
        arwBtn.setFocusable(false);
        arwBtn.setVisible(getPopMenuSize() > 0);
        arwBtn.addMouseListener(mouseInputAdapter);
        arwBtn.addMouseMotionListener(mouseInputAdapter);
        return arwBtn;
    }

    /**
     * @param val
     * @param border
     */
    public void setOverrideBorder(boolean val, Border border) {
        overrideButtonBorder = val;
        if (val) {
            setBorder(border);
        }
    }

    /**
     * Adds listener.
     * @param al the action listener
     */
    public void addActionListener(ActionListener al) {
        listeners.add(al);
    }

    /**
     * Removes listener.
     * @param al the action listener
     */
    public void removeActionListener(ActionListener al) {
        listeners.remove(al);
    }

    /**
     * Returns the text of the button.
     * @return the text of the button.
     */
    public String getText() {
        return mainBtn.getText();
    }

    /**
     * @param icon
     */
    public void setIcon(final ImageIcon icon) {
        mainBtn.setIcon(icon);
    }

    /*------------------------------[ PropertyChangeListener ]---------------------------------------------------*/

    /* (non-Javadoc)
     * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
     */
    public void propertyChange(PropertyChangeEvent evt) {
        Object source = evt.getSource();
        if (source == mainBtn.getModel() || source == arrowBtn.getModel()) {
            arrowBtn.setEnabled(mainBtn.isEnabled());
            if (!arrowBtn.isVisible()) {
                arrowBtn.setVisible(getPopMenuSize() > 0);
                invalidate();
                doLayout();
                repaint();
            }
        }
    }

    /*------------------------------[ ChangeListener ]---------------------------------------------------*/

    /* (non-Javadoc)
     * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
     */
    public void stateChanged(ChangeEvent e) {
        if (e.getSource() == mainBtn.getModel()) {
            if (popupVisible && !mainBtn.getModel().isRollover()) {
                mainBtn.getModel().setRollover(true);
                return;
            }
            arrowBtn.getModel().setRollover(mainBtn.getModel().isRollover());
            arrowBtn.setSelected(mainBtn.getModel().isArmed() && mainBtn.getModel().isPressed());

        } else {
            if (popupVisible && !arrowBtn.getModel().isSelected()) {
                arrowBtn.getModel().setSelected(true);
                return;
            }
            mainBtn.getModel().setRollover(arrowBtn.getModel().isRollover());
        }
        arrowBtn.setVisible(getPopMenuSize() > 0);
    }

    /*------------------------------[ ActionListener ]---------------------------------------------------*/

    /* (non-Javadoc)
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
     */
    public void actionPerformed(ActionEvent ae) {
        if (ae.getSource() == arrowBtn) {
            JPopupMenu popup = getPopupMenu();
            popup.addPopupMenuListener(this);
            popup.show(popupAnchorComponent, 0, popupAnchorComponent.getHeight());

        } else {
            for (ActionListener al : listeners) {
                al.actionPerformed(ae);
            }
        }
    }

    /*------------------------------[ PopupMenuListener ]---------------------------------------------------*/

    /* (non-Javadoc)
     * @see javax.swing.event.PopupMenuListener#popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent)
     */
    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        popupVisible = true;
        mainBtn.getModel().setRollover(true);
        arrowBtn.getModel().setSelected(true);
    }

    /* (non-Javadoc)
     * @see javax.swing.event.PopupMenuListener#popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent)
     */
    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
        popupVisible = false;

        mainBtn.getModel().setRollover(false);
        arrowBtn.getModel().setSelected(false);
        ((JPopupMenu) e.getSource()).removePopupMenuListener(this); // act as
                                                                    // good
                                                                    // programmer
                                                                    // :)
    }

    /* (non-Javadoc)
     * @see javax.swing.event.PopupMenuListener#popupMenuCanceled(javax.swing.event.PopupMenuEvent)
     */
    public void popupMenuCanceled(PopupMenuEvent e) {
        popupVisible = false;
    }

    /*------------------------------[ Other Methods ]---------------------------------------------------*/

    /**
     * Returns a new JPopMenu each time it is called, the poopup menu is created from the internal list.
     * @return Returns a new JPopMenu each time it is called, the poopup menu is created from the internal list
     */
    protected JPopupMenu getPopupMenu() {
        if (menus != null) {
            JPopupMenu popupMenu = new JPopupMenu();
            for (JComponent comp : menus) {
                if (comp instanceof JMenuItem) {
                    popupMenu.add((JMenuItem) comp);

                } else if (comp instanceof JSeparator) {
                    popupMenu.add(comp);
                }
            }
            return popupMenu;
        }
        return null;
    }

    /**
     * @return
     */
    protected int getPopMenuSize() {
        return menus == null ? 0 : menus.size();
    }

    /* (non-Javadoc)
     * @see java.awt.Component#paint(java.awt.Graphics)
     */
    @Override
    public void paint(Graphics g) {
        super.paint(g);

        if (isHovering && !hasFocus && isEnabled()) {
            g.setColor(hoverColor);

            Insets insets = getInsets();
            Dimension size = getSize();

            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            RoundRectangle2D.Double rr = new RoundRectangle2D.Double(insets.left + 1, insets.top + 1,
                    size.width - insets.right - insets.left - 3, size.height - insets.bottom - insets.top - 3, 10,
                    10);
            g2d.setStroke(lineStroke);
            g2d.draw(rr);
        }
    }

    /**
     * Returns the menus for the DropDown.
     * @return the menus for the DropDown.
     */
    public List<JComponent> getMenus() {
        return menus;
    }

    /**
     * Sets the menus.
     * @param menus the menus.
     */
    public void setMenus(List<JComponent> menus) {
        this.menus = menus;
    }

    /**
     * Returns the statusBarHintText.
     * @return the statusBarHintText.
     */
    public String getStatusBarHintText() {
        return statusBarHintText;
    }

    /**
     * Sets the hint.
     * @param statusBarHintText The statusBarHintText to set.
     */
    public void setStatusBarHintText(String statusBarHintText) {
        this.statusBarHintText = statusBarHintText;
    }

    /* (non-Javadoc)
     * @see javax.swing.JComponent#setEnabled(boolean)
     */
    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);

        if (!enabled) {
            setBorder(emptyBorder);
        }

        mainBtn.setEnabled(enabled);
        arrowBtn.setEnabled(enabled);
        if (!enabled && isHovering) {
            isHovering = false;
            repaint();
        }
    }

    /* (non-Javadoc)
     * @see java.lang.Object#finalize()
     */
    @Override
    protected void finalize() throws Throwable {
        listeners.clear();
        menus.clear();
        UIHelper.removeMouseListeners(this);
        super.finalize();
    }

}