how to create jcomboBox with two /multiple columns in the drop down let but when we select only one selected value show in Jcombobox
please give me any solution for this .
You might be able to use JList#setLayoutOrientation(JList.VERTICAL_WRAP):
import java.awt.*;
import javax.accessibility.Accessible;
import javax.swing.*;
import javax.swing.plaf.basic.ComboPopup;
public class TwoColumnsDropdownTest {
private Component makeUI() {
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
model.addElement("111");
model.addElement("2222");
model.addElement("3");
model.addElement("44444");
model.addElement("55555");
model.addElement("66");
model.addElement("777");
model.addElement("8");
model.addElement("9999");
int rowCount = (model.getSize() + 1) / 2;
JComboBox<String> combo = new JComboBox<String>(model) {
#Override public Dimension getPreferredSize() {
Insets i = getInsets();
Dimension d = super.getPreferredSize();
int w = Math.max(100, d.width);
int h = d.height;
int buttonWidth = 20; // ???
return new Dimension(buttonWidth + w + i.left + i.right, h + i.top + i.bottom);
}
#Override public void updateUI() {
super.updateUI();
setMaximumRowCount(rowCount);
setPrototypeDisplayValue("12345");
Accessible o = getAccessibleContext().getAccessibleChild(0);
if (o instanceof ComboPopup) {
JList<?> list = ((ComboPopup) o).getList();
list.setLayoutOrientation(JList.VERTICAL_WRAP);
list.setVisibleRowCount(rowCount);
list.setFixedCellWidth((getPreferredSize().width - 2) / 2);
}
}
};
JPanel p = new JPanel();
p.add(combo);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new TwoColumnsDropdownTest().makeUI());
frame.setSize(320, 240);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
You need to do a few things.
By default, what is displayed in the JComboBox, as the selected item, is the value returned by method toString of the items in the ComboBoxModel. Based on your comment I wrote an Item class and overrode the toString method.
In order to display something different in the drop-down list, you need a custom ListCellRenderer.
In order for the drop-down list to display the entire details of each item, the drop-down list needs to be wider than the JComboBox. I used code from the following SO question to achieve that:
How can I change the width of a JComboBox dropdown list?
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.math.BigDecimal;
import java.text.NumberFormat;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.basic.BasicComboPopup;
public class MultiColumnCombo implements PopupMenuListener {
private JComboBox<Item> combo;
public void popupMenuCanceled(PopupMenuEvent event) {
// Do nothing.
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent event) {
// Do nothing.
}
public void popupMenuWillBecomeVisible(PopupMenuEvent event) {
AccessibleContext comboAccessibleContext = combo.getAccessibleContext();
int comboAccessibleChildrenCount = comboAccessibleContext.getAccessibleChildrenCount();
if (comboAccessibleChildrenCount > 0) {
Accessible comboAccessibleChild0 = comboAccessibleContext.getAccessibleChild(0);
if (comboAccessibleChild0 instanceof BasicComboPopup) {
EventQueue.invokeLater(() -> {
BasicComboPopup comboPopup = (BasicComboPopup) comboAccessibleChild0;
JScrollPane scrollPane = (JScrollPane) comboPopup.getComponent(0);
Dimension d = setCurrentDimension(scrollPane.getPreferredSize());
scrollPane.setPreferredSize(d);
scrollPane.setMaximumSize(d);
scrollPane.setMinimumSize(d);
scrollPane.setSize(d);
Point location = combo.getLocationOnScreen();
int height = combo.getPreferredSize().height;
comboPopup.setLocation(location.x, location.y + height - 1);
comboPopup.setLocation(location.x, location.y + height);
});
}
}
}
private void createAndDisplayGui() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createCombo());
frame.setSize(450, 300);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createCombo() {
JPanel panel = new JPanel();
Item[] items = new Item[]{new Item("A", "Item A", new BigDecimal(1.99d), new BigDecimal(2.99d)),
new Item("B", "Item B", new BigDecimal(7.5d), new BigDecimal(9.0d)),
new Item("C", "Item C", new BigDecimal(0.25d), new BigDecimal(3.15d))};
combo = new JComboBox<>(items);
AccessibleContext comboAccessibleContext = combo.getAccessibleContext();
int comboAccessibleChildrenCount = comboAccessibleContext.getAccessibleChildrenCount();
if (comboAccessibleChildrenCount > 0) {
Accessible comboAccessibleChild0 = comboAccessibleContext.getAccessibleChild(0);
if (comboAccessibleChild0 instanceof BasicComboPopup) {
BasicComboPopup comboPopup = (BasicComboPopup) comboAccessibleChild0;
comboPopup.getList().setCellRenderer(new MultiColumnRenderer());
}
}
combo.addPopupMenuListener(this);
panel.add(combo);
return panel;
}
private Dimension setCurrentDimension(Dimension dim) {
Dimension d = new Dimension(dim);
d.width = 120;
return d;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new MultiColumnCombo().createAndDisplayGui());
}
}
class Item {
private String code;
private String name;
private BigDecimal price;
private BigDecimal salePrice;
public Item(String code, String name, BigDecimal price, BigDecimal salePrice) {
this.code = code;
this.name = name;
this.price = price;
this.salePrice = salePrice;
}
public String displayString() {
return String.format("%s %s %s %s",
code,
name,
NumberFormat.getCurrencyInstance().format(price),
NumberFormat.getCurrencyInstance().format(salePrice));
}
public String toString() {
return name;
}
}
class MultiColumnRenderer implements ListCellRenderer<Object> {
/** Component returned by method {#link #getListCellRendererComponent}. */
private JLabel cmpt;
public MultiColumnRenderer() {
cmpt = new JLabel();
cmpt.setOpaque(true);
cmpt.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
}
public Component getListCellRendererComponent(JList<? extends Object> list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
String text;
if (value == null) {
text = "";
}
else {
if (value instanceof Item) {
text = ((Item) value).displayString();
}
else {
text = value.toString();
}
}
cmpt.setText(text);
if (isSelected) {
cmpt.setBackground(list.getSelectionBackground());
cmpt.setForeground(list.getSelectionForeground());
}
else {
cmpt.setBackground(list.getBackground());
cmpt.setForeground(list.getForeground());
}
cmpt.setFont(list.getFont());
return cmpt;
}
}
Related
I have created a JTable with some information about formula 1 racing car drivers. The below code is for the JTable
import java.awt.LayoutManager;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import java.awt.event.*;
public class Test1 extends JFrame implements ActionListener {
JButton button;
Test1() {
//setBounds(100, 100, 500, 400);
JFrame frame = new JFrame();
button = new JButton();
//button.setBounds(50, 50, 20, 10);s
button.setText("Random Race");
button.addActionListener(this);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTable table = new JTable(new Model1Data());
frame.setBounds(100, 100, 500, 400);
JPanel panel = new JPanel();
frame.add(panel);
panel.add(new JScrollPane(table));
panel.add(button);
//add(new J
frame.setVisible(true);
//this.add(button);
pack();
}
#Override
public void actionPerformed(ActionEvent e) {
Formula1ChampionsManager d= new Formula1ChampionsManager();
d.button();
}
}
This is the code for the Model1Data. This is the code for the table to update its cells.
import javax.swing.table.AbstractTableModel;
public class Model1Data extends AbstractTableModel implements ChampionsManager {
String colNames[] = { "Name", "Team", "No of first Places", "Total Points" };
Class<?> colClasses[] = { String.class, String.class, Integer.class, Integer.class };
public int getRowCount() {
return myDrivers.size();
}
public int getColumnCount() {
return colNames.length;
}
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return myDrivers.get(rowIndex).getName();
}
if (columnIndex == 1) {
return myDrivers.get(rowIndex).getTeam();
}
if (columnIndex == 2) {
return myDrivers.get(rowIndex).getfirstPlace();
}
if (columnIndex == 3) {
return myDrivers.get(rowIndex).totalPoints();
}
return null;
}
public String getColumnName(int columnIndex) {
return colNames[columnIndex];
}
public Class<?> getColumnClass(int columnIndex) {
return colClasses[columnIndex];
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 0) {
myDrivers.get(rowIndex).setName((String) aValue);
}
if (columnIndex == 1) {
myDrivers.get(rowIndex).setTeam((String) aValue);
}
if (columnIndex == 2) {
myDrivers.get(rowIndex).setfirstPlace((Integer) aValue);
}
if (columnIndex == 3) {
myDrivers.get(rowIndex).settotalPoints((Integer) aValue);
}
//fireTableCellUpdated(rowIndex, columnIndex);
}
}
This what the table GUI looks like Table GUI. When I click the button the values for No of first places and Total Points are changed randomly. But the table doesn't get updated with the values. If I pull on the side of the frame it gets updated. How do I get it to update when I click the button?
The JTable listens for changes in the TableModel. Hence class AbstractTableModel has methods such as fireTableCellUpdated(rowIndex, columnIndex) (which you commented out in your code - why?). If you override method setValueAt then you need to call that method, otherwise the JTable will not update when its data is changed. I'm guessing that changing the size of the JFrame causes a repaint of the JTable which causes the JTable to reload the data from its model (but I could be wrong).
Your class Model1Data should extend DefaultTableModel, rather than AbstractTableModel, since that class already contains all the functionality you require. The only method that class Model1Data needs to override is getColumnClass (which it already does). You also need to override at least one constructor. Since you didn't post a minimal, reproducible example, I'm guessing that this constructor may be appropriate. Hence the code for class Model1Data should be:
public class Model1Data extends javax.swing.table.DefaultTableModel {
Class<?> colClasses[] = { String.class, String.class, Integer.class, Integer.class };
public Model1Data(Object[] columnNames, int rowCount) {
super(columnNames, rowCount);
}
public Class<?> getColumnClass(int columnIndex) {
return colClasses[columnIndex];
}
}
And in class Test1 (which does not need to extend JFrame, by the way) you can create the JTable like so:
JTable table = new JTable(new Model1Data(new String[]{ "Name", "Team", "No of first Places", "Total Points" }, 0));
Edit
Here's my take on what you are trying to do. The below code can be copied as is, compiled and run. Just click on the Random Race button and the data in the JTable will update.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
public class Test1 {
private JTable table;
private void buildAndDisplayGui() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createTable(), BorderLayout.CENTER);
frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JButton createButton(String text, int mnemonic, ActionListener listener) {
JButton button = new JButton(text);
button.setMnemonic(mnemonic);
button.addActionListener(listener);
return button;
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(createButton("Random Race", KeyEvent.VK_R, this::randomRace));
return buttonsPanel;
}
private JScrollPane createTable() {
table = new JTable(new Model1Data());
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
JScrollPane scrollPane = new JScrollPane(table);
return scrollPane;
}
/**
* Simulates a race and randomly determines placings of the drivers and assigns appropriate
* points scores.
*
* #param event - encapsulates button press event
*/
private void randomRace(ActionEvent event) {
Formula1ChampionsManager.simulate();
TableModel tm = table.getModel();
if (tm instanceof Model1Data) {
Model1Data model = (Model1Data) tm;
model.setRowCount(0);
model.init();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new Test1().buildAndDisplayGui();
});
}
}
class Model1Data extends DefaultTableModel {
private static final String[] COLUMN_NAMES = {"Name", "Team", "No of first Places", "Total Points"};
public Model1Data() {
super(COLUMN_NAMES, 0);
init();
}
public void addDriver(F1Driver driver) {
addRow(new Object[]{driver.getName(),
driver.getTeam(),
driver.getFirstPlace(),
driver.getTotalPoints()});
}
public void init() {
Formula1ChampionsManager.getDrivers()
.stream()
.forEach(this::addDriver);
}
}
class Formula1ChampionsManager {
private static List<F1Driver> drivers;
static {
drivers = List.of(new F1Driver("Lewis Hamilton", F1Teams.MERCEDES),
new F1Driver("Max Verstappen",F1Teams.RED_BULL),
new F1Driver("Charles Leclerc",F1Teams.FERRARI),
new F1Driver("Lando Norris",F1Teams.MCLAREN),
new F1Driver("Fernando Alonso",F1Teams.ALPINE),
new F1Driver("Pierre Gasly",F1Teams.ALPHATAURI),
new F1Driver("Sebastian Vettel",F1Teams.ASTON_MARTIN),
new F1Driver("Nicholas Latifi",F1Teams.WILLIAMS),
new F1Driver("Valtteri Bottas",F1Teams.ALFA_ROMEO),
new F1Driver("Mick Schumacher",F1Teams.HAAS));
}
public static List<F1Driver> getDrivers() {
return drivers;
}
public static void simulate() {
List<F1Driver> result = new ArrayList<>(drivers);
Collections.shuffle(result);
F1Driver first = result.get(0);
first.update(F1Driver.FIRST);
F1Driver second = result.get(1);
second.update(F1Driver.SECOND);
F1Driver third = result.get(2);
third.update(F1Driver.THIRD);
F1Driver fourth = result.get(3);
fourth.update(F1Driver.FOURTH);
F1Driver fifth = result.get(4);
fifth.update(F1Driver.FIFTH);
F1Driver sixth = result.get(5);
sixth.update(F1Driver.SIXTH);
F1Driver seventh = result.get(6);
seventh.update(F1Driver.SEVENTH);
F1Driver eighth = result.get(7);
eighth.update(F1Driver.EIGHTH);
F1Driver ninth = result.get(8);
ninth.update(F1Driver.NINTH);
F1Driver tenth = result.get(9);
tenth.update(F1Driver.TENTH);
}
}
enum F1Teams {
MERCEDES("Mercedes"),
RED_BULL("Red Bull"),
FERRARI("Ferrari"),
MCLAREN("McLaren"),
ALPINE("Alpine"),
ALPHATAURI("AlphaTauri"),
ASTON_MARTIN("Aston Martin"),
WILLIAMS("Williams"),
ALFA_ROMEO("Alfa Romeo"),
HAAS("Haas");
private String name;
private F1Teams(String name) {
this.name = name;
}
#Override // java.lang.Enum
public String toString() {
return name;
}
}
class F1Driver {
public static final int FIRST = 25;
public static final int SECOND = 18;
public static final int THIRD = 15;
public static final int FOURTH = 12;
public static final int FIFTH = 10;
public static final int SIXTH = 8;
public static final int SEVENTH = 6;
public static final int EIGHTH = 4;
public static final int NINTH = 2;
public static final int TENTH = 1;
private String name;
private F1Teams team;
private int firstPlace;
private int totalPoints;
public F1Driver(String name, F1Teams team) {
this(name, team, 0, 0);
}
public F1Driver(String name, F1Teams team, int firstPlace, int totalPoints) {
Objects.requireNonNull(name, "Missing name.");
this.name = name;
this.team = team;
this.firstPlace = firstPlace;
this.totalPoints = totalPoints;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public F1Teams getTeam() {
return team;
}
public void setTeam(F1Teams team) {
this.team = team;
}
public int getFirstPlace() {
return firstPlace;
}
public void setFirstPlace(int firstPlace) {
this.firstPlace = firstPlace;
}
public int getTotalPoints() {
return totalPoints;
}
public void setTotalPoints(int totalPoints) {
this.totalPoints = totalPoints;
}
#Override // java.lang.Object
public boolean equals(Object obj) {
boolean equal = this == obj;
if (!equal) {
if (obj instanceof F1Driver) {
F1Driver other = (F1Driver) obj;
equal = name != null &&
name.equals(other.name) &&
team == other.team;
}
}
return equal;
}
#Override // java.lang.Object
public int hashCode() {
return (String.valueOf(name) + String.valueOf(team)).hashCode();
}
#Override // java.lang.Object
public String toString() {
return String.format("%s [%s] %d [%d]", name, team, totalPoints, firstPlace);
}
public void update(int points) {
totalPoints += points;
if (points == FIRST) {
firstPlace++;
}
}
}
Here's how it looks when I run the above code.
See at the end my SSCCE code. What I'm trying to achieve is:
Equal number of columns in the data model and columns model
Using setAutoCreateColumnsFromModel(false) to avoid recreation of columns model when a column is added or removed by the data/table model
Ability to move columns
The large button adds a new column to the end. Each new column gets a unique identifier.
The header has a right click menu HeaderMenu to remove columns. A timer calls table.tableModel.addRow() to insert a new row on the top. The data for each column is generated by class Column. In this demo the value is simply a rows counter with column's identifier.
In the actual table (not this demo)
each column is a subclass of Column and generates meaningful data
the menu also contains insert left/right and replace. This is achieved using similar as in the demo add/remove methods and by moving a column to the desired position
the data model may contain a dozen of columns and over a million rows
rows may be added with a time interval between a millisecond to several seconds, i.e. performance matters
This demo demonstrates the problem with deletion of columns which generates errors like this:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 2 >= 2
at java.util.Vector.elementAt(Vector.java:477)
at javax.swing.table.DefaultTableModel.getValueAt(DefaultTableModel.java:649)
at javax.swing.JTable.getValueAt(JTable.java:2720)
at javax.swing.JTable.prepareRenderer(JTable.java:5712)
at javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2114)
...
Please advise how to fix it. Here is the entire code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
#SuppressWarnings("serial")
public class TableDemo extends JTable {
private static class Column {
private int rowsCounter = 0;
private final String identifier;
public Column(String identifier) {
this.identifier = identifier;
}
private String nextCellValue() {
return (rowsCounter++) + ", id: " + identifier;
}
}
private static class MyTableModel extends DefaultTableModel {
private final List<Column> columns = new ArrayList<>();
private int nextColumnIdentifier = 0;
private void addRow() {
Object[] row = columns.stream().map(Column::nextCellValue).toArray();
insertRow(0, row);
}
private TableColumn addColumn() {
String identifier = String.valueOf(nextColumnIdentifier++);
columns.add(new Column(identifier));
addColumn(identifier);
TableColumn tc = new TableColumn();
tc.setIdentifier(identifier);
tc.setHeaderValue(identifier);
tc.setModelIndex(columns.size() - 1);
return tc;
}
private void removeColumn(int idx) {
columns.remove(idx);
columnIdentifiers.remove(idx);
for (Object row : dataVector) {
((Vector<?>) row).remove(idx);
}
fireTableStructureChanged();
}
}
private static class HeaderMenu extends JPopupMenu {
private int columnViewIndex;
private HeaderMenu(final TableDemo table) {
JMenuItem item = new JMenuItem("Delete column");
item.addActionListener(e -> table.deleteColumn(columnViewIndex));
add(item);
final MouseAdapter ma = new MouseAdapter() {
boolean dragged = false;
#Override
public void mouseReleased(MouseEvent e) {
if (!dragged && e.getButton() == MouseEvent.BUTTON3) {
final Point p = e.getPoint();
SwingUtilities.invokeLater(() -> {
columnViewIndex = table.columnAtPoint(p);
show(e.getComponent(), p.x, p.y);
});
}
dragged = false;
}
#Override
public void mouseDragged(MouseEvent e) {
dragged = true;
}
};
table.getTableHeader().addMouseListener(ma);
table.getTableHeader().addMouseMotionListener(ma);
}
}
private MyTableModel tableModel = new MyTableModel();
private TableDemo() {
new HeaderMenu(this);
setModel(tableModel);
setAutoCreateColumnsFromModel(false);
setDefaultEditor(Object.class, null);
}
private void addColumn() {
TableColumn tc = tableModel.addColumn();
addColumn(tc);
}
void deleteColumn(int idxView) {
TableColumn tc = getColumnModel().getColumn(idxView);
tableModel.removeColumn(tc.getModelIndex());
removeColumn(tc);
}
private static void buildAndShowGui() {
TableDemo table = new TableDemo();
table.setPreferredScrollableViewportSize(new Dimension(800, 300));
table.setFillsViewportHeight(true);
JScrollPane tableScrollPane = new JScrollPane(table);
JButton buttonAdd = new JButton("Add column");
buttonAdd.addActionListener(e -> table.addColumn());
int gaps = 10;
JPanel panel = new JPanel(new BorderLayout(gaps, gaps));
panel.setBorder(BorderFactory.createEmptyBorder(gaps, gaps, gaps, gaps));
panel.add(buttonAdd, BorderLayout.NORTH);
panel.add(tableScrollPane, BorderLayout.CENTER);
JFrame frame = new JFrame(table.getClass().getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
new Timer().schedule(new TimerTask() {
#Override
public void run() {
SwingUtilities.invokeLater(() -> table.tableModel.addRow());
}
}, 500, 100);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> buildAndShowGui());
}
}
The problem is that after removal of a column from some modelIndex in the table/data model, the TableColumn#getModelIndex() of some of the columns in the columns model may become shifted by 1. Here is an example, suppose the table has 3 columns and the code below produces 0 1 2:
for (int i = 0; i < getColumnModel().getColumnCount(); i++) {
System.out.print(getColumnModel().getColumn(i).getModelIndex() + " ");
}
Then after removal of the column 1 from both data model and column model, the output of this code becomes: 0 2. Therefore JTable produces ArrayIndexOutOfBoundsException when accessing non-existing column 2 in the data model. This is why removal of the last column didn't generate an error.
The solution is to shift the modelIndex of the columns on the right side of the removed column. This can be done with a custom TableColumnModel:
private static class MyColumnsModel extends DefaultTableColumnModel {
private TableColumn deleteColumn(int idxView) {
if (selectionModel != null) {
selectionModel.removeIndexInterval(idxView, idxView);
}
TableColumn tc = tableColumns.remove(idxView);
tc.removePropertyChangeListener(this);
for (TableColumn tableColumn : tableColumns) {
if (tableColumn.getModelIndex() > tc.getModelIndex()) {
tableColumn.setModelIndex(tableColumn.getModelIndex() - 1);
}
}
return tc;
}
}
And with add / remove methods of the table as following:
private void addColumn() {
TableColumn tc = tableModel.addColumn();
addColumn(tc); // equal to columnsModel.addColumn(tc);
}
private void deleteColumn(int idxView) {
TableColumn tc = columnsModel.deleteColumn(idxView);
tableModel.removeColumn(tc.getModelIndex());
}
Here is the entire fixed code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
#SuppressWarnings("serial")
public class TableDemo extends JTable {
private static class Column {
private int rowsCounter = 0;
private final String identifier;
public Column(String identifier) {
this.identifier = identifier;
}
private String nextCellValue() {
return (rowsCounter++) + ", id: " + identifier;
}
}
private static class MyTableModel extends DefaultTableModel {
private final List<Column> columns = new ArrayList<>();
private int nextColumnIdentifier = 0;
private void addRow() {
Object[] row = columns.stream().map(Column::nextCellValue).toArray();
insertRow(0, row);
}
private TableColumn addColumn() {
String identifier = String.valueOf(nextColumnIdentifier++);
columns.add(new Column(identifier));
addColumn(identifier);
TableColumn tc = new TableColumn();
tc.setIdentifier(identifier);
tc.setHeaderValue(identifier);
tc.setModelIndex(columns.size() - 1);
return tc;
}
private void removeColumn(int idx) {
columns.remove(idx);
columnIdentifiers.remove(idx);
for (Object row : dataVector) {
((Vector<?>) row).remove(idx);
}
fireTableStructureChanged();
}
}
private static class MyColumnsModel extends DefaultTableColumnModel {
private TableColumn deleteColumn(int idxView) {
if (selectionModel != null) {
selectionModel.removeIndexInterval(idxView, idxView);
}
TableColumn tc = tableColumns.remove(idxView);
tc.removePropertyChangeListener(this);
for (TableColumn tableColumn : tableColumns) {
if (tableColumn.getModelIndex() > tc.getModelIndex()) {
tableColumn.setModelIndex(tableColumn.getModelIndex() - 1);
}
}
return tc;
}
}
private static class HeaderMenu extends JPopupMenu {
private int columnViewIndex;
private HeaderMenu(final TableDemo table) {
JMenuItem item = new JMenuItem("Delete column");
item.addActionListener(e -> table.deleteColumn(columnViewIndex));
add(item);
final MouseAdapter ma = new MouseAdapter() {
boolean dragged = false;
#Override
public void mouseReleased(MouseEvent e) {
if (!dragged && e.getButton() == MouseEvent.BUTTON3) {
final Point p = e.getPoint();
SwingUtilities.invokeLater(() -> {
columnViewIndex = table.columnAtPoint(p);
show(e.getComponent(), p.x, p.y);
});
}
dragged = false;
}
#Override
public void mouseDragged(MouseEvent e) {
dragged = true;
}
};
table.getTableHeader().addMouseListener(ma);
table.getTableHeader().addMouseMotionListener(ma);
}
}
private MyTableModel tableModel = new MyTableModel();
private MyColumnsModel columnsModel = new MyColumnsModel();
private TableDemo() {
new HeaderMenu(this);
setModel(tableModel);
setColumnModel(columnsModel);
setAutoCreateColumnsFromModel(false);
setDefaultEditor(Object.class, null);
}
private void addColumn() {
TableColumn tc = tableModel.addColumn();
addColumn(tc); // equal to columnsModel.addColumn(tc);
}
private void deleteColumn(int idxView) {
TableColumn tc = columnsModel.deleteColumn(idxView);
tableModel.removeColumn(tc.getModelIndex());
}
private static void buildAndShowGui() {
TableDemo table = new TableDemo();
table.setPreferredScrollableViewportSize(new Dimension(800, 300));
table.setFillsViewportHeight(true);
JScrollPane tableScrollPane = new JScrollPane(table);
JButton buttonAdd = new JButton("Add column");
buttonAdd.addActionListener(e -> table.addColumn());
int gaps = 10;
JPanel panel = new JPanel(new BorderLayout(gaps, gaps));
panel.setBorder(BorderFactory.createEmptyBorder(gaps, gaps, gaps, gaps));
panel.add(buttonAdd, BorderLayout.NORTH);
panel.add(tableScrollPane, BorderLayout.CENTER);
JFrame frame = new JFrame(table.getClass().getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
new Timer().schedule(new TimerTask() {
#Override
public void run() {
SwingUtilities.invokeLater(() -> table.tableModel.addRow());
}
}, 500, 100);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> buildAndShowGui());
}
}
I am trying to make a custom rollover effect on the Collapsed Icon for a JTree. However, I am unsure how to target an individual handle instead of all the handles.
If you run the code below, you will see that when you hover over any handle, node, or leaf of the JTree all the collapsed handles will change to the rollover. This is not desired. So how can I change just a single handle when I am hovering over that handle, and preferably not when hovering over the node next to it?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.plaf.basic.*;
#SuppressWarnings("serial")
public class DirectoryExplorer extends JFrame {
private DirectoryExplorer() {
super("Directory Explorer");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(1, 1));
createPanel();
setSize(800,600);
setVisible(true);
}
private void createPanel() {
JPanel panel = new JPanel(new GridLayout(1, 1));
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Hello");
root.add(new DefaultMutableTreeNode("1"));
root.add(new DefaultMutableTreeNode("2"));
root.add(new DefaultMutableTreeNode("3"));
JTree tree = new JTree();
BasicTreeUI tUI = (BasicTreeUI) tree.getUI();
tUI.setCollapsedIcon(new ImageIcon("resources/closed.png"));
tUI.setExpandedIcon(new ImageIcon("resources/open.png"));
tree.setShowsRootHandles(true);
tree.addMouseMotionListener(new MouseHandler(tree));
panel.add(new JScrollPane(tree));
getContentPane().add(panel);
}
public static void main(String[] args) {
new DirectoryExplorer();
}
private class MouseHandler implements MouseMotionListener {
JTree t = null;
BasicTreeUI tUI = null;
public MouseHandler(JTree tree) {
t = tree;
tUI = (BasicTreeUI) tree.getUI();
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
TreePath selPath = t.getPathForLocation(e.getX(), e.getY());
if(selPath != null)
tUI.setCollapsedIcon(new ImageIcon("resources/rollover.png"));
else
tUI.setCollapsedIcon(new ImageIcon("resources/closed.png"));
t.repaint();
}
}
}
In order to achieve the desired result you need to override BasicTreeUI.paintExpandControl() and BasicTreeUI.MouseHandler.mouseMoved(). You will also need to create a few methods such as setRolloverIcon().
A working example of this might look like this
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
#SuppressWarnings("serial")
public class DirectoryExplorer extends JFrame {
private DirectoryExplorer() {
super("Directory Explorer");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(1, 1));
createPanel();
setSize(800,600);
setVisible(true);
}
private void createPanel() {
JPanel panel = new JPanel(new GridLayout(1, 1));
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Hello");
root.add(new DefaultMutableTreeNode("1"));
root.add(new DefaultMutableTreeNode("2"));
root.add(new DefaultMutableTreeNode("3"));
JTree tree = new JTree();
//UI Stuff//
TreeHandleUI tUI = new TreeHandleUI(tree);
tree.setUI(tUI);
try {
tUI.setCollapsedIcon(new ImageIcon(new URL("https://i.stack.imgur.com/nKJFv.png")));
tUI.setExpandedIcon(new ImageIcon(new URL("https://i.stack.imgur.com/NJvcp.png")));
tUI.setRolloverIcon(new ImageIcon(new URL("https://i.stack.imgur.com/jN6uX.png")));
} catch(MalformedURLException e) {
System.out.println("Bad URL / URLs");
}
////////////
tree.setShowsRootHandles(true);
panel.add(new JScrollPane(tree));
getContentPane().add(panel);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new DirectoryExplorer());
}
private class TreeHandleUI extends BasicTreeUI {
///Variables
private JTree t = null;
private Icon rolloverIcon = null;
private boolean rolloverEnabled = false;
private UpdateHandler uH = null;
private boolean isLeftToRight( Component c ) {
return c.getComponentOrientation().isLeftToRight();
}
public TreeHandleUI(JTree tree) {
t = tree;
uH = new UpdateHandler(t);
t.addMouseMotionListener(uH);
}
public void setRolloverIcon(Icon rolloverG) {
Icon oldValue = rolloverIcon;
rolloverIcon = rolloverG;
setRolloverEnabled(true);
if (rolloverIcon != oldValue) {
t.repaint();
}
}
private void setRolloverEnabled(boolean handleRolloverEnabled) {
boolean oldValue = rolloverEnabled;
rolloverEnabled = handleRolloverEnabled;
if (handleRolloverEnabled != oldValue) {
t.repaint();
}
}
#Override
protected void paintExpandControl(Graphics g,
Rectangle clipBounds, Insets insets,
Rectangle bounds, TreePath path,
int row, boolean isExpanded,
boolean hasBeenExpanded,
boolean isLeaf) {
Object value = path.getLastPathComponent();
if (!isLeaf && (!hasBeenExpanded || treeModel.getChildCount(value) > 0)) {
int middleXOfKnob;
if (isLeftToRight(t)) {
middleXOfKnob = bounds.x - getRightChildIndent() + 1;
} else {
middleXOfKnob = bounds.x + bounds.width + getRightChildIndent() - 1;
}
int middleYOfKnob = bounds.y + (bounds.height / 2);
if (isExpanded) {
Icon expandedIcon = getExpandedIcon();
if(expandedIcon != null)
drawCentered(tree, g, expandedIcon, middleXOfKnob, middleYOfKnob );
} else if(isLocationInExpandControl(path, uH.getXPos(), uH.getYPos()) && !isExpanded && rolloverEnabled) {
if(row == uH.getRow()) {
if(rolloverIcon != null)
drawCentered(tree, g, rolloverIcon, middleXOfKnob, middleYOfKnob);
} else {
Icon collapsedIcon = getCollapsedIcon();
if(collapsedIcon != null)
drawCentered(tree, g, collapsedIcon, middleXOfKnob, middleYOfKnob);
}
} else {
Icon collapsedIcon = getCollapsedIcon();
if(collapsedIcon != null)
drawCentered(tree, g, collapsedIcon, middleXOfKnob, middleYOfKnob);
}
}
}
private class UpdateHandler extends BasicTreeUI.MouseHandler {
private JTree t = null;
private int xPos = 0;
private int yPos = 0;
private boolean leftToRight(Component c) {
return c.getComponentOrientation().isLeftToRight();
}
public UpdateHandler(JTree tree) {
t = tree;
}
#Override
public void mouseMoved(MouseEvent e) {
xPos = e.getX();
yPos = e.getY();
t.repaint();
}
public int getXPos() {
return xPos;
}
public int getYPos() {
return yPos;
}
public int getRow() {
return getRowForPath(t, getClosestPathForLocation(t, xPos, yPos));
}
}
}
}
Code will run without downloading images however they are available below
closed.png
open.png
rollover.png
A Stepped ComboBox is very useful to make the drop-down pop-up wider than the text field. However when new content is added to the list, the pop-up gets its initial width back.
By default
After refresh (new element)
SSCCE
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;
public class SteppedComboBoxRefresh extends JFrame {
private List<String> list;
private final SteppedComboBox combo;
public SteppedComboBoxRefresh() {
super("SteppedComboBox Refresh");
this.list = new ArrayList<String>(Arrays.asList(new String[]{
"AAA", "AAAAAA"
}));
this.combo = new SteppedComboBox(this.list.toArray());
this.combo.setDimensions(50);
JButton addButton = new JButton("Add longer string");
addButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
list.add(list.get(list.size()-1) + "AAA");
combo.setModel(new DefaultComboBoxModel(list.toArray()));
combo.setDimensions(50);
}
});
getContentPane().setLayout(new FlowLayout());
getContentPane().add(this.combo);
getContentPane().add(addButton);
}
public static void main (String args[]) {
SteppedComboBoxRefresh f = new SteppedComboBoxRefresh();
f.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.setSize (300, 100);
f.setVisible(true);
}
}
class SteppedComboBoxUI extends MetalComboBoxUI {
#Override
protected ComboPopup createPopup() {
BasicComboPopup popup = new BasicComboPopup( this.comboBox ) {
#Override
public void show() {
Dimension popupSize = ((SteppedComboBox)this.comboBox).getPopupSize();
popupSize.setSize( popupSize.width,
getPopupHeightForRowCount( this.comboBox.getMaximumRowCount() ) );
Rectangle popupBounds = computePopupBounds( 0,
this.comboBox.getBounds().height, popupSize.width, popupSize.height);
this.scroller.setMaximumSize( popupBounds.getSize() );
this.scroller.setPreferredSize( popupBounds.getSize() );
this.scroller.setMinimumSize( popupBounds.getSize() );
this.list.invalidate();
int selectedIndex = this.comboBox.getSelectedIndex();
if ( selectedIndex == -1 ) {
this.list.clearSelection();
} else {
this.list.setSelectedIndex( selectedIndex );
}
this.list.ensureIndexIsVisible( this.list.getSelectedIndex() );
setLightWeightPopupEnabled( this.comboBox.isLightWeightPopupEnabled() );
show( this.comboBox, popupBounds.x, popupBounds.y );
}
};
popup.getAccessibleContext().setAccessibleParent(this.comboBox);
return popup;
}
}
class SteppedComboBox extends JComboBox {
protected int popupWidth;
public SteppedComboBox(ComboBoxModel aModel) {
super(aModel);
setUI(new SteppedComboBoxUI());
this.popupWidth = 0;
}
public SteppedComboBox(final Object[] items) {
super(items);
setUI(new SteppedComboBoxUI());
this.popupWidth = 0;
}
public SteppedComboBox(Vector items) {
super(items);
setUI(new SteppedComboBoxUI());
this.popupWidth = 0;
}
public void setPopupWidth(int width) {
this.popupWidth = width;
}
public Dimension getPopupSize() {
Dimension size = getSize();
if (this.popupWidth < 1) {
this.popupWidth = size.width;
}
return new Dimension(this.popupWidth, size.height);
}
public void setDimensions(int width) {
Dimension d = getPreferredSize();
setPreferredSize(new Dimension(width, d.height));
setPopupWidth(d.width);
}
}
The ComboBox still uses its previous PreferredSize. It's needed to set the preferred size back to null, so that we get the size which is preferred by the new content in the list.
void javax.swing.JComponent.setPreferredSize(Dimension preferredSize)
Sets the preferred size of this component. If preferredSize is null, the UI will be asked for the preferred size.
public void setDimensions(int width) {
setPreferredSize(null);
Dimension d = getPreferredSize();
setPreferredSize(new Dimension(width, d.height));
setPopupWidth(d.width);
}
Result
You could use the Combo Box Popup.
It is a more flexible version of the Stepped Combo Box. Best of all it can be used on any combo box since the logic is implemented in a `PopupMenuListener'.
You can control the maximum width of the popup. You can even have the popup display above the combo box instead of below.
I need to display JList with custom JPanel components called Item. These components have unique identification name. They may be dynamically added to the JList, or just updated (if already exist). I try the following implementation, but it produces just an empty JList. Please advise.
class Item extends JPanel {
JLabel name = new JLabel(" ");
JLabel col1 = new JLabel(" ");
JLabel col2 = new JLabel(" ");
Item(){
setMinimumSize(new Dimension(100, 20));
setLayout(new BorderLayout());
add(name, BorderLayout.WEST);
add(col1, BorderLayout.CENTER);
add(col2, BorderLayout.EAST);
}
}
public class Test_List extends JFrame {
private final JList list = new JList(new Item[0]);
HashMap<String, Item> map = new HashMap<String, Item>();
Test_List(){
setTitle("Test JList");
setLayout(new BorderLayout());
add(new JScrollPane(list), BorderLayout.CENTER);
pack();
}
public void update_item(String name, String s1, String s2){
Item item = map.get(name);
if (item == null){ // add new
item = new Item();
item.name.setText(name);
map.put(name, item);
list.add(item);
}
item.col1.setText(s1);
item.col2.setText(s2);
}
public static void main(String s[]) {
final Test_List frame = new Test_List();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.setVisible(true);
}
});
frame.update_item("A", "a", "aa"); // add new item
frame.update_item("B", "b", "bb"); // add new item
frame.update_item("A", "aa", "a"); // update existing item
}
}
A model is responsible for modeling data, it should NEVER contain UI specific information, as the model shouldn't care how it is to be displayed, it is responsible for transforming the data so it confirms to the view/model contract, in this case the ListModel contract.
Start by taking a look at Writing a Custom Cell Renderer
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
public class TestListCellRenderer {
public static void main(String[] args) {
new TestListCellRenderer();
}
public TestListCellRenderer() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
DefaultListModel model = new DefaultListModel();
model.addElement(new Item("A", "a", "aa"));
model.addElement(new Item("B", "b", "bb"));
model.addElement(new Item("C", "c", "cc"));
model.addElement(new Item("D", "d", "dd"));
JList list = new JList(model);
list.setCellRenderer(new ItemCellRenderer());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(list));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class Item {
private String name;
private String col1;
private String col2;
public Item(String name, String col1, String col2) {
this.name = name;
this.col1 = col1;
this.col2 = col2;
}
public String getCol1() {
return col1;
}
public String getCol2() {
return col2;
}
public String getName() {
return name;
}
}
public static class ItemCellRenderer extends JPanel implements ListCellRenderer<Item>{
private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
protected static Border noFocusBorder = DEFAULT_NO_FOCUS_BORDER;
JLabel name = new JLabel(" ");
JLabel col1 = new JLabel(" ");
JLabel col2 = new JLabel(" ");
public ItemCellRenderer() {
setLayout(new BorderLayout());
add(name, BorderLayout.WEST);
add(col1, BorderLayout.CENTER);
add(col2, BorderLayout.EAST);
}
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 20);
}
#Override
public Dimension getPreferredSize() {
return getMinimumSize();
}
protected Border getNoFocusBorder() {
Border border = UIManager.getBorder("List.cellNoFocusBorder");
if (System.getSecurityManager() != null) {
if (border != null) return border;
return SAFE_NO_FOCUS_BORDER;
} else {
if (border != null &&
(noFocusBorder == null ||
noFocusBorder == DEFAULT_NO_FOCUS_BORDER)) {
return border;
}
return noFocusBorder;
}
}
#Override
public Component getListCellRendererComponent(JList<? extends Item> list, Item value, int index, boolean isSelected, boolean cellHasFocus) {
setComponentOrientation(list.getComponentOrientation());
Color bg = null;
Color fg = null;
JList.DropLocation dropLocation = list.getDropLocation();
if (dropLocation != null
&& !dropLocation.isInsert()
&& dropLocation.getIndex() == index) {
bg = UIManager.getColor("List.dropCellBackground");
fg = UIManager.getColor("List.dropCellForeground");
isSelected = true;
}
if (isSelected) {
setBackground(bg == null ? list.getSelectionBackground() : bg);
setForeground(fg == null ? list.getSelectionForeground() : fg);
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
name.setText(value.getName());
col1.setText(value.getCol1());
col2.setText(value.getCol2());
name.setForeground(getForeground());
col1.setForeground(getForeground());
col2.setForeground(getForeground());
setEnabled(list.isEnabled());
name.setFont(list.getFont());
col1.setFont(list.getFont());
col2.setFont(list.getFont());
Border border = null;
if (cellHasFocus) {
if (isSelected) {
border = UIManager.getBorder("List.focusSelectedCellHighlightBorder");
}
if (border == null) {
border = UIManager.getBorder("List.focusCellHighlightBorder");
}
} else {
border = getNoFocusBorder();
}
setBorder(border);
return this;
}
}
}
Having said all that, I think you would be better off with a using a JTable, see How to Use Tables