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