1 package de.matthias_burbach.mosaique.swing;
2
3 import java.awt.event.ActionEvent;
4 import java.awt.event.MouseAdapter;
5 import java.awt.event.MouseEvent;
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.Comparator;
9 import java.util.Enumeration;
10 import java.util.Iterator;
11 import java.util.List;
12
13 import javax.swing.AbstractAction;
14 import javax.swing.JPopupMenu;
15 import javax.swing.JTree;
16 import javax.swing.event.TreeSelectionEvent;
17 import javax.swing.event.TreeSelectionListener;
18 import javax.swing.tree.TreeModel;
19 import javax.swing.tree.TreeNode;
20 import javax.swing.tree.TreePath;
21 import javax.swing.tree.TreeSelectionModel;
22
23 import de.matthias_burbach.mosaique.core.Mosaique;
24 import de.matthias_burbach.mosaique.core.model.AbstractFileItem;
25
26 /***
27 * Features various popup menus whose action items depend on the type of the
28 * selected node.
29 *
30 * @author Matthias Burbach
31 */
32 public class MosaiqueTree extends JTree implements TreeSelectionListener {
33 /***
34 * Collapses the selected node deeply, i. e.
35 * all descendant nodes are collapsed as well.
36 * <p/>
37 * Is available on all types of nodes.
38 *
39 * @author Matthias Burbach
40 */
41 private class CollapseAllAction extends AbstractAction {
42 /***
43 * Constructs this action.
44 */
45 public CollapseAllAction() {
46 super("Collapse All");
47 }
48
49
50
51
52
53
54 /***
55 * {@inheritDoc}
56 */
57 public void actionPerformed(final ActionEvent e) {
58 TreePath treePath = getSelectionPath();
59 if (treePath != null) {
60 Enumeration descsEnum = getDescendantToggledPaths(treePath);
61 List descsList = enumToList(descsEnum);
62 sortListByPathCountDescending(descsList);
63 Iterator iter = descsList.iterator();
64 while (iter.hasNext()) {
65 TreePath descTreePath = (TreePath) iter.next();
66 collapsePath(descTreePath);
67 }
68 }
69 }
70
71 /***
72 * @param anEnumeration The enumeration to be converted to a list.
73 * @return The list holding the elements of <code>enum</code>.
74 */
75 private List enumToList(final Enumeration anEnumeration) {
76 List result = new ArrayList();
77 while (anEnumeration.hasMoreElements()) {
78 result.add(anEnumeration.nextElement());
79 }
80 return result;
81 }
82
83 /***
84 * @param treePaths The tree paths to be sorted.
85 */
86 private void sortListByPathCountDescending(final List treePaths) {
87 Comparator comparator = new Comparator() {
88 public int compare(final Object o1, final Object o2) {
89 TreePath tp1 = (TreePath) o1;
90 TreePath tp2 = (TreePath) o2;
91 return tp2.getPathCount() - tp1.getPathCount();
92 }
93 };
94 Collections.sort(treePaths, comparator);
95 }
96 }
97
98 /***
99 * Pops up the appropriate menu depending on the type of node selected.
100 *
101 * @author Matthias Burbach
102 */
103 private class PopupTrigger extends MouseAdapter {
104
105
106
107
108
109 /***
110 * {@inheritDoc}
111 */
112 public void mouseReleased(final MouseEvent e) {
113 if (e.isPopupTrigger()) {
114 int x = e.getX();
115 int y = e.getY();
116
117 int selectedRow =
118 MosaiqueTree.this.getClosestRowForLocation(x, y);
119 MosaiqueTree.this.setSelectionRow(selectedRow);
120 treeNodePopupMenu.show(MosaiqueTree.this, x, y);
121 }
122 }
123 }
124
125 /***
126 * The popup menu on general tree nodes.
127 */
128 private JPopupMenu treeNodePopupMenu;
129
130 /***
131 * The main GUI application object.
132 */
133 private MosaiqueFrame mosaiqueFrame;
134
135 /***
136 * @param mosaique The core application object.
137 * @param mosaiqueFrame The gui application object.
138 * @param treeModel The tree model to delegate change operations to.
139 */
140 public MosaiqueTree(
141 final Mosaique mosaique,
142 final MosaiqueFrame mosaiqueFrame,
143 final TreeModel treeModel) {
144 this.mosaiqueFrame = mosaiqueFrame;
145
146 setModel(treeModel);
147 getSelectionModel().setSelectionMode(
148 TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
149
150 MosaiqueTreeCellRenderer treeCellRenderer =
151 new MosaiqueTreeCellRenderer(mosaique);
152 setCellRenderer(treeCellRenderer);
153
154 treeNodePopupMenu = new JPopupMenu();
155 treeNodePopupMenu.add(new CollapseAllAction());
156 add(treeNodePopupMenu);
157
158 addMouseListener(new PopupTrigger());
159
160 addTreeSelectionListener(this);
161 }
162
163 /***
164 * @return The currently selected tree node or <code>null</code>.
165 */
166 public TreeNode getSelectedNode() {
167 TreeNode result = null;
168 TreePath treePath = getSelectionPath();
169 if (treePath != null) {
170 result = (TreeNode) treePath.getLastPathComponent();
171 }
172 return result;
173 }
174
175 /***
176 * @return The list of currently selected nodes of type {@link TreeNode}.
177 */
178 public List getSelectedNodes() {
179 List result = new ArrayList();
180 TreePath[] treePaths = getSelectionPaths();
181 if (treePaths != null) {
182 for (int i = 0; i < treePaths.length; i++) {
183 TreeNode candidateNode =
184 (TreeNode) treePaths[i].getLastPathComponent();
185 result.add(candidateNode);
186 }
187 }
188 return result;
189 }
190
191
192
193
194
195
196 /***
197 * {@inheritDoc}
198 */
199 public void valueChanged(final TreeSelectionEvent e) {
200 TreeNode selectedNode = getSelectedNode();
201 if (selectedNode instanceof AbstractFileItemNode) {
202 AbstractFileItemNode node = (AbstractFileItemNode) selectedNode;
203 AbstractFileItem fileItem = node.getFileItem();
204 mosaiqueFrame.openFile(
205 fileItem.getFilePath(),
206 fileItem.getPrimarySearchText(),
207 fileItem.getSecondarySearchText(),
208 fileItem.getBegin(),
209 fileItem.getEnd());
210 }
211 }
212 }