In code below, I have two combo boxes where I am trying to:
- Make the first one looks just like the second one (without ugly borders and separate right button);
- Remove the
ComboPopup
black line border;
- And if possible highlight the mouse-over popup table row.
MainClass
class:
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import com.eps.agir.libraries.DetailedComboBox;
@SuppressWarnings("serial")
public class MainClass extends JFrame {
private JPanel cp_root;
public MainClass() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
cp_root = new JPanel(null);
setContentPane(cp_root);
List<List<?>> data = new ArrayList<List<?>>();
data.add(new ArrayList<String>(Arrays.asList("1", "1st Element", "1st Parameter")));
data.add(new ArrayList<String>(Arrays.asList("2", "2nd Element", "2nd Parameter")));
data.add(new ArrayList<String>(Arrays.asList("3", "3rd Element", "3rd Parameter")));
DetailedComboBox multiColumn = new DetailedComboBox(new String[]{"ID", "Element", "Parameter"}, new int[]{0, 200, 150}, 1, new int[]{0});
multiColumn.setTableData(data);
multiColumn.setBounds(20, 30, 400, 30);
cp_root.add(multiColumn);
JComboBox<String> simple = new JComboBox<String>(new DefaultComboBoxModel<String>(new String[] {"1st Element", "2nd Element", "3rd Element"}));
simple.setBounds(20, 70, 400, 30);
cp_root.add(simple);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
MainClass frame = new MainClass();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
DetailedComboBox
class:
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
@SuppressWarnings("serial")
public class DetailedComboBox extends JComboBox<Object> {
private List<List<? extends Object>> tableData;
private String[] columnNames;
private int[] columnWidths;
private int displayColumn;
private int[] removedColumns;
public DetailedComboBox(String[] colNames, int[] colWidths, int displayColumnIndex, int removedIndexes[]){
super();
this.columnNames = colNames;
this.columnWidths = colWidths;
this.displayColumn = displayColumnIndex;
this.removedColumns = removedIndexes;
setUI(new TableComboBoxUI());
setEditable(false);
}
/**
* Populate the combobox and drop-down table with the supplied data.
* If the supplied List is non-null and non-empty, it is assumed that
* the data is a List of Lists to be used for the drop-down table.
* The combobox is also populated with the column data from the
* column defined by <code>displayColumn</code>.
*/
public void setTableData(List<List<? extends Object>> tableData){
this.tableData = (tableData == null ? new ArrayList<List<? extends Object>>() : tableData);
// even though the incoming data is for the table, we must also
// populate the combobox's data, so first clear the previous list.
removeAllItems();
// then load the combobox with data from the appropriate column
Iterator<List<? extends Object>> iter = this.tableData.iterator();
while (iter.hasNext()){
List<? extends Object> rowData = iter.next();
addItem(rowData.get(displayColumn));
}
}
public List<? extends Object> getSelectedRow(){
return tableData.get(getSelectedIndex());
}
/**
* The handler for the combobox's components
*/
private class TableComboBoxUI extends MetalComboBoxUI {
/**
* Create a popup component for the ComboBox
*/
protected ComboPopup createPopup(){
return new TableComboPopup(comboBox, this);
}
/**
* Return the JList component
*/
@SuppressWarnings("rawtypes")
public JList getList(){
return listBox;
}
}
/**
* The drop-down of the combobox, which is a JTable instead of a JList.
*/
private class TableComboPopup extends BasicComboPopup implements ListSelectionListener, ItemListener{
private final JTable table;
private TableComboBoxUI comboBoxUI;
private PopupTableModel tableModel;
private JScrollPane scroll;
/**
* Construct a popup component that's a table
*/
@SuppressWarnings("rawtypes")
public TableComboPopup(JComboBox combo, TableComboBoxUI ui){
super(combo);
this.comboBoxUI = ui;
tableModel = new PopupTableModel();
table = new JTable(tableModel);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getTableHeader().setReorderingAllowed(false);
TableColumnModel tableColumnModel = table.getColumnModel();
tableColumnModel.setColumnSelectionAllowed(false);
for (int index = 0; index < table.getColumnCount(); index++){
TableColumn tableColumn = tableColumnModel.getColumn(index);
tableColumn.setPreferredWidth(columnWidths[index]);
}
for (int index = removedColumns.length-1; index >= 0 ; index--) {
table.removeColumn(tableColumnModel.getColumn(removedColumns[index]));
}
scroll = new JScrollPane(table);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
ListSelectionModel selectionModel = table.getSelectionModel();
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
selectionModel.addListSelectionListener(this);
combo.addItemListener(this);
table.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent event){
Point p = event.getPoint();
int row = table.rowAtPoint(p);
comboBox.setSelectedIndex(row);
hide();
}
});
table.setBackground(UIManager.getColor("ComboBox.listBackground"));
table.setForeground(UIManager.getColor("ComboBox.listForeground"));
}
/**
* This method is overridden from BasicComboPopup
*/
public void show(){
if (isEnabled()){
super.removeAll();
int scrollWidth = table.getPreferredSize().width + 20;
int scrollHeight = comboBoxUI.getList().getPreferredScrollableViewportSize().height;
scroll.setPreferredSize(new Dimension(scrollWidth, scrollHeight));
super.add(scroll);
ListSelectionModel selectionModel = table.getSelectionModel();
selectionModel.removeListSelectionListener(this);
selectRow();
selectionModel.addListSelectionListener(this);
int scrollX = 0;
int scrollY = comboBox.getBounds().height;
show(comboBox, scrollX, scrollY);
}
}
/**
* Implemention of ListSelectionListener
*/
public void valueChanged(ListSelectionEvent event){
comboBox.setSelectedIndex(table.getSelectedRow());
}
/**
* Implemention of ItemListener
*/
public void itemStateChanged(ItemEvent event){
if (event.getStateChange() != ItemEvent.DESELECTED){
ListSelectionModel selectionModel = table.getSelectionModel();
selectionModel.removeListSelectionListener(this);
selectRow();
selectionModel.addListSelectionListener(this);
}
}
/**
* Sync the selected row of the table with the selected row of the combo.
*/
private void selectRow(){
int index = comboBox.getSelectedIndex();
if (index != -1){
table.setRowSelectionInterval(index, index);
table.scrollRectToVisible(table.getCellRect(index, 0, true));
}
}
}
/**
* A model for the popup table's data
*/
private class PopupTableModel extends AbstractTableModel{
/**
* Return the # of columns in the drop-down table
*/
public int getColumnCount(){
return columnNames.length;
}
/**
* Return the # of rows in the drop-down table
*/
public int getRowCount(){
return tableData == null ? 0 : tableData.size();
}
/**
* Determine the value for a given cell
*/
public Object getValueAt(int row, int col){
if (tableData == null || tableData.size() == 0){
return "";
}
return tableData.get(row).get(col);
}
/**
* All cells in the drop-down table are uneditable
*/
public boolean isCellEditable(int row, int col){
return false;
}
/**
* Pull the column names out of the tableInfo object for the header
*/
public String getColumnName(int column){
String columnName = null;
if (column >= 0 && column < columnNames.length){
columnName = columnNames[column].toString();
}
return (columnName == null) ? super.getColumnName(column) : columnName;
}
}
}
I think it's because use of MetalComboBoxUI
in DetailedComboBox
class.
I will appreciate any alternative solution.
Thanks in advance.