package edu.oregonstate.cartography.gui;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.Serializable;
import javax.swing.JComboBox;
import javax.swing.JPopupMenu;
import javax.swing.JToggleButton;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
/**
* A toggle button that shows a popup menu while being selected.
* http://explodingpixels.wordpress.com/2008/11/10/prevent-popup-menu-dismissal/
*/
public class MenuToggleButton extends JToggleButton implements Serializable{
private JPopupMenu popupMenu = null;
private boolean shouldHandlePopupWillBecomeInvisible = true;
public MenuToggleButton() {
// install a special client property on the button to prevent it from
// closing of the popup when the down arrow is pressed.
JComboBox box = new JComboBox();
Object preventHide = box.getClientProperty("doNotCancelPopup");
putClientProperty("doNotCancelPopup", preventHide);
}
private MouseListener createButtonMouseListener() {
return new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
// if the popup menu is currently showing, then hide it.
// else if the popup menu is not showing, then show it.
if (popupMenu.isShowing()) {
hidePopupMenu();
} else if (isEnabled()) {
showPopupMenu();
}
}
};
}
private PopupMenuListener createPopupMenuListener() {
return new PopupMenuListener() {
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
// no implementation.
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
// handle this event if so indicated. the only time we don't handle
// this event is when the button itself is pressed, the press action
// toggles the button selected state for us. this case handles when
// the button has been toggled, but the user clicks outside the
// button in order to dismiss the menu.
if (shouldHandlePopupWillBecomeInvisible) {
setSelected(false);
}
}
public void popupMenuCanceled(PopupMenuEvent e) {
// the popup menu has been canceled externally (either by
// pressing escape or clicking off of the popup menu). update
// the button's state to reflect the menu dismissal.
setSelected(false);
}
};
}
private void hidePopupMenu() {
shouldHandlePopupWillBecomeInvisible = false;
popupMenu.setVisible(false);
shouldHandlePopupWillBecomeInvisible = true;
}
private void showPopupMenu() {
// show the menu below the button, and slightly to the right.
popupMenu.show(this, 5, getHeight());
}
public JPopupMenu getPopupMenu() {
return popupMenu;
}
public void setPopupMenu(JPopupMenu popupMenu) {
// bad things happen if the menu is set more than once!
if (this.popupMenu != null) {
throw new IllegalStateException("menu already set");
}
this.popupMenu = popupMenu;
// add a popup menu listener to update the button's selection state
// when the menu is being dismissed.
this.popupMenu.addPopupMenuListener(createPopupMenuListener());
// install a mouse listener on the button to hide and show the popup
// menu as appropriate.
addMouseListener(createButtonMouseListener());
}
}