1 package com.melloware.jukes.gui.view;
2
3 import java.awt.Component;
4 import java.awt.Rectangle;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.MouseAdapter;
7 import java.awt.event.MouseEvent;
8 import java.awt.event.MouseListener;
9 import java.beans.PropertyChangeEvent;
10 import java.beans.PropertyChangeListener;
11
12 import javax.swing.Action;
13 import javax.swing.JComponent;
14 import javax.swing.JPopupMenu;
15 import javax.swing.JTree;
16 import javax.swing.ToolTipManager;
17 import javax.swing.event.TreeExpansionEvent;
18 import javax.swing.event.TreeExpansionListener;
19 import javax.swing.event.TreeWillExpandListener;
20 import javax.swing.tree.DefaultTreeCellRenderer;
21 import javax.swing.tree.DefaultTreeModel;
22 import javax.swing.tree.ExpandVetoException;
23 import javax.swing.tree.TreeModel;
24 import javax.swing.tree.TreeNode;
25 import javax.swing.tree.TreePath;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import skt.swing.SwingUtil;
31 import skt.swing.search.IncrementalSearchKeyListener;
32 import skt.swing.search.TreeFindAction;
33
34 import com.jgoodies.uif.action.ActionManager;
35 import com.jgoodies.uif.application.Application;
36 import com.jgoodies.uif.component.UIFTree;
37 import com.jgoodies.uifextras.util.UIFactory;
38 import com.melloware.jukes.gui.tool.Actions;
39 import com.melloware.jukes.gui.tool.MainModule;
40 import com.melloware.jukes.gui.tool.Resources;
41 import com.melloware.jukes.gui.view.node.AbstractTreeNode;
42 import com.melloware.jukes.gui.view.node.NavigationNode;
43 import com.melloware.jukes.gui.view.node.TrackNode;
44 import com.melloware.jukes.util.GuiUtil;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 final class NavigationPanelBuilder {
63
64 private static final Log LOG = LogFactory.getLog(NavigationPanelBuilder.class);
65
66
67
68
69
70 private UIFTree tree;
71
72
73
74
75 private final MainModule module;
76
77
78
79
80
81 NavigationPanelBuilder(MainModule mainModule) {
82 this.module = mainModule;
83 initComponents();
84 mainModule.addPropertyChangeListener(MainModule.PROPERTYNAME_NAVIGATION_TREE_MODEL, new TreeModelChangeHandler());
85 SelectionChangeHandler selectionHandler = new SelectionChangeHandler();
86 mainModule.addPropertyChangeListener(MainModule.PROPERTYNAME_SELECTION, selectionHandler);
87 mainModule.addPropertyChangeListener(MainModule.PROPERTYNAME_HIGHLIGHT, selectionHandler);
88 }
89
90
91
92
93
94 JComponent build() {
95 return UIFactory.createStrippedScrollPane(tree);
96 }
97
98
99
100
101
102
103 private void initComponents() {
104 tree = new RefreshedTree(module.getNavigationTreeModel());
105 tree.setSelectionModel(module.getNavigationTreeSelectionModel());
106 final NavigationTreeExpandListener expander = new NavigationTreeExpandListener();
107 tree.addTreeWillExpandListener(expander);
108 tree.addTreeExpansionListener(expander);
109 tree.putClientProperty("JTree.lineStyle", "None");
110 tree.setRootVisible(false);
111 tree.setShowsRootHandles(true);
112 tree.setCellRenderer(new TreeCellRenderer());
113
114
115 final TreeFindAction findAction = new TreeFindAction(true);
116 SwingUtil.installActions(tree, new Action[] { new TreeFindAction(true), findAction });
117 tree.addKeyListener(new IncrementalSearchKeyListener(findAction));
118
119
120 final JPopupMenu popup = MainMenuBuilder.buildPlayerPopupMenu(tree);
121 MouseListener popupListener = new TreePopupListener(popup);
122 tree.addMouseListener(popupListener);
123
124 tree.setEditable(false);
125 tree.setScrollsOnExpand(true);
126
127
128 ToolTipManager.sharedInstance().registerComponent(tree);
129 }
130
131
132
133
134
135 private void updateExpansionState() {
136 MainFrame mf = (MainFrame) Application.getDefaultParentFrame();
137 AbstractTreeNode node = (AbstractTreeNode) mf.getMainModule().getSelectedNode();
138
139 if (node != null) {
140 final DefaultTreeModel treeModel = ((DefaultTreeModel) tree.getModel());
141 final TreeNode[] nodes = treeModel.getPathToRoot(node);
142 final TreePath path = new TreePath(nodes);
143 boolean expanded = tree.isExpanded(path);
144 if (expanded) {
145 tree.expandPath(path);
146 }
147 }
148 }
149
150
151
152
153
154 private void updateModel(TreeModel newModel) {
155 tree.setModel(newModel);
156
157 }
158
159
160
161
162 private void updateSelectedState() {
163 MainFrame mf = (MainFrame) Application.getDefaultParentFrame();
164 AbstractTreeNode node = (AbstractTreeNode) mf.getMainModule().getSelectedNode();
165 if (node != null) {
166 final DefaultTreeModel treeModel = ((DefaultTreeModel) tree.getModel());
167 final TreeNode[] nodes = treeModel.getPathToRoot(node);
168 final TreePath path = new TreePath(nodes);
169
170
171 tree.scrollPathToVisible(path);
172 tree.makeVisible(path);
173 tree.setSelectionPath(path);
174 }
175 }
176
177
178 private static class TreeCellRenderer extends DefaultTreeCellRenderer {
179
180 @Override
181 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
182 boolean leaf, int row, boolean focus) {
183 super.getTreeCellRendererComponent(tree, value, selected, expanded, false, row, focus);
184 NavigationNode node = (NavigationNode) value;
185 if (node.getFontColor() == null) {
186 this.setForeground(sel ? getTextSelectionColor() : getTextNonSelectionColor());
187 } else {
188 this.setForeground(sel ? getTextSelectionColor() : node.getFontColor());
189 }
190
191 selected = sel;
192 UIFTree extTree = (UIFTree) tree;
193 this.setFont((node.getFont() == null) ? extTree.getFont() : node.getFont());
194 this.setIcon(node.getNodeIcon(sel));
195 this.setText(node.getName());
196 this.setToolTipText(node.getName());
197 return this;
198 }
199 }
200
201
202 private static class NavigationTreeExpandListener implements TreeWillExpandListener, TreeExpansionListener {
203
204
205
206
207 public void treeCollapsed(TreeExpansionEvent aEvent) {
208 GuiUtil.setBusyCursor(Application.getDefaultParentFrame(), false);
209 }
210
211
212
213
214 public void treeExpanded(TreeExpansionEvent aEvent) {
215 GuiUtil.setBusyCursor(Application.getDefaultParentFrame(), false);
216 }
217
218
219
220
221 public void treeWillCollapse(TreeExpansionEvent aEvent) throws ExpandVetoException {
222 GuiUtil.setBusyCursor(Application.getDefaultParentFrame(), true);
223 }
224
225
226
227
228 public void treeWillExpand(TreeExpansionEvent aEvent) throws ExpandVetoException {
229 AbstractTreeNode node = (AbstractTreeNode) aEvent.getPath().getLastPathComponent();
230 GuiUtil.setBusyCursor(Application.getDefaultParentFrame(), true);
231 node.loadChildren();
232 }
233
234 }
235
236
237 private static class RefreshedTree extends UIFTree {
238
239 public RefreshedTree(TreeModel treeModel) {
240 super(treeModel);
241 }
242
243
244
245
246
247
248 @Override
249 public int getScrollableUnitIncrement(Rectangle aVisibleRect, int aOrientation, int aDirection) {
250
251 return MainModule.SETTINGS.getCatalogScrollUnits();
252 }
253
254
255
256
257 @Override
258 public void updateUI() {
259 super.updateUI();
260 setSelectionPath(getSelectionPath());
261 setCellRenderer(new TreeCellRenderer());
262 }
263
264 }
265
266
267 private class SelectionChangeHandler implements PropertyChangeListener {
268
269
270
271
272
273 public void propertyChange(PropertyChangeEvent evt) {
274
275 if (evt.getPropertyName().equals(MainModule.PROPERTYNAME_SELECTION)) {
276 LOG.debug("Selected");
277 updateExpansionState();
278 } else if (evt.getPropertyName().equals(MainModule.PROPERTYNAME_HIGHLIGHT)) {
279 LOG.debug("Highlighted");
280 updateSelectedState();
281 }
282 }
283 }
284
285
286
287 private class TreeModelChangeHandler implements PropertyChangeListener {
288
289
290
291
292
293
294 public void propertyChange(PropertyChangeEvent evt) {
295 TreeModel newModel = (TreeModel) evt.getNewValue();
296 updateModel(newModel);
297
298
299 }
300 }
301
302
303 private static class TreePopupListener extends MouseAdapter {
304
305 final JPopupMenu popup;
306
307 public TreePopupListener(JPopupMenu popup) {
308 this.popup = popup;
309 }
310
311 @Override
312 public void mousePressed(MouseEvent evt) {
313 maybeShowPopup(evt);
314 boolean queue = false;
315
316 JTree tree = (JTree) evt.getComponent();
317
318
319 TreePath path = tree.getPathForLocation(evt.getX(), evt.getY());
320
321 if (path == null) {
322 return;
323 }
324
325
326 if (((evt.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) && (evt.getClickCount() == 2)) {
327 LOG.debug("[Treenode left double clicked].");
328
329 if (!(path.getLastPathComponent() instanceof TrackNode)) {
330 return;
331 }
332 queue = true;
333 }
334
335
336 if (((evt.getModifiers() & MouseEvent.BUTTON2_MASK) != 0) && (evt.getClickCount() == 1)) {
337 LOG.debug("[Treenode mouse wheel clicked].");
338 queue = true;
339 }
340
341 if (queue) {
342 JComponent source = (JComponent) evt.getSource();
343 final ActionEvent event = new ActionEvent(source, 1, Actions.PLAYER_QUEUE_ID);
344 source.putClientProperty(Resources.EDITOR_COMPONENT, source);
345 ActionManager.get(Actions.PLAYER_QUEUE_ID).actionPerformed(event);
346 }
347 }
348
349 @Override
350 public void mouseReleased(MouseEvent e) {
351 maybeShowPopup(e);
352 }
353
354 private void maybeShowPopup(MouseEvent e) {
355 if ((e.isPopupTrigger() || ((e.getModifiers() & MouseEvent.BUTTON2_MASK) != 0))) {
356 JTree tree = (JTree) e.getComponent();
357
358
359 TreePath path = tree.getPathForLocation(e.getX(), e.getY());
360
361 if (path == null) {
362 return;
363 }
364
365
366 if (!(path.getLastPathComponent() instanceof AbstractTreeNode)) {
367 return;
368 }
369 tree.addSelectionPath(path);
370
371
372 if (e.isPopupTrigger()) {
373 popup.show(e.getComponent(), e.getX(), e.getY());
374 }
375 }
376 }
377 }
378
379 }