/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander 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 3 of the License, or * (at your option) any later version. * * muCommander 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, see <http://www.gnu.org/licenses/>. */ package com.mucommander.ui.combobox; import javax.swing.*; import java.awt.event.ActionEvent; import java.util.Vector; import java.util.WeakHashMap; /** * SaneComboBox is a JComboBox which does not have the awful default JComboBox behavior of firing ActionEvents * when navigating with the arrow keys between choices of the popup menu. * This page describes the problem in details: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4199622 * * <p>Also when using {@link ComboBoxListener}, action events that are normally triggered by JComboBox * when the add/insert/remove item methods are called are filtered out, only actual selection changes performed * by the user are fired. * * @author Maxence Bernard */ public class SaneComboBox extends JComboBox { private WeakHashMap<ComboBoxListener, Object> listeners = new WeakHashMap<ComboBoxListener, Object>(); private boolean ignoreActionEvent; public SaneComboBox() { super(); init(); } public SaneComboBox(ComboBoxModel comboBoxModel) { super(comboBoxModel); init(); } public SaneComboBox(Object[] items) { super(items); init(); } public SaneComboBox(Vector<Object> items) { super(items); init(); } private void init() { // Prevent up/down keys from firing ActionEvents // for Java 1.3 putClientProperty("JComboBox.lightweightKeyboardNavigation","Lightweight"); // Commented as it causes rendering issues under Mac OS X Leopard (does not render like a native combo box) // // for Java 1.4 and up // putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); // Listen to combo box action events, these are fired each time an item is selected when the popup menu // is visible, either by pressing 'Enter' on an item or by clicking on it. addActionListener(new AbstractAction() { public void actionPerformed(ActionEvent actionEvent) { // Filter out action events triggered by the add/insert/remove item methods if(!ignoreActionEvent) fireComboBoxSelectionChanged(); } }); } ////////////////////////////////////// // ComboBoxListener support methods // ////////////////////////////////////// /** * Adds the specified ComboBoxListener to the list of registered listeners. * * <p>Listeners are stored as weak references so {@link #removeComboBoxListener(ComboBoxListener)} * doesn't need to be called for listeners to be garbage collected when they're not used anymore. * * @param listener the ComboBoxListener to add to the list of registered listeners. */ public void addComboBoxListener(ComboBoxListener listener) { listeners.put(listener, null); } /** * Removes the specified ComboBoxListener from the list of registered listeners. * * @param listener the ComboBoxListener to remove from the list of registered listeners. */ public void removeComboBoxListener(ComboBoxListener listener) { listeners.remove(listener); } /** * Notifies all registered ComboBoxListener instances that an item has been selected from the * combo box popup menu. The item may have been selected either with the 'Enter' key, or by clicking on the item. * * <p>Unlike JComboBox ActionListener behavior, calls to the add/insert/remove item methods do *not* trigger * a selection event. */ protected void fireComboBoxSelectionChanged() { // Iterate on all listeners for(ComboBoxListener listener : listeners.keySet()) listener.comboBoxSelectionChanged(this); } //////////////////////// // Overridden methods // //////////////////////// @Override public void addItem(Object object) { ignoreActionEvent = true; super.addItem(object); ignoreActionEvent = false; } @Override public void insertItemAt(Object object, int i) { ignoreActionEvent = true; super.insertItemAt(object, i); ignoreActionEvent = false; } @Override public void removeItem(Object object) { ignoreActionEvent = true; super.removeItem(object); ignoreActionEvent = false; } @Override public void removeItemAt(int i) { ignoreActionEvent = true; super.removeItemAt(i); ignoreActionEvent = false; } @Override public void removeAllItems() { ignoreActionEvent = true; super.removeAllItems(); ignoreActionEvent = false; } }