/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. Copyright 2011 Gephi Consortium. All rights reserved. The contents of this file are subject to the terms of either the GNU General Public License Version 3 only ("GPL") or the Common Development and Distribution License("CDDL") (collectively, the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the License at http://gephi.org/about/legal/license-notice/ or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the specific language governing permissions and limitations under the License. When distributing the software, include this License Header Notice in each file and include the License files at /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the License Header, with the fields enclosed by brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" If you wish your version of this file to be governed by only the CDDL or only the GPL Version 3, indicate your decision by adding "[Contributor] elects to include this software in this distribution under the [CDDL or GPL Version 3] license." If you do not indicate a single choice of license, a recipient has the option to distribute your version of this file under either the CDDL, the GPL Version 3 or to extend the choice of license to its licensees as provided above. However, if you add GPL Version 3 code and therefore, elected the GPL Version 3 license, then the option applies only if the new code is made subject to such option by the copyright holder. Contributor(s): Portions Copyrighted 2011 Gephi Consortium. */ package org.gephi.ui.components; import java.awt.Point; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.Map; import javax.swing.DefaultButtonModel; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JPopupMenu; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import org.openide.util.ImageUtilities; //author S. Aubrecht from org.openide.awt public class JDropDownButton extends JButton { private boolean mouseInButton = false; private boolean mouseInArrowArea = false; private Map<String, Icon> regIcons = new HashMap<String, Icon>(5); private Map<String, Icon> arrowIcons = new HashMap<String, Icon>(5); private static final String ICON_NORMAL = "normal"; //NOI18N private static final String ICON_PRESSED = "pressed"; //NOI18N private static final String ICON_ROLLOVER = "rollover"; //NOI18N private static final String ICON_ROLLOVER_SELECTED = "rolloverSelected"; //NOI18N private static final String ICON_SELECTED = "selected"; //NOI18N private static final String ICON_DISABLED = "disabled"; //NOI18N private static final String ICON_DISABLED_SELECTED = "disabledSelected"; //NOI18N private static final String ICON_ROLLOVER_LINE = "rolloverLine"; //NOI18N private static final String ICON_ROLLOVER_SELECTED_LINE = "rolloverSelectedLine"; //NOI18N /** * Use this property name to assign or remove popup menu to/from buttons created by this factory, * e.g. <code>dropDownButton.putClientProperty( PROP_DROP_DOWN_MENU, new JPopupMenu() )</code> * The property value must be <code>JPopupMenu</code>, removing this property removes the arrow from the button. */ public static final String PROP_DROP_DOWN_MENU = "dropDownMenu"; private PopupMenuListener menuListener; /** Creates a new instance of MenuToggleButton */ public JDropDownButton(Icon icon, JPopupMenu popup) { assert null != icon; putClientProperty(PROP_DROP_DOWN_MENU, popup); setIcon(icon); resetIcons(); addPropertyChangeListener(PROP_DROP_DOWN_MENU, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { resetIcons(); } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { if (null != getPopupMenu()) { mouseInArrowArea = isInArrowArea(e.getPoint()); updateRollover(_getRolloverIcon(), _getRolloverSelectedIcon()); } } }); addMouseListener(new MouseAdapter() { private boolean popupMenuOperation = false; @Override public void mousePressed(MouseEvent e) { if(!isEnabled()) { return; } popupMenuOperation = false; JPopupMenu menu = getPopupMenu(); if (menu != null && getModel() instanceof Model) { Model model = (Model) getModel(); if (!model._isPressed()) { if (isInArrowArea(e.getPoint()) && menu.getComponentCount() > 0) { model._press(); menu.addPopupMenuListener(getMenuListener()); menu.show(JDropDownButton.this, 0, getHeight()); popupMenuOperation = true; } } else { model._release(); menu.removePopupMenuListener(getMenuListener()); popupMenuOperation = true; } } } @Override public void mouseReleased(MouseEvent e) { // If we done something with the popup menu, we should consume // the event, otherwise the button's action will be triggered. if (popupMenuOperation) { popupMenuOperation = false; e.consume(); } } @Override public void mouseEntered(MouseEvent e) { mouseInButton = true; if (hasPopupMenu()) { mouseInArrowArea = isInArrowArea(e.getPoint()); updateRollover(_getRolloverIcon(), _getRolloverSelectedIcon()); } } @Override public void mouseExited(MouseEvent e) { mouseInButton = false; mouseInArrowArea = false; if (hasPopupMenu()) { updateRollover(_getRolloverIcon(), _getRolloverSelectedIcon()); } } }); setModel(new Model()); } private PopupMenuListener getMenuListener() { if (null == menuListener) { menuListener = new PopupMenuListener() { public void popupMenuWillBecomeVisible(PopupMenuEvent e) { } public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { // If inside the button let the button's mouse listener // deal with the state. The popup menu will be hidden and // we should not show it again. if (getModel() instanceof Model) { ((Model) getModel())._release(); } JPopupMenu menu = getPopupMenu(); if (null != menu) { menu.removePopupMenuListener(this); } } public void popupMenuCanceled(PopupMenuEvent e) { } }; } return menuListener; } private void updateRollover(Icon rollover, Icon rolloverSelected) { super.setRolloverIcon(rollover); super.setRolloverSelectedIcon(rolloverSelected); } private void resetIcons() { Icon icon = regIcons.get(ICON_NORMAL); if (null != icon) { setIcon(icon); } icon = regIcons.get(ICON_PRESSED); if (null != icon) { setPressedIcon(icon); } icon = regIcons.get(ICON_ROLLOVER); if (null != icon) { setRolloverIcon(icon); } icon = regIcons.get(ICON_ROLLOVER_SELECTED); if (null != icon) { setRolloverSelectedIcon(icon); } icon = regIcons.get(ICON_SELECTED); if (null != icon) { setSelectedIcon(icon); } icon = regIcons.get(ICON_DISABLED); if (null != icon) { setDisabledIcon(icon); } icon = regIcons.get(ICON_DISABLED_SELECTED); if (null != icon) { setDisabledSelectedIcon(icon); } } private Icon _getRolloverIcon() { Icon icon = null; icon = arrowIcons.get(mouseInArrowArea ? ICON_ROLLOVER : ICON_ROLLOVER_LINE); if (null == icon) { Icon orig = regIcons.get(ICON_ROLLOVER); if (null == orig) { orig = regIcons.get(ICON_NORMAL); } icon = new IconWithArrow(orig, !mouseInArrowArea); arrowIcons.put(mouseInArrowArea ? ICON_ROLLOVER : ICON_ROLLOVER_LINE, icon); } return icon; } private Icon _getRolloverSelectedIcon() { Icon icon = null; icon = arrowIcons.get(mouseInArrowArea ? ICON_ROLLOVER_SELECTED : ICON_ROLLOVER_SELECTED_LINE); if (null == icon) { Icon orig = regIcons.get(ICON_ROLLOVER_SELECTED); if (null == orig) { orig = regIcons.get(ICON_ROLLOVER); } if (null == orig) { orig = regIcons.get(ICON_NORMAL); } icon = new IconWithArrow(orig, !mouseInArrowArea); arrowIcons.put(mouseInArrowArea ? ICON_ROLLOVER_SELECTED : ICON_ROLLOVER_SELECTED_LINE, icon); } return icon; } public JPopupMenu getPopupMenu() { Object menu = getClientProperty(PROP_DROP_DOWN_MENU); if (menu instanceof JPopupMenu) { return (JPopupMenu) menu; } return null; } boolean hasPopupMenu() { return null != getPopupMenu(); } private boolean isInArrowArea(Point p) { return p.getLocation().x >= getWidth() - IconWithArrow.getArrowAreaWidth() - getInsets().right; } @Override public void setIcon(Icon icon) { assert null != icon; Icon arrow = updateIcons(icon, ICON_NORMAL); arrowIcons.remove(ICON_ROLLOVER_LINE); arrowIcons.remove(ICON_ROLLOVER_SELECTED_LINE); arrowIcons.remove(ICON_ROLLOVER); arrowIcons.remove(ICON_ROLLOVER_SELECTED); super.setIcon(hasPopupMenu() ? arrow : icon); } private Icon updateIcons(Icon orig, String iconType) { Icon arrow = null; if (null == orig) { regIcons.remove(iconType); arrowIcons.remove(iconType); } else { regIcons.put(iconType, orig); arrow = new ImageIcon(ImageUtilities.icon2Image(new IconWithArrow(orig, false))); arrowIcons.put(iconType, arrow); } return arrow; } @Override public void setPressedIcon(Icon icon) { Icon arrow = updateIcons(icon, ICON_PRESSED); super.setPressedIcon(hasPopupMenu() ? arrow : icon); } @Override public void setSelectedIcon(Icon icon) { Icon arrow = updateIcons(icon, ICON_SELECTED); super.setSelectedIcon(hasPopupMenu() ? arrow : icon); } @Override public void setRolloverIcon(Icon icon) { Icon arrow = updateIcons(icon, ICON_ROLLOVER); arrowIcons.remove(ICON_ROLLOVER_LINE); arrowIcons.remove(ICON_ROLLOVER_SELECTED_LINE); super.setRolloverIcon(hasPopupMenu() ? arrow : icon); } @Override public void setRolloverSelectedIcon(Icon icon) { Icon arrow = updateIcons(icon, ICON_ROLLOVER_SELECTED); arrowIcons.remove(ICON_ROLLOVER_SELECTED_LINE); super.setRolloverSelectedIcon(hasPopupMenu() ? arrow : icon); } @Override public void setDisabledIcon(Icon icon) { //TODO use 'disabled' arrow icon Icon arrow = updateIcons(icon, ICON_DISABLED); super.setDisabledIcon(hasPopupMenu() ? arrow : icon); } @Override public void setDisabledSelectedIcon(Icon icon) { //TODO use 'disabled' arrow icon Icon arrow = updateIcons(icon, ICON_DISABLED_SELECTED); super.setDisabledSelectedIcon(hasPopupMenu() ? arrow : icon); } private class Model extends DefaultButtonModel { private boolean _pressed = false; @Override public void setPressed(boolean b) { if (mouseInArrowArea || _pressed) { return; } super.setPressed(b); } public void _press() { if ((isPressed()) || !isEnabled()) { return; } stateMask |= PRESSED + ARMED; fireStateChanged(); _pressed = true; } public void _release() { _pressed = false; mouseInArrowArea = false; setArmed(false); setPressed(false); setRollover(false); setSelected(false); } public boolean _isPressed() { return _pressed; } @Override protected void fireStateChanged() { if (_pressed) { return; } super.fireStateChanged(); } @Override public void setArmed(boolean b) { if (_pressed) { return; } super.setArmed(b); } @Override public void setEnabled(boolean b) { if (_pressed) { return; } super.setEnabled(b); } @Override public void setSelected(boolean b) { if (_pressed) { return; } super.setSelected(b); } @Override public void setRollover(boolean b) { if (_pressed) { return; } super.setRollover(b); } } /** * Creates JButton with a small arrow that shows the provided popup menu when clicked. * * @param icon The default icon, cannot be null * @param dropDownMenu Popup menu to display when the arrow is clicked. If this parameter is null * then the button doesn't show any arrow and behaves like a regular JButton. It is possible to add * the popup menu later using PROP_DROP_DOWN_MENU client property. * @return A button that is capable of displaying an 'arrow' in its icon to open a popup menu. */ public static JButton createDropDownButton(Icon icon, JPopupMenu dropDownMenu) { return new JDropDownButton(icon, dropDownMenu); } }