X-Git-Url: http://git.nikiroo.be/?p=fanfix.git;a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Futils%2Fui%2FBreadCrumbsBar.java;fp=src%2Fbe%2Fnikiroo%2Futils%2Fui%2FBreadCrumbsBar.java;h=8b993e64b58706c4bf1b567d482ff1618deae43d;hp=0000000000000000000000000000000000000000;hb=f19b48e27a56ebab18687debd9ef52581a03f06d;hpb=dfa4091ccd9f46687c7326aa6bf2eaf588a7cb83 diff --git a/src/be/nikiroo/utils/ui/BreadCrumbsBar.java b/src/be/nikiroo/utils/ui/BreadCrumbsBar.java new file mode 100644 index 0000000..8b993e6 --- /dev/null +++ b/src/be/nikiroo/utils/ui/BreadCrumbsBar.java @@ -0,0 +1,225 @@ + +package be.nikiroo.utils.ui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +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.getChildren().isEmpty()) { + // TODO (see things with icons included in 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); + } + } + } + }); + + new SwingWorker, Void>() { + @Override + protected DataNode doInBackground() throws Exception { + tree.loadData(); + return tree.getRoot(); + } + + @Override + protected void done() { + try { + node = get(); + 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); + } + } +}