// Copyright (C) 2005 Mammoth Software LLC
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
// Contact the author at: info@mammothsoftware.com
package javax.swing.origamist;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.border.Border;
/**
* A Drop Down Button.
*
* @author m. bangham
* @author Martin Pecka - added Javadoc and some minor modifications.
* Copyright 2005 Mammoth Software LLC
*/
public class JDropDownButton extends JButton
{
/** */
private static final long serialVersionUID = 8583671910964778527L;
protected JPopupMenu popup = new JPopupMenu();
protected JToolBar tb = new ToolBar();
protected JRolloverButton mainButton;
protected JRolloverButton arrowButton;
protected ActionListener mainButtonListener = new ActionListener() {
public void actionPerformed(ActionEvent e)
{
if (popup.getComponentCount() > 0) {
Component component = popup.getComponent(0);
if (component instanceof JMenuItem) {
JMenuItem item = (JMenuItem) component;
item.doClick(0);
}
}
}
};
/**
* Create a dropdown button with the given icon and size set to 25x25 for the icon and 11x11 for the arrow.
*
* @param icon The icon to set.
*/
public JDropDownButton(Icon icon)
{
this(icon, new Dimension(25, 25));
}
/**
* Create a dropdown button with the given icon and size set to 25x25 for the icon and 11x11 for the arrow. Set the
* size of the main button to <code>size</code>.
*
* @param icon The icon to set.
* @param size The size of the main button.
*/
public JDropDownButton(Icon icon, Dimension size)
{
this();
mainButton = new JRolloverButton(icon, size, false);
arrowButton = new JRolloverButton(new DownArrow(), new Dimension(11, 11), false);
init();
}
/**
* Creates a dropdown button from the given <code>mainButton</code> and <code>arrowButton</code>.
*
* @param mainButton The main button of this dropdown.
* @param arrowButton The arrow button.
*/
public JDropDownButton(JRolloverButton mainButton, JRolloverButton arrowButton)
{
this();
this.mainButton = mainButton;
this.arrowButton = arrowButton;
init();
}
/**
* Creates a dropdown button with the given <code>mainButton</code> and the default arrow button.
*
* @param mainButton The main button of this dropdown.
*/
public JDropDownButton(JRolloverButton mainButton)
{
this(mainButton, new JRolloverButton(new DownArrow(), new Dimension(11, 11), false));
}
/**
* Creates a dropdown button with the given <code>mainButton</code> and the default arrow button.
*
* @param mainButton The main button of this dropdown.
*/
public JDropDownButton(AbstractButton mainButton)
{
this(new JRolloverButton(mainButton, false), new JRolloverButton(new DownArrow(), new Dimension(11, 11), false));
}
private JDropDownButton()
{
super();
setBorder(null);
}
@Override
public void setText(String text)
{
mainButton.setText(text);
}
/**
* Returns <code>null</code> always. If you want to get the text of the <code>mainButton</code>, call
* <code>getMainButton().getText()</code>.
*/
@Override
public String getText()
{
return null;
}
@Override
public void setToolTipText(String text)
{
super.setToolTipText(text);
mainButton.setToolTipText(text);
arrowButton.setToolTipText(text);
}
/**
* Returns <code>null</code> always. If you want to get the icon of the <code>mainButton</code>, call
* <code>getMainButton().getIcon()</code>.
*/
@Override
public Icon getIcon()
{
return null;
}
@Override
public void setIcon(Icon defaultIcon)
{
mainButton.setIcon(defaultIcon);
}
@Override
public int getMnemonic()
{
if (mainButton != null)
return mainButton.getMnemonic();
else
return super.getMnemonic();
}
@Override
public void setMnemonic(int mnemonic)
{
if (mainButton != null)
mainButton.setMnemonic(mnemonic);
else
super.setMnemonic(mnemonic);
}
@Override
public void setEnabled(boolean enabled)
{
super.setEnabled(enabled);
mainButton.setEnabled(enabled);
arrowButton.setEnabled(enabled);
}
@Override
public void setOpaque(boolean isOpaque)
{
super.setOpaque(isOpaque);
if (mainButton != null)
mainButton.setOpaque(isOpaque);
if (arrowButton != null)
arrowButton.setOpaque(isOpaque);
}
@Override
public void updateUI()
{
super.updateUI();
setBorder(null);
}
/**
* @return the mainButton
*/
public JButton getMainButton()
{
return mainButton;
}
/**
* @return the arrowButton
*/
public JButton getArrowButton()
{
return arrowButton;
}
/**
* @return The border to be added to the button when the mouse is placed over the button.
*/
protected Border getRolloverBorder()
{
return BorderFactory.createEmptyBorder();
}
/**
* Add a mouse listener to the main and arrow buttons.
*/
protected void initRolloverListener()
{
MouseListener l = new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e)
{
mainButton.setRollover(true);
arrowButton.setRollover(true);
}
@Override
public void mouseExited(MouseEvent e)
{
mainButton.setRollover(false);
arrowButton.setRollover(false);
}
};
mainButton.addMouseListener(l);
arrowButton.addMouseListener(l);
}
/**
* Initialize this button.
*/
protected void init()
{
initRolloverListener();
this.setAction(new PopupAction());
mainButton.setAction(new PopupAction());
// using setAction() would delete the arrow icon
arrowButton.addActionListener(new PopupAction());
super.setText(null);
super.setIcon(null);
arrowButton.setFocusable(false);
setFocusable(true);
// these two focus listeners are responsible for focusing only the mainButton while preserving isFocusable() for
// the whole JDropDownButton
addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e)
{
if (e.getOppositeComponent() != mainButton)
mainButton.requestFocusInWindow();
}
});
// without this listener, it would be impossible to traverse focus back
mainButton.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e)
{
setFocusable(true);
}
@Override
public void focusGained(FocusEvent e)
{
setFocusable(false);
}
});
mainButton.setRolloverBorder(null);
arrowButton.setRolloverBorder(null);
Icon disDownArrow = new DisabledDownArrow();
arrowButton.setDisabledIcon(disDownArrow);
arrowButton.setMaximumSize(new Dimension(11, 100));
arrowButton.setMinimumSize(new Dimension(11, 11));
mainButton.setOpaque(isOpaque());
arrowButton.setOpaque(isOpaque());
setMargin(new Insets(0, 0, 0, 0));
// Windows draws border around buttons, but not toolbar buttons
// Using a toolbar keeps the look consistent.
tb.setBorder(null);
tb.setMargin(new Insets(0, 0, 0, 0));
tb.setFloatable(false);
tb.add(mainButton);
tb.add(arrowButton);
tb.setOpaque(false);
add(tb);
}
/**
* Removes a component from the popup.
*
* @param component The component to remove.
*/
public void removeComponent(Component component)
{
popup.remove(component);
}
/**
* Adds a component to the popup.
*
* @param component The component to add.
* @return The <code>component</code> argument.
*/
public Component addComponent(Component component)
{
return popup.add(component);
}
/**
* Indicates that the first item in the menu should be executed when the main button is clicked.
*
* @param isRunFirstItem True for on, false for off.
*/
public void setRunFirstItem(boolean isRunFirstItem)
{
if (isRunFirstItem) {
mainButton.setAction(null);
mainButton.addActionListener(mainButtonListener);
}
}
public void setPopupVisible(boolean visible)
{
if (visible)
getPopupMenu().show(this, 0, this.getHeight());
else
getPopupMenu().setVisible(false);
}
public JPopupMenu getPopupMenu()
{
return popup;
}
/**
* Pops up the drop-down list.
*
* @author Martin Pecka
*/
private class PopupAction extends AbstractAction
{
/** */
private static final long serialVersionUID = 3939022490935483876L;
@Override
public void actionPerformed(ActionEvent ae)
{
setPopupVisible(true);
}
}
private static class DownArrow implements Icon
{
Color arrowColor = Color.black;
@Override
public void paintIcon(Component c, Graphics g, int x, int y)
{
g.setColor(arrowColor);
g.drawLine(x, y, x + 4, y);
g.drawLine(x + 1, y + 1, x + 3, y + 1);
g.drawLine(x + 2, y + 2, x + 2, y + 2);
}
@Override
public int getIconWidth()
{
return 6;
}
@Override
public int getIconHeight()
{
return 4;
}
}
private static class DisabledDownArrow extends DownArrow
{
public DisabledDownArrow()
{
arrowColor = new Color(140, 140, 140);
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y)
{
super.paintIcon(c, g, x, y);
g.setColor(Color.white);
g.drawLine(x + 3, y + 2, x + 4, y + 1);
g.drawLine(x + 3, y + 3, x + 5, y + 1);
}
}
private class ToolBar extends JToolBar
{
/** */
private static final long serialVersionUID = -7995181550031357697L;
@Override
public void updateUI()
{
super.updateUI();
setBorder(null);
}
}
}