/[svn]/jsampler/trunk/src/org/jsampler/view/InstrumentsDbTreeModel.java
ViewVC logotype

Contents of /jsampler/trunk/src/org/jsampler/view/InstrumentsDbTreeModel.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1688 - (show annotations) (download)
Thu Feb 14 16:52:36 2008 UTC (16 years, 1 month ago) by iliev
File size: 18643 byte(s)
* Implemented a backend list with option to manually choose a backend
  to connect on startup(Edit/Preferences, then click the `Backend' tab)
  and option to change the backend without restarting JSampler
  (Actions/Change Backend or Ctrl + B)

* Added confirmation messages for removing sampler channels and
  audio/MIDI devices (Edit/Preferences, then click the `View' tab)

1 /*
2 * JSampler - a java front-end for LinuxSampler
3 *
4 * Copyright (C) 2005-2007 Grigor Iliev <grigor@grigoriliev.com>
5 *
6 * This file is part of JSampler.
7 *
8 * JSampler is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 *
12 * JSampler is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with JSampler; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 * MA 02111-1307 USA
21 */
22
23 package org.jsampler.view;
24
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27
28 import java.util.Vector;
29
30 import javax.swing.SwingUtilities;
31
32 import javax.swing.event.TreeModelEvent;
33 import javax.swing.event.TreeModelListener;
34
35 import javax.swing.tree.TreeModel;
36 import javax.swing.tree.TreePath;
37
38 import net.sf.juife.event.TaskEvent;
39 import net.sf.juife.event.TaskListener;
40
41 import org.jsampler.CC;
42
43 import org.jsampler.task.InstrumentsDb;
44
45 import org.linuxsampler.lscp.DbDirectoryInfo;
46 import org.linuxsampler.lscp.DbInstrumentInfo;
47
48 import org.linuxsampler.lscp.event.InstrumentsDbEvent;
49 import org.linuxsampler.lscp.event.InstrumentsDbListener;
50
51 import static org.linuxsampler.lscp.Parser.*;
52
53
54 /**
55 *
56 * @author Grigor Iliev
57 */
58 public class InstrumentsDbTreeModel implements TreeModel {
59 private DbDirectoryTreeNode root = null;
60 private Vector<TreeModelListener> listeners = new Vector<TreeModelListener>();
61
62 /**
63 * Creates a new instance of <code>InstrumentsDbTreeModel</code>.
64 */
65 public
66 InstrumentsDbTreeModel() { this(null); }
67
68 /**
69 * Creates a new instance of <code>InstrumentsDbTreeModel</code>.
70 * @param l A listener that will be notified when the root
71 * directory content is loaded.
72 */
73 public
74 InstrumentsDbTreeModel(final ActionListener l) {
75 // TODO: This shouldn't be done in the event-dispatcing thread
76 CC.getClient().addInstrumentsDbListener(getHandler());
77 ///////
78
79 initDb(l);
80 }
81
82 private void
83 initDb(final ActionListener l) {
84 final InstrumentsDb.GetDrectory gdi = new InstrumentsDb.GetDrectory("/");
85 final InstrumentsDb.GetDrectories gd = new InstrumentsDb.GetDrectories("/");
86 final InstrumentsDb.GetInstruments gi = new InstrumentsDb.GetInstruments("/");
87
88 gdi.addTaskListener(new TaskListener() {
89 public void
90 taskPerformed(TaskEvent e) {
91 if(gdi.doneWithErrors()) return;
92 root = new DbDirectoryTreeNode(gdi.getResult());
93 fireNodeStructureChanged(root);
94
95 CC.getTaskQueue().add(gd);
96 CC.getTaskQueue().add(gi);
97 }
98 });
99
100 gd.addTaskListener(new TaskListener() {
101 public void
102 taskPerformed(TaskEvent e) {
103 root.setConnected(true);
104 if(gd.doneWithErrors()) return;
105 updateDirectoryContent(root, gd.getResult());
106
107 for(int i = 0; i < root.getChildCount(); i++) {
108 DbDirectoryTreeNode node = root.getChildAt(i);
109 node.setConnected(true);
110 updateDirectoryContent(node, "/" + toEscapedFileName(node));
111 }
112
113 if(l != null) l.actionPerformed(null);
114 }
115 });
116
117 gi.addTaskListener(new TaskListener() {
118 public void
119 taskPerformed(TaskEvent e) {
120 if(gi.doneWithErrors()) return;
121 updateDirectoryContent(root, gi.getResult());
122 }
123 });
124
125 CC.getTaskQueue().add(gdi);
126 }
127
128 public void
129 treeWillExpand(TreePath path) {
130 DbDirectoryTreeNode node = (DbDirectoryTreeNode)path.getLastPathComponent();
131
132 if(!node.isConnected()) {
133 node.setConnected(true);
134 updateDirectoryContent(node, node.getInfo().getDirectoryPath());
135 }
136
137 for(int i = 0; i < node.getChildCount(); i++) {
138 DbDirectoryTreeNode child = node.getChildAt(i);
139 if(child.isConnected()) continue;
140 child.setConnected(true);
141 String pathName = getPathName(path.getPath());
142 if(pathName.length() > 1) pathName += "/";
143 updateDirectoryContent(child, pathName + toEscapedFileName(child));
144 }
145 }
146
147 // Tree model methods
148 public void
149 addTreeModelListener(TreeModelListener l) {
150 listeners.add(l);
151 }
152
153 public void
154 removeTreeModelListener(TreeModelListener l) {
155 listeners.remove(l);
156 }
157
158 public Object
159 getChild(Object parent, int index) {
160 return ((DbDirectoryTreeNode)parent).getChildAt(index);
161 }
162
163 public int
164 getChildCount(Object parent) {
165 return ((DbDirectoryTreeNode)parent).getChildCount();
166 }
167
168 public Object
169 getRoot() { return root; }
170
171 public int
172 getIndexOfChild(Object parent, Object child) {
173 if(parent == null || child == null) return -1;
174 return ((DbDirectoryTreeNode)parent).getIndex((DbDirectoryTreeNode)child);
175 }
176
177 public boolean
178 isLeaf(Object node) { return ((DbDirectoryTreeNode)node).isLeaf(); }
179
180 public void
181 valueForPathChanged(TreePath path, Object newValue) {
182
183 }
184 ///////
185
186 /**
187 * Schedules an update of the directory content for the specified directory node.
188 */
189 private void
190 updateDirectoryContent(final DbDirectoryTreeNode dirNode, String dirPath) {
191 updateDirectoryContent(dirNode, dirPath, null);
192 }
193
194 /**
195 * Schedules an update of the directory content for the specified directory node.
196 * @param l A listener that will be notified when the subdirectory list is updated.
197 */
198 private void
199 updateDirectoryContent (
200 final DbDirectoryTreeNode dirNode, String dirPath, final ActionListener l
201 ) {
202 final InstrumentsDb.GetDrectories gd = new InstrumentsDb.GetDrectories(dirPath);
203
204 gd.addTaskListener(new TaskListener() {
205 public void
206 taskPerformed(TaskEvent e) {
207 if(gd.doneWithErrors()) {
208 if(l != null) l.actionPerformed(null);
209 return;
210 }
211 updateDirectoryContent(dirNode, gd.getResult());
212 if(l != null) l.actionPerformed(null);
213 }
214 });
215 CC.scheduleTask(gd);
216
217 final InstrumentsDb.GetInstruments gi = new InstrumentsDb.GetInstruments(dirPath);
218
219 gi.addTaskListener(new TaskListener() {
220 public void
221 taskPerformed(TaskEvent e) {
222 if(gi.doneWithErrors()) return;
223 updateDirectoryContent(dirNode, gi.getResult());
224 }
225 });
226 CC.scheduleTask(gi);
227 }
228
229 private void
230 updateDirectoryContent(DbDirectoryTreeNode parent, DbDirectoryInfo[] children) {
231 boolean found = false;
232 Vector<DbDirectoryTreeNode> removedNodes = new Vector<DbDirectoryTreeNode>();
233 for(int i = 0; i < parent.getChildCount(); i++) {
234 for(int j = 0; j < children.length; j++) {
235 if(children[j] == null) continue;
236 if(children[j].getName().equals(parent.getChildAt(i).toString())) {
237 children[j] = null;
238 found = true;
239 break;
240 }
241 }
242 if(!found) removedNodes.add(parent.getChildAt(i));
243 found = false;
244 }
245
246 for(DbDirectoryTreeNode node : removedNodes) {
247 int i = parent.getIndex(node);
248 parent.removeDirectory(i);
249 fireNodeRemoved(parent, node, i);
250 }
251
252 for(DbDirectoryInfo info : children) {
253 if(info == null) continue;
254 DbDirectoryTreeNode node = new DbDirectoryTreeNode(info);
255 parent.addDirectory(node);
256 fireNodeInserted(node, parent.getIndex(node));
257 if(parent.getParent() == null) {
258 node.setConnected(true);
259 updateDirectoryContent(node, info.getDirectoryPath());
260 } else if(parent.isConnected()) {
261 updateDirectoryContent(node, info.getDirectoryPath());
262 }
263 }
264 }
265
266 private void
267 updateDirectoryContent(DbDirectoryTreeNode parent, DbInstrumentInfo[] children) {
268 boolean found = false;
269 Vector<DbInstrumentInfo> removedNodes = new Vector<DbInstrumentInfo>();
270 for(int i = 0; i < parent.getInstrumentCount(); i++) {
271 String name = parent.getInstrumentAt(i).getName();
272
273 for(int j = 0; j < children.length; j++) {
274 if(children[j] == null) continue;
275 if(children[j].getName().equals(name)) {
276 children[j] = null;
277 found = true;
278 break;
279 }
280 }
281 if(!found) removedNodes.add(parent.getInstrumentAt(i));
282 found = false;
283 }
284
285 for(DbInstrumentInfo info : removedNodes) {
286 int i = parent.getInstrumentIndex(info);
287 parent.removeInstrument(i);
288 }
289
290 for(DbInstrumentInfo info : children) {
291 if(info == null) continue;
292 parent.addInstrument(info);
293 }
294 }
295
296 /**
297 * Schedules a task for refreshing the directory content of the specified directory.
298 * Note that the specified directory is expected to be connected.
299 * @param dir The absolute path name of the directory to refresh.
300 */
301 public void
302 refreshDirectoryContent(String dir) {
303 final DbDirectoryTreeNode node = getNodeByPath(dir);
304 if(node == null) return;
305
306 node.removeAllDirectories();
307 fireNodeStructureChanged(node);
308 node.removeAllInstruments();
309
310 final InstrumentsDb.GetDrectories gd = new InstrumentsDb.GetDrectories(dir);
311
312 gd.addTaskListener(new TaskListener() {
313 public void
314 taskPerformed(TaskEvent e) {
315 if(gd.doneWithErrors()) return;
316
317 for(DbDirectoryInfo info : gd.getResult()) {
318 DbDirectoryTreeNode n = new DbDirectoryTreeNode(info);
319 node.addDirectory(n);
320 fireNodeInserted(n, node.getIndex(n));
321 n.setConnected(true);
322 updateDirectoryContent(n, n.getInfo().getDirectoryPath());
323 }
324 }
325 });
326 CC.scheduleTask(gd);
327
328 final InstrumentsDb.GetInstruments gi = new InstrumentsDb.GetInstruments(dir);
329
330 gi.addTaskListener(new TaskListener() {
331 public void
332 taskPerformed(TaskEvent e) {
333 if(gi.doneWithErrors()) return;
334
335 for(DbInstrumentInfo info : gi.getResult()) {
336 node.addInstrument(info);
337 }
338 }
339 });
340 CC.scheduleTask(gi);
341 }
342
343 protected Object[]
344 getPathToRoot(DbDirectoryTreeNode node) {
345 Vector v = new Vector();
346
347 while(node != null) {
348 v.insertElementAt(node, 0);
349 if(node == getRoot()) break;
350 node = node.getParent();
351 }
352
353 return v.toArray(new Object[v.size()]);
354 }
355
356 protected String
357 getPathName(Object[] objs) {
358 if(objs.length == 1) return "/";
359
360 StringBuffer sb = new StringBuffer();
361 for(int i = 1; i < objs.length; i++) {
362 sb.append('/').append(toEscapedFileName(objs[i]));
363 }
364
365 return sb.toString();
366 }
367
368 private void
369 fireNodeInserted(DbDirectoryTreeNode node, int index) {
370 Object[] path = getPathToRoot(node.getParent());
371 int[] idxs = { index };
372 Object[] objs = { node };
373 TreeModelEvent e = new TreeModelEvent(this, path, idxs, objs);
374 for(TreeModelListener l : listeners) {
375 l.treeNodesInserted(e);
376 }
377 }
378
379 private void
380 fireNodeChanged(DbDirectoryTreeNode node, int index) {
381 Object[] path = getPathToRoot(node.getParent());
382 int[] idxs = { index };
383 Object[] objs = { node };
384 TreeModelEvent e = new TreeModelEvent(this, path, idxs, objs);
385 for(TreeModelListener l : listeners) {
386 l.treeNodesChanged(e);
387 }
388 }
389
390 private void
391 fireNodeRemoved(DbDirectoryTreeNode parent, DbDirectoryTreeNode node, int index) {
392 Object[] path = getPathToRoot(parent);
393 int[] idxs = { index };
394 Object[] objs = { node };
395 TreeModelEvent e = new TreeModelEvent(this, path, idxs, objs);
396 for(int i = listeners.size() - 1; i >=0; i--) {
397 listeners.get(i).treeNodesRemoved(e);
398 }
399 }
400
401 private void
402 fireNodeStructureChanged(DbDirectoryTreeNode node) {
403 Object[] path = getPathToRoot(node);
404 Object[] objs = { node };
405 TreeModelEvent e = new TreeModelEvent(this, path);
406 for(TreeModelListener l : listeners) {
407 l.treeStructureChanged(e);
408 }
409 }
410
411 public DbDirectoryTreeNode
412 getNodeByPath(String path) {
413 String[] dirs = getDirectoryList(path);
414 if(dirs == null) return null;
415 if(dirs.length == 1) return root;
416 if(root == null) return null;
417 DbDirectoryTreeNode node = root;
418 boolean found = false;
419 for(int i = 1; i < dirs.length; i++) {
420 for(int k = 0; k < node.getChildCount(); k++) {
421 String s = toNonEscapedFileName(dirs[i]);
422 if(s.equals(node.getChildAt(k).toString())) {
423 node = node.getChildAt(k);
424 found = true;
425 break;
426 }
427 }
428
429 if(!found) return null;
430 found = false;
431 }
432
433 return node;
434 }
435
436 public String
437 getPathByNode(DbDirectoryTreeNode node) {
438 if(node == null) return null;
439 return getPathName(getPathToRoot(node));
440 }
441
442 /**
443 * @param l A listener which will be notified when the operation is completed.
444 */
445 public void
446 loadPath(String path, final ActionListener l) {
447 // TODO: This method is lazily implemented. Should be optimized.
448 final String[] dirs = getDirectoryList(path);
449 if(dirs == null) {
450 l.actionPerformed(null);
451 return;
452 }
453
454 final ActionListener listener = new ActionListener() {
455 public void
456 actionPerformed(ActionEvent e) {
457 String s = "";
458 DbDirectoryTreeNode node = null;
459 for(int i = 0; i < dirs.length; i++) {
460 if(i > 1) s += "/" + dirs[i];
461 else s += dirs[i];
462 node = getNodeByPath(s);
463 if(node == null) {
464 if(l != null) l.actionPerformed(null);
465 return;
466 }
467
468 if(!node.isConnected()) {
469 node.setConnected(true);
470 updateDirectoryContent(node, s, this);
471 return;
472 }
473 }
474
475 if(l != null) l.actionPerformed(null);
476 }
477 };
478
479 listener.actionPerformed(null);
480 }
481
482 /** Resets this model. */
483 public void
484 reset() {
485 DbDirectoryTreeNode oldRoot = root;
486 root = null;
487 TreeModelEvent e = new TreeModelEvent(this, (TreePath)null);
488 for(TreeModelListener l : listeners) {
489 l.treeStructureChanged(e);
490 }
491 }
492
493 private final EventHandler eventHandler = new EventHandler();
494
495 private EventHandler
496 getHandler() { return eventHandler; }
497
498 private class EventHandler implements InstrumentsDbListener {
499 /**
500 * Invoked when the number of instrument
501 * directories in a specific directory has changed.
502 */
503 public void
504 directoryCountChanged(final InstrumentsDbEvent e) {
505 SwingUtilities.invokeLater(new Runnable() {
506 public void
507 run() { updateDirectoryCount(e); }
508 });
509 }
510
511 private void
512 updateDirectoryCount(InstrumentsDbEvent e) {
513 if(e.getPathName().equals("/") && root == null) {
514 /*
515 * If the directory tree failed to load due to a database
516 * corruption it may work now if the db has been formatted
517 */
518 initDb(null);
519 }
520
521 DbDirectoryTreeNode node = getNodeByPath(e.getPathName());
522 if(node == null) return;
523 if(!node.isConnected()) return;
524
525 updateDirectoryContent(node, e.getPathName());
526 }
527
528 /**
529 * Invoked when the settings of an instrument directory are changed.
530 */
531 public void
532 directoryInfoChanged(final InstrumentsDbEvent e) {
533 SwingUtilities.invokeLater(new Runnable() {
534 public void
535 run() { updateDirectoryInfo(e); }
536 });
537 }
538
539 private void
540 updateDirectoryInfo(InstrumentsDbEvent e) {
541 final DbDirectoryTreeNode node = getNodeByPath(e.getPathName());
542 if(node == null) return;
543
544 final InstrumentsDb.GetDrectory t =
545 new InstrumentsDb.GetDrectory(e.getPathName());
546
547 t.addTaskListener(new TaskListener() {
548 public void
549 taskPerformed(TaskEvent e) {
550 if(t.doneWithErrors()) return;
551 if(node.getParent() != null) {
552 node.getParent().updateDirectory(t.getResult());
553 fireNodeChanged(node, node.getParent().getIndex(node));
554 }
555 }
556 });
557
558 CC.getTaskQueue().add(t);
559 }
560
561 /**
562 * Invoked when an instrument directory is renamed.
563 */
564 public void
565 directoryNameChanged(final InstrumentsDbEvent e) {
566 SwingUtilities.invokeLater(new Runnable() {
567 public void
568 run() { directoryRenamed(e); }
569 });
570 }
571
572 private void
573 directoryRenamed(InstrumentsDbEvent e) {
574 DbDirectoryTreeNode node = getNodeByPath(e.getPathName());
575 if(node == null) {
576 // If the directory is renamed by this frontend the
577 // directory should already be with the new name
578 String s = getParentDirectory(e.getPathName());
579 if(s.length() == 1) s += toEscapedFileName(e.getNewName());
580 else s += "/" + toEscapedFileName(e.getNewName());
581 node = getNodeByPath(s);
582 }
583 if(node == null || node.getParent() == null) {
584 CC.getLogger().warning("Invalid path: " + e.getPathName());
585 return;
586 }
587
588 node.setName(e.getNewName());
589 DbDirectoryTreeNode parent = node.getParent();
590
591 int i = parent.getIndex(node);
592 parent.removeDirectory(i);
593 fireNodeRemoved(parent, node, i);
594
595 parent.addDirectory(node);
596 fireNodeInserted(node, parent.getIndex(node));
597 }
598
599 /**
600 * Invoked when the number of instruments
601 * in a specific directory has changed.
602 */
603 public void
604 instrumentCountChanged(final InstrumentsDbEvent e) {
605 SwingUtilities.invokeLater(new Runnable() {
606 public void
607 run() {
608 DbDirectoryTreeNode node = getNodeByPath(e.getPathName());
609 if(node == null) {
610 return;
611 }
612 if(!node.isConnected()) {
613 return;
614 }
615
616 updateDirectoryContent(node, e.getPathName());
617 }
618 });
619 }
620
621 /**
622 * Invoked when the settings of an instrument are changed.
623 */
624 public void
625 instrumentInfoChanged(final InstrumentsDbEvent e) {
626 SwingUtilities.invokeLater(new Runnable() {
627 public void
628 run() { updateInstrumentInfo(e); }
629 });
630 }
631
632 /**
633 * Invoked when an instrument is renamed.
634 */
635 public void
636 instrumentNameChanged(final InstrumentsDbEvent e) {
637 SwingUtilities.invokeLater(new Runnable() {
638 public void
639 run() { instrumentRenamed(e); }
640 });
641 }
642
643 private void
644 updateInstrumentInfo(InstrumentsDbEvent e) {
645 String dir = getParentDirectory(e.getPathName());
646 final DbDirectoryTreeNode node = getNodeByPath(dir);
647 if(node == null) return;
648 if(!node.isConnected()) return;
649
650 final InstrumentsDb.GetInstrument t =
651 new InstrumentsDb.GetInstrument(e.getPathName());
652
653 t.addTaskListener(new TaskListener() {
654 public void
655 taskPerformed(TaskEvent e) {
656 if(t.doneWithErrors()) return;
657 node.updateInstrument(t.getResult());
658 }
659 });
660
661 CC.getTaskQueue().add(t);
662 }
663
664 private void
665 instrumentRenamed(InstrumentsDbEvent e) {
666 String dir = getParentDirectory(e.getPathName());
667 DbDirectoryTreeNode node = getNodeByPath(dir);
668 if(node == null) return;
669
670 String instr = getFileName(e.getPathName());
671 if(instr == null) return;
672
673 DbInstrumentInfo info = node.getInstrument(toNonEscapedFileName(instr));
674
675 if(info == null) {
676 // If the instrument is renamed by this frontend the
677 // instrument should already be with the new name
678 info = node.getInstrument(e.getNewName());
679 }
680 if(info == null) {
681 CC.getLogger().warning("Invalid path: " + e.getPathName());
682 return;
683 }
684
685 info.setName(e.getNewName());
686 node.removeInstrument(node.getInstrumentIndex(info));
687 node.addInstrument(info);
688 }
689
690 /** Invoked when the status of particular job has changed. */
691 public void
692 jobStatusChanged(InstrumentsDbEvent e) { }
693 }
694 }

  ViewVC Help
Powered by ViewVC