source: trunk/gli/src/org/greenstone/gatherer/util/SynchronizedTreeModel.java@ 5807

Last change on this file since 5807 was 5581, checked in by mdewsnip, 21 years ago

Many formatting, structural and code improvements.

  • Property svn:keywords set to Author Date Id Revision
File size: 4.6 KB
Line 
1package org.greenstone.gatherer.util;
2
3import javax.swing.*;
4import javax.swing.tree.*;
5import org.greenstone.gatherer.util.DefaultSynchronizedTreeNode;
6import org.greenstone.gatherer.util.TreeModelTest;
7import org.greenstone.gatherer.util.SynchronizedTreeNode;
8
9/** This synchronized TreeModel is comprised of two seperate models. The extended model is used to paint the screen, and can only be updated on the AWTEvent Thread. The second, private, model contains the current actual state of the model with changes made immediately. If such changes occur then a task is queued in the AWTEvent Thread to update the 'painted' model. This model depends on the TreeNodes used having these properties:
10* TreeNode x_node = new TreeNode("x");
11* TreeNode y_node = x_node.cloneNode();
12* for(int i = 0; i < x_node.getChildCount(); i++) {
13* x_node.getChildAt(i) != y_node.getChildAt(i);
14* x_node.getChildAt(i).equals(y_node.getChildAt(i));
15* }
16* x_node != y_node;
17* (new TreeNode("x")).equals(new TreeNode("x"));
18* In other words, any two different instances of the tree node must be equal according to the equals method.
19* Methods which need data from the tree should also use the model methods (ie model.getChildCount(node) rather than node.getChildCount()) and should not be in the AWTEvent Thread (as such calls will see the possibly out of date 'visual' model, not the actual underlying model).
20*/
21public class SynchronizedTreeModel
22 extends DefaultTreeModel
23 implements Runnable {
24
25 private boolean changed = false;
26 private DefaultTreeModel offscreen;
27 private TreeModelTest test = null;
28
29 SynchronizedTreeModel(SynchronizedTreeNode root) {
30 super(root);
31 offscreen = new DefaultTreeModel(root.cloneNode());
32 }
33
34 SynchronizedTreeModel(SynchronizedTreeNode root, TreeModelTest test) {
35 super(root);
36 offscreen = new DefaultTreeModel(root.cloneNode());
37 this.test = test;
38 }
39
40 /** Returns the child of parent at index index in the parent's child array. */
41 public Object getChild(Object parent, int index) {
42 if(isEventThread()) {
43 return super.getChild(parent, index);
44 }
45 else {
46 return offscreen.getChild(parent, index);
47 }
48 }
49
50 /** Returns the number of children of parent. */
51 public int getChildCount(Object parent) {
52 if(isEventThread()) {
53 return super.getChildCount(parent);
54 }
55 else {
56 return offscreen.getChildCount(parent);
57 }
58 }
59
60 /** Builds the parents of node up to and including the root node, where the original node is the last element in the returned array. This is probably the most 'expensive' method we make synchronized, but it isn't called often so thats ok. */
61 public TreeNode[] getPathToRoot(TreeNode aNode) {
62 if(isEventThread()) {
63 return super.getPathToRoot(aNode);
64 }
65 else {
66 return offscreen.getPathToRoot(aNode);
67 }
68 }
69
70 public Object getRoot() {
71 if(isEventThread()) {
72 return super.getRoot();
73 }
74 else {
75 return offscreen.getRoot();
76 }
77 }
78
79 /** Invoked this to insert newChild at location index in parents children. */
80 public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) {
81 if(isEventThread()) {
82 if(test != null) test.debug("insertNodeInto(" + newChild + ", " + parent + ", " + index + ")");
83 super.insertNodeInto(newChild, parent, index);
84 }
85 else {
86 if(test != null) test.debug("offscreen.insertNodeInto(" + newChild + ", " + parent + ", " + index + ")");
87 offscreen.insertNodeInto(newChild, parent, index);
88 queueUpdate();
89 }
90 }
91
92 /** Message this to remove node from its parent. */
93 public void removeNodeFromParent(MutableTreeNode node) {
94 if(isEventThread()) {
95 if(test != null) test.debug("removeNodeFromParent(" + node + ")");
96 super.removeNodeFromParent(node);
97 }
98 else {
99 if(test != null) test.debug("offscreen.removeNodeFromParent(" + node + ")");
100 offscreen.removeNodeFromParent(node);
101 queueUpdate();
102 }
103 }
104
105 public void run() {
106 synchronized(this) {
107 if(changed) {
108 setRoot(((SynchronizedTreeNode)(offscreen.getRoot())). cloneNode());
109 nodeChanged(root);
110 changed = false;
111 if(test != null) test.debug("Painted model refreshed.");
112 }
113 else {
114 if(test != null) test.debug("Painted model is current.");
115 }
116 }
117 }
118
119 /** Determine if this call is happening on the AWTEvent Dispatch Thread. */
120 private boolean isEventThread() {
121 return SwingUtilities.isEventDispatchThread();
122 }
123
124 private synchronized void queueUpdate() {
125 changed = true;
126 SwingUtilities.invokeLater(this);
127 if(test != null) test.debug("Painted model is obsolete.");
128 }
129}
Note: See TracBrowser for help on using the repository browser.