package be.nikiroo.utils.ui; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; import javax.swing.BoxLayout; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JToggleButton; import javax.swing.SwingWorker; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; public class BreadCrumbsBar extends ListenerPanel { private class BreadCrumb extends JPanel { private JToggleButton button; private JToggleButton down; public BreadCrumb(final DataNode node) { this.setLayout(new BorderLayout()); if (!node.isRoot()) { button = new JToggleButton(node.toString()); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { button.setSelected(false); if (!node.isRoot()) { // TODO: allow clicking on root? option? setSelectedNode(node); } } }); this.add(button, BorderLayout.CENTER); } if ((node.isRoot() && node.getChildren().isEmpty()) || !node.getChildren().isEmpty()) { // TODO allow an image or ">", viewer down = new JToggleButton(">"); final JPopupMenu popup = new JPopupMenu(); for (final DataNode child : node.getChildren()) { popup.add(new AbstractAction(child.toString()) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { setSelectedNode(child); } }); } down.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ev) { if (down.isSelected()) { popup.show(down, 0, down.getBounds().height); } else { popup.setVisible(false); } } }); popup.addPopupMenuListener(new PopupMenuListener() { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { } @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { down.setSelected(false); } @Override public void popupMenuCanceled(PopupMenuEvent e) { } }); this.add(down, BorderLayout.EAST); } } } static public final String CHANGE_ACTION = "change"; private boolean vertical; private DataNode node; private List crumbs = new ArrayList(); public BreadCrumbsBar(final DataTree tree) { vertical = true; // to force an update setVertical(false); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { super.componentResized(e); synchronized (crumbs) { for (BreadCrumb crumb : crumbs) { setCrumbSize(crumb); } } } }); setSelectedNode(new DataNode(null, null)); new SwingWorker, Void>() { @Override protected DataNode doInBackground() throws Exception { tree.loadData(); return tree.getRoot(); } @Override protected void done() { try { DataNode node = get(); setSelectedNode(null); BreadCrumbsBar.this.node = node; addCrumb(node); // TODO: option? if (node.size() > 0) { setSelectedNode(node.getChildren().get(0)); } else { revalidate(); repaint(); } } catch (Exception e) { e.printStackTrace(); } } }.execute(); } public void setVertical(boolean vertical) { if (vertical != this.vertical) { synchronized (crumbs) { this.vertical = vertical; for (BreadCrumb crumb : crumbs) { this.remove(crumb); } if (vertical) { this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); } else { this.setLayout(new WrapLayout(WrapLayout.LEADING)); } for (BreadCrumb crumb : crumbs) { this.add(crumb); setCrumbSize(crumb); } } this.revalidate(); this.repaint(); } } public DataNode getSelectedNode() { return node; } public void setSelectedNode(DataNode node) { if (this.node == node) { return; } synchronized (crumbs) { // clear until common ancestor (can clear all!) while (this.node != null && !this.node.isParentOf(node)) { this.node = this.node.getParent(); this.remove(crumbs.remove(crumbs.size() - 1)); } // switch root if needed and possible if (this.node == null && node != null) { this.node = node.getRoot(); addCrumb(this.node); } // re-create until node while (node != null && this.node != node) { DataNode ancestorOrNode = node; for (DataNode child : this.node.getChildren()) { if (child.isParentOf(node)) ancestorOrNode = child; } this.node = ancestorOrNode; addCrumb(this.node); } } this.revalidate(); this.repaint(); fireActionPerformed(CHANGE_ACTION); } private void addCrumb(DataNode node) { BreadCrumb crumb = new BreadCrumb(node); this.crumbs.add(crumb); setCrumbSize(crumb); this.add(crumb); } private void setCrumbSize(BreadCrumb crumb) { if (vertical) { crumb.setMaximumSize(new Dimension(this.getWidth(), crumb.getMinimumSize().height)); } else { crumb.setMaximumSize(null); } } }