I have a complex UI with little screen real estate, so I'm trying to allow users to manage contents of a combo box using a right-click context menu. But I'm having trouble getting the combo box's drop down list to appear at the same time as the pop-up menu. I've created a small program with just the relevant code to show where I've gotten so far. If anyone knows how to get the pop-up menu to appear on top of the combo box's drop-down list, I'd sure appreciate the help.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.lang.reflect.Field;
import java.util.ArrayList;
import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
public class ComboBoxTest {
private static final String[] names = {
"Tom", "John", "Harry", "Sally", "Mary", "Jennifer"
};
private MyComboBoxModel model;
private JComboBox comboBox;
private JPopupMenu popupMenu;
public ComboBoxTest() {
// create frame
JFrame frame = new JFrame("ComboBox Test Program");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create pop-up menu for combo box
popupMenu = new JPopupMenu();
JMenuItem mi1 = new JMenuItem("Move Up");
mi1.addActionListener(new MoveUpAction());
JMenuItem mi2 = new JMenuItem("Move Down");
mi2.addActionListener(new MoveDownAction());
JMenuItem mi3 = new JMenuItem("Delete Item");
mi3.addActionListener(new DeleteItemAction());
popupMenu.add(mi1);
popupMenu.add(mi2);
popupMenu.add(mi3);
// create data model for combo box
ArrayList<String> nameList = new ArrayList<String>();
for (String name : names) {
nameList.add(name);
}
model = new MyComboBoxModel(nameList);
// create the combo box itself
comboBox = new JComboBox(model);
comboBox.setSelectedIndex(0);
comboBox.setEditable(true);
// add right-click context menu to combo box's drop-down list
addPopupMouseListener(comboBox, new MyMouseListener());
// add it to the frame
JPanel top = new JPanel();
top.add(comboBox);
frame.add(top, BorderLayout.NORTH);
frame.setSize(300, 225);
frame.setVisible(true);
}
/*
* Add a mouse listener to a JComboBox's pop up menu,
* adapted from code found on the web here:
* http://engin-tekin.blogspot.com/2009/10/hrefhttpkfd.html
*/
private static void addPopupMouseListener(JComboBox box, MouseListener ml) {
try {
Field popupInBasicComboBoxUI = BasicComboBoxUI.class.getDeclaredField("popup");
popupInBasicComboBoxUI.setAccessible(true);
BasicComboPopup popup = (BasicComboPopup) popupInBasicComboBoxUI.get(box.getUI());
Field scrollerInBasicComboPopup = BasicComboPopup.class.getDeclaredField("scroller");
scrollerInBasicComboPopup.setAccessible(true);
JScrollPane scroller = (JScrollPane) scrollerInBasicComboPopup.get(popup);
scroller.getViewport().getView().addMouseListener(ml);
}
catch (NoSuchFieldException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public class MoveUpAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
String sel = (String)comboBox.getSelectedItem();
System.out.println("Move up " + sel);
model.moveUp(sel);
}
}
public class MoveDownAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
String sel = (String)comboBox.getSelectedItem();
System.out.println("Move down " + sel);
model.moveDown(sel);
}
}
public class DeleteItemAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
String sel = (String)comboBox.getSelectedItem();
System.out.println("Delete " + sel);
model.delete(sel);
}
}
public class MyMouseListener extends MouseAdapter {
public void mouseReleased(MouseEvent me) {
if (me.isPopupTrigger()) {
final MouseEvent e = me;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// make the drop down combo box list stay visible
comboBox.showPopup();
int x = e.getX();
int y = e.getY();
popupMenu.show(e.getComponent(), x, y);
}
});
}
}
}
public static void main(String args[]) {
new ComboBoxTest();
}
}
class MyComboBoxModel extends AbstractListModel implements ComboBoxModel {
private Object selectedItem;
private ArrayList<String> arrayList;
public MyComboBoxModel(ArrayList<String> list) {
arrayList = list;
}
public Object getSelectedItem() {
return selectedItem;
}
public void setSelectedItem(Object newValue) {
selectedItem = newValue;
}
public int getSize() {
return arrayList.size();
}
public Object getElementAt(int i) {
return arrayList.get(i);
}
/**
* Move the specified item up in the list.
* If the item is already at the top, this will
* move it to the bottom.
* @param item the item to move
*/
public void moveUp(String item) {
int place = arrayList.indexOf(item);
if (place >= 0) {
arrayList.remove(place);
if (place > 0)
arrayList.add(place-1, item);
else
arrayList.add(item);
}
}
/**
* Move the specified item down in the list.
* If the item is already at the bottom, this
* will move it to the top.
* @param item the item to move
*/
public void moveDown(String item) {
int place = arrayList.indexOf(item);
if (place >= 0) {
arrayList.remove(place);
if (place < arrayList.size())
arrayList.add(place+1, item);
else
arrayList.add(0, item);
}
}
/**
* Remove the specified item from the list
* @param item the item to remove
*/
public void delete(String item) {
arrayList.remove(item);
}
}