JCombobox with JMenuBar (as drop down menu)
Hello, i'm trying to make a new component (as generic as possible) to display a tree structure in a combo-like style.
So i finally choose a JTextfield to display the current item + a JMenuBar with Menu and MenuItem generated from a TreeModel (i'm not sure this is the better choice to make, but tha's not my present problem).
2 questions :
1) am I reinventing the wheel ?
2) i've got a problem of pack when displaying the compent and i don't find where.
Thanks. Cyril.
--
So, the code... two main classes : TreeComboBox + ArrowMenu
the TreeComboBox :
package fr.emanation.util.gui.treeComboBox;
import javax.swing.*;
import javax.swing.tree.TreeModel;
import java.awt.*;
import java.awt.event.FocusListener;
publicclass TreeComboBox<TNode>extends JPanel
{
private TNode theSelectedNode;
private ArrowMenuBar theMenuBar;
private JTextField theTextField;
privateint theCompactWidth;
public TreeComboBox(TreeModel aTreeModel)
{
new BorderLayout(0, 0);
theTextField =new JTextField();
theTextField.setEditable(true);
theTextField.setBackground(Color.white);
setFocusable(false);
add(theTextField, BorderLayout.CENTER);
MyArrowMenuInvoker anInvoker =new MyArrowMenuInvoker();
theMenuBar =new ArrowMenuBar<TNode>(anInvoker, aTreeModel);
JPanel menuBarPanel =new JPanel(new BorderLayout(0, 0));
menuBarPanel.add(theMenuBar, BorderLayout.CENTER);
add(menuBarPanel, BorderLayout.EAST);
}
public TNode getNode()
{
return (TNode) theSelectedNode;
}
publicvoid setSelectedNode(TNode aNode)
{
theSelectedNode = aNode;
theTextField.setText(aNode.toString());
}
public TreeModel getModel()
{
return theMenuBar.getModel();
}
public JComponent[] getFocusableComponents()
{
returnnew JComponent[]{theTextField, theMenuBar};
}
//-- JComponent overriden methods
public Dimension getMinimumSize()
{
final Dimension minSize = super.getMinimumSize();
minSize.width = theCompactWidth;
return minSize;
}
publicvoid setCompactWidth(finalint aCompactWidth)
{
theCompactWidth = aCompactWidth;
}
publicvoid setPreferredSize(Dimension aSize)
{
super.setPreferredSize(aSize);
if (theTextField !=null)
{
theTextField.setPreferredSize(aSize);
}
}
publicvoid setForeground(Color fg)
{
super.setForeground(fg);
if (theMenuBar !=null)
{
for (int index = 0; index < theMenuBar.getComponentCount(); index++)
{
theMenuBar.getComponents()[index].setForeground(fg);
}
}
if (theTextField !=null)
{
theTextField.setForeground(fg);
}
}
publicvoid setBackground(Color bg)
{
super.setBackground(bg);
if (theMenuBar !=null)
{
for (int index = 0; index < theMenuBar.getComponentCount(); index++)
{
theMenuBar.getComponents()[index].setBackground(bg);
}
}
if (theTextField !=null)
{
theTextField.setBackground(bg);
}
}
publicvoid setFont(Font aFont)
{
super.setFont(aFont);
if (theMenuBar !=null)
{
theMenuBar.setFont(aFont);
}
if (theTextField !=null)
{
theTextField.setFont(aFont);
}
}
publicvoid setToolTipText(String aToolTip)
{
super.setToolTipText(aToolTip);
if (theTextField !=null)
{
theTextField.setToolTipText(aToolTip);
}
}
publicvoid setEnabled(finalboolean bEnabled)
{
super.setEnabled(bEnabled);
if (theTextField !=null)return;
{
theTextField.setEnabled(bEnabled);
if (bEnabled)
{
theTextField.setBackground(Color.WHITE);
}
else
{
JLabel tmpLabel =new JLabel();
tmpLabel.setEnabled(false);
theTextField.setBackground(tmpLabel.getBackground());
}
}
if (theMenuBar !=null)
{
theMenuBar.setEnabled(bEnabled);
}
}
publicsynchronizedvoid addFocusListener(FocusListener l)
{
super.addFocusListener(l);
if (theMenuBar !=null)
{
theMenuBar.addFocusListener(l);
}
if (theTextField !=null)
{
theTextField.addFocusListener(l);
}
}
publicsynchronizedvoid removeFocusListener(FocusListener l)
{
super.removeFocusListener(l);
if (theMenuBar !=null)
{
theMenuBar.removeFocusListener(l);
}
if (theTextField !=null)
{
theTextField.removeFocusListener(l);
}
}
publicboolean isFocusOwner()
{
if (theMenuBar ==null || theTextField ==null)return super.isFocusOwner();
boolean bM = (theMenuBar ==null) && theMenuBar.isFocusOwner();
boolean bT = (theTextField ==null) && theTextField.isFocusOwner();
return (bM || bT);
}
//-- inner classes
privateclass MyArrowMenuInvokerimplements IArrowMenuInvoker<TNode>
{
public Component getComponent()
{
return theTextField;
}
publicvoid setNode(TNode aNode)
{
setSelectedNode(aNode);
}
}
}
the ArrowMenuBar :
package fr.emanation.util.gui.treeComboBox;
import javax.swing.*;
import javax.swing.border.EtchedBorder;
import javax.swing.plaf.UIResource;
import javax.swing.tree.TreeModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
publicclass ArrowMenuBar<TNode>extends JMenuBar
{
private NodeMenu theMenu;
private TreeModel theModel;
private IArrowMenuInvoker<TNode> theInvoker;
public ArrowMenuBar(IArrowMenuInvoker<TNode> anInvoker, TreeModel aModel)
{
super();
theModel = aModel;
theInvoker = anInvoker;
TNode root = (TNode) theModel.getRoot();
if (!theModel.isLeaf(root))
{
theMenu =new ArrowMenu(root);
theMenu.setBackground(new JPanel().getBackground());
}
MenuItemListener listener =new MenuItemListener();
setListener(theMenu, listener);
add(theMenu);
}
privatevoid setListener(JMenuItem anItem, ActionListener aListener)
{
if (anIteminstanceof JMenu)
{
JMenu menu = (JMenu) anItem;
int n = menu.getItemCount();
for (int index = 0; index < n; index++)
{
setListener(menu.getItem(index), aListener);
}
}
elseif (anItem !=null)
{
// null means separator
anItem.addActionListener(aListener);
}
}
public TreeModel getModel()
{
return theModel;
}
public Action getLaunchArrowMenuBarAction()
{
returnnew LaunchArrowMenuBarAction();
}
//-- overrides JComponent
public Dimension getPreferredSize()
{
return theMenu.getPreferredSize();
}
private Dimension getItemSize(JMenu aMenu)
{
Dimension dimension =new Dimension(0, 0);
int n = aMenu.getItemCount();
for (int index = 0; index < n; index++)
{
Dimension itemD;
JMenuItem item = aMenu.getItem(index);
if (iteminstanceof JMenu)
{
itemD = getItemSize((JMenu) item);
}
elseif (item !=null)
{
itemD = item.getPreferredSize();
}
else
{
itemD =new Dimension(0, 0);// separator
}
dimension.width = Math.max(dimension.width, itemD.width);
dimension.height = Math.max(dimension.height, itemD.height);
}
return dimension;
}
// inner classes
privateclass LaunchArrowMenuBarActionextends AbstractAction
{
public LaunchArrowMenuBarAction()
{
super("...");
}
publicvoid actionPerformed(ActionEvent e)
{
if (e.getActionCommand() =="Launch")
{
System.out.println("Launch");
}
}
}
privateclass MenuItemListenerimplements ActionListener
{
publicvoid actionPerformed(ActionEvent anEvent)
{
NodeMenuItem item = (NodeMenuItem) anEvent.getSource();
theInvoker.setNode(item.getNode());
theMenu.requestFocus();
}
}
privateclass NodeMenuextends JMenu
{
private TNode theNode;
public NodeMenu(TNode aNode)
{
this(aNode.toString(), aNode);
}
public NodeMenu(String theText, TNode aNode)
{
super(theText);
theNode = aNode;
add(new NodeMenuItem("[.]", aNode));
for (int index = 0; index < theModel.getChildCount(aNode); index++)
{
TNode childNode = (TNode) theModel.getChild(aNode, index);
if (theModel.isLeaf(childNode))
{
add(new NodeMenuItem(childNode));
}
else
{
add(new NodeMenu(childNode));
}
}
}
publicvoid setNode(TNode aNode)
{
theNode = aNode;
}
public TNode getNode()
{
return theNode;
}
}
privateclass NodeMenuItemextends JMenuItem
{
private TNode theNode;
public NodeMenuItem(TNode aNode)
{
super(aNode.toString());
theNode = aNode;
}
public NodeMenuItem(String aText, TNode aNode)
{
super(aText);
theNode = aNode;
}
public TNode getNode()
{
return theNode;
}
}
privateclass ArrowMenuextends NodeMenu
{
//private ArrowIcon theIconRenderer;
private Color shadow = UIManager.getColor("controlShadow");
private Color darkShadow = UIManager.getColor("controlDkShadow");
private Color highlight = UIManager.getColor("controlLtHighlight");
public ArrowMenu(TNode aNode)
{
super("", aNode);
//theIconRenderer = new ArrowIcon(SwingConstants.SOUTH, true);
setBorder(new EtchedBorder());
//setIcon(new BlankIcon(null, 11));
setHorizontalTextPosition(JButton.LEFT);
setFocusPainted(true);
}
publicvoid paint(Graphics g)
{
Color origColor;
boolean isEnabled;
int w, h, size;
w = getSize().width;
h = getSize().height;
origColor = g.getColor();
isEnabled = isEnabled();
g.setColor(getBackground());
g.fillRect(1, 1, w - 2, h - 2);
/// Draw the proper Border
if (getBorder() !=null && !(getBorder()instanceof UIResource))
{
paintBorder(g);
}
else
{
// Using the background color set above
g.drawLine(0, 0, 0, h - 1);
g.drawLine(1, 0, w - 2, 0);
g.setColor(highlight);// inner 3D border
g.drawLine(1, 1, 1, h - 3);
g.drawLine(2, 1, w - 3, 1);
g.setColor(shadow);// inner 3D border
g.drawLine(1, h - 2, w - 2, h - 2);
g.drawLine(w - 2, 1, w - 2, h - 3);
g.setColor(darkShadow);// black drop shadow __|
g.drawLine(0, h - 1, w - 1, h - 1);
g.drawLine(w - 1, h - 1, w - 1, 0);
}
// If there's no room to draw arrow, bail
if (h < 5 || w < 5)
{
g.setColor(origColor);
return;
}
// Draw the arrow
size = Math.min((h - 4) / 3, (w - 4) / 3);
size = Math.max(size, 2);
paintTriangle(g, (w - size) / 2, (h - size) / 2,
size, SwingConstants.SOUTH, isEnabled);
// Reset the Graphics back to it's original settings
g.setColor(origColor);
}
public Dimension getPreferredSize()
{
returnnew Dimension(16, 16);
}
public Dimension getMinimumSize()
{
returnnew Dimension(5, 5);
}
public Dimension getMaximumSize()
{
returnnew Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
publicvoid paintTriangle(Graphics g,int x,int y,int size,
int direction,boolean isEnabled)
{
Color oldColor = g.getColor();
int mid, i, j;
j = 0;
size = Math.max(size, 2);
mid = (size / 2) - 1;
g.translate(x, y);
if (isEnabled)
{
g.setColor(darkShadow);
}
else
{
g.setColor(shadow);
}
switch (direction)
{
case NORTH:
for (i = 0; i < size; i++)
{
g.drawLine(mid - i, i, mid + i, i);
}
if (!isEnabled)
{
g.setColor(highlight);
g.drawLine(mid - i + 2, i, mid + i, i);
}
break;
case SOUTH:
if (!isEnabled)
{
g.translate(1, 1);
g.setColor(highlight);
for (i = size - 1; i >= 0; i--)
{
g.drawLine(mid - i, j, mid + i, j);
j++;
}
g.translate(-1, -1);
g.setColor(shadow);
}
j = 0;
for (i = size - 1; i >= 0; i--)
{
g.drawLine(mid - i, j, mid + i, j);
j++;
}
break;
case WEST:
for (i = 0; i < size; i++)
{
g.drawLine(i, mid - i, i, mid + i);
}
if (!isEnabled)
{
g.setColor(highlight);
g.drawLine(i, mid - i + 2, i, mid + i);
}
break;
case EAST:
if (!isEnabled)
{
g.translate(1, 1);
g.setColor(highlight);
for (i = size - 1; i >= 0; i--)
{
g.drawLine(j, mid - i, j, mid + i);
j++;
}
g.translate(-1, -1);
g.setColor(shadow);
}
j = 0;
for (i = size - 1; i >= 0; i--)
{
g.drawLine(j, mid - i, j, mid + i);
j++;
}
break;
}
g.translate(-x, -y);
g.setColor(oldColor);
}
}
}

