2 package be
.nikiroo
.utils
.ui
;
4 import java
.awt
.BorderLayout
;
5 import java
.awt
.Dimension
;
6 import java
.awt
.event
.ActionEvent
;
7 import java
.awt
.event
.ActionListener
;
8 import java
.awt
.event
.ComponentAdapter
;
9 import java
.awt
.event
.ComponentEvent
;
10 import java
.util
.ArrayList
;
11 import java
.util
.List
;
13 import javax
.swing
.AbstractAction
;
14 import javax
.swing
.BoxLayout
;
15 import javax
.swing
.JPanel
;
16 import javax
.swing
.JPopupMenu
;
17 import javax
.swing
.JToggleButton
;
18 import javax
.swing
.SwingWorker
;
19 import javax
.swing
.event
.PopupMenuEvent
;
20 import javax
.swing
.event
.PopupMenuListener
;
22 public class BreadCrumbsBar
<T
> extends ListenerPanel
{
23 private class BreadCrumb
extends JPanel
{
24 private JToggleButton button
;
25 private JToggleButton down
;
27 public BreadCrumb(final DataNode
<T
> node
) {
28 this.setLayout(new BorderLayout());
31 button
= new JToggleButton(node
.toString());
32 button
.addActionListener(new ActionListener() {
34 public void actionPerformed(ActionEvent e
) {
35 button
.setSelected(false);
37 // TODO: allow clicking on root? option?
38 setSelectedNode(node
);
43 this.add(button
, BorderLayout
.CENTER
);
46 if ((node
.isRoot() && node
.getChildren().isEmpty())
47 || !node
.getChildren().isEmpty()) {
48 // TODO allow an image or ">", viewer
49 down
= new JToggleButton(">");
50 final JPopupMenu popup
= new JPopupMenu();
52 for (final DataNode
<T
> child
: node
.getChildren()) {
53 popup
.add(new AbstractAction(child
.toString()) {
54 private static final long serialVersionUID
= 1L;
57 public void actionPerformed(ActionEvent e
) {
58 setSelectedNode(child
);
63 down
.addActionListener(new ActionListener() {
65 public void actionPerformed(ActionEvent ev
) {
66 if (down
.isSelected()) {
67 popup
.show(down
, 0, down
.getBounds().height
);
69 popup
.setVisible(false);
74 popup
.addPopupMenuListener(new PopupMenuListener() {
76 public void popupMenuWillBecomeVisible(PopupMenuEvent e
) {
80 public void popupMenuWillBecomeInvisible(PopupMenuEvent e
) {
81 down
.setSelected(false);
85 public void popupMenuCanceled(PopupMenuEvent e
) {
89 this.add(down
, BorderLayout
.EAST
);
94 static public final String CHANGE_ACTION
= "change";
96 private boolean vertical
;
97 private DataNode
<T
> node
;
98 private List
<BreadCrumb
> crumbs
= new ArrayList
<BreadCrumb
>();
100 public BreadCrumbsBar(final DataTree
<T
> tree
) {
101 vertical
= true; // to force an update
104 addComponentListener(new ComponentAdapter() {
106 public void componentResized(ComponentEvent e
) {
107 super.componentResized(e
);
108 synchronized (crumbs
) {
109 for (BreadCrumb crumb
: crumbs
) {
116 setSelectedNode(new DataNode
<T
>(null, null));
118 new SwingWorker
<DataNode
<T
>, Void
>() {
120 protected DataNode
<T
> doInBackground() throws Exception
{
122 return tree
.getRoot();
126 protected void done() {
128 DataNode
<T
> node
= get();
130 setSelectedNode(null);
131 BreadCrumbsBar
.this.node
= node
;
135 if (node
.size() > 0) {
136 setSelectedNode(node
.getChildren().get(0));
141 } catch (Exception e
) {
148 public void setVertical(boolean vertical
) {
149 if (vertical
!= this.vertical
) {
150 synchronized (crumbs
) {
151 this.vertical
= vertical
;
153 for (BreadCrumb crumb
: crumbs
) {
158 this.setLayout(new BoxLayout(this, BoxLayout
.Y_AXIS
));
160 this.setLayout(new WrapLayout(WrapLayout
.LEADING
));
163 for (BreadCrumb crumb
: crumbs
) {
174 public DataNode
<T
> getSelectedNode() {
178 public void setSelectedNode(DataNode
<T
> node
) {
179 if (this.node
== node
) {
183 synchronized (crumbs
) {
184 // clear until common ancestor (can clear all!)
185 while (this.node
!= null && !this.node
.isParentOf(node
)) {
186 this.node
= this.node
.getParent();
187 this.remove(crumbs
.remove(crumbs
.size() - 1));
190 // switch root if needed and possible
191 if (this.node
== null && node
!= null) {
192 this.node
= node
.getRoot();
196 // re-create until node
197 while (node
!= null && this.node
!= node
) {
198 DataNode
<T
> ancestorOrNode
= node
;
199 for (DataNode
<T
> child
: this.node
.getChildren()) {
200 if (child
.isParentOf(node
))
201 ancestorOrNode
= child
;
204 this.node
= ancestorOrNode
;
212 fireActionPerformed(CHANGE_ACTION
);
215 private void addCrumb(DataNode
<T
> node
) {
216 BreadCrumb crumb
= new BreadCrumb(node
);
217 this.crumbs
.add(crumb
);
222 private void setCrumbSize(BreadCrumb crumb
) {
224 crumb
.setMaximumSize(new Dimension(this.getWidth(),
225 crumb
.getMinimumSize().height
));
227 crumb
.setMaximumSize(null);