By the time I wrote and tested the code below, you probably got lot of better answers...
I don't mind as I enjoyed the experiment/learning (still a bit green on the Swing front).
The base idea is to use a renderer for the items of the combo box. For most items, it is a simple JLabel with the text of the item. For the last recent/most used item, I decorate the JLabel with a custom border drawing a line on its bottom.
The following code is still a bit rough, just a quick test of the concept. A base to improve upon.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
public class TestGUI extends JFrame
{
private JComboBox m_combo;
private ListCellRenderer m_renderer;
private int m_lastRecentIndex;
public TestGUI()
{
setTitle("Test GUI");
setSize(400, 100);
Container cont = getContentPane();
cont.setLayout(new FlowLayout());
cont.add(new JLabel("Data: ")) ;
String[] itemsOther = new String[] { "one", "two", "three" };
m_combo = new JComboBox(itemsOther);
String[] itemsRecent = new String[] { "ichi", "ni", "san" };
m_lastRecentIndex = itemsRecent.length - 1;
for (int i = 0; i < itemsRecent.length; i++)
{
m_combo.insertItemAt(itemsRecent[i], i);
}
m_renderer = new JLRenderer();
m_combo.setRenderer(m_renderer);
m_combo.setSelectedIndex(-1);
cont.add(m_combo);
m_combo.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
System.out.println(m_combo.getSelectedItem().toString());
}
});
}
class JLRenderer extends JLabel implements ListCellRenderer
{
JLabel m_lastRecent;
public JLRenderer()
{
m_lastRecent = new JLabel();
m_lastRecent.setBorder(new BottomLineBorder(Color.BLACK));
}
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus)
{
if (value == null)
{
value = "Select an option";
}
if (index == m_lastRecentIndex)
{
m_lastRecent.setText(value.toString());
return m_lastRecent;
}
setText(value.toString());
return this;
}
class BottomLineBorder implements Border
{
private Color m_color;
BottomLineBorder()
{
this(Color.BLACK);
}
BottomLineBorder(Color color)
{
m_color = color;
}
public void paintBorder(Component c, Graphics g,
int x, int y, int width, int height)
{
Graphics copy = g.create();
if (copy != null)
{
try
{
copy.translate(x, y);
copy.setColor(m_color);
copy.drawLine(0, height - 1, width - 1, height - 1);
}
finally
{
copy.dispose();
}
}
}
public boolean isBorderOpaque()
{
return true;
}
public Insets getBorderInsets(Component c)
{
return new Insets(0, 0, 1, 0);
}
}
}
public static void main(String[] args)
{
TestGUI gui = new TestGUI();
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.setVisible(true);
}
}
Yes, I use non standard brace alignment and members/fields decoration... So sue me! ;-)
[EDIT] Just for fun, I also made a class for a thicker separation. I give it because it took me some time to find out I had to override the second getBorderInsets to declare the extra space it needs. So it might be useful to somebody else.
class BottomThickLineBorder extends AbstractBorder
{
private int m_thickness;
private Color m_color;
BottomThickLineBorder()
{
this(1, Color.BLACK);
}
BottomThickLineBorder(int thickness, Color color)
{
m_thickness = thickness;
m_color = color;
}
public void paintBorder(Component c, Graphics g,
int x, int y, int width, int height)
{
Graphics copy = g.create();
if (copy != null)
{
try
{
copy.translate(x, y);
copy.setColor(m_color);
copy.fillRect(0, height - m_thickness, width - 1, height - 1);
}
finally
{
copy.dispose();
}
}
}
public boolean isBorderOpaque()
{
return true;
}
public Insets getBorderInsets(Component c, Insets i)
{
return new Insets(0, 0, m_thickness, 0);
}
}