summaryrefslogtreecommitdiff
path: root/source/com/c2kernel/gui/DynamicTreeBuilder.java
blob: 29a62dc6c69248a4e76929fda52273a4ae5fdfdb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package com.c2kernel.gui;

import javax.swing.ImageIcon;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import com.c2kernel.gui.data.Node;
import com.c2kernel.gui.data.NodeSubscriber;
import com.c2kernel.lookup.Path;
import com.c2kernel.utils.Language;
import com.c2kernel.utils.Logger;
import com.c2kernel.utils.Resource;

/**
 * Installed as the user object on a single child node of a new node known to be composite.
 * <p>Shows 'Loading . . .' when the branch is opened, but a TreeExpansionListener attempts to fire this thread off in the first child node.
 * <br>When started, this thread will retrieve all of the real child nodes and add them to its parent while removing itself (hopefully for garbage collection)
 *
 * @version $Revision: 1.24 $ $Date: 2004/12/15 12:12:06 $
 * @author  $Author: abranson $
 */

public class DynamicTreeBuilder implements NodeSubscriber {
    private DefaultTreeModel treeModel;
    private DefaultMutableTreeNode parent;
    public short state = IDLE;
    public static final short IDLE = 0;
    public static final short LOADING = 1;
    public static final short PARTIAL = 2;
    public static final short FINISHED = 3;
    private DefaultMutableTreeNode loading;
    private static ImageIcon loadIcon = Resource.getImageResource("loading.gif");
    private static ImageIcon pauseIcon = Resource.getImageResource("reload.gif");
    
    /**
     * The newly created DynamicTreeBuilder records its parent node - the one for which it will build child nodes for.
     * @param nodeClicked The Parent Tree Node that will be populated.
     * @see NodeItem
     * @see TreeDisplay*/
    public DynamicTreeBuilder(DefaultMutableTreeNode parent) {
        this.parent = parent;
        loading = new DefaultMutableTreeNode(this);
    }

    /**
     * Before the tree builder can execute, it needs to be given references to the tree and NodeFactory needed to create the new nodes.
     * @param myNodeFactory The NodeFactory that can be queried for new NodeItems.
     * @param parentTree The JTree in which this node is currently contained.
     */
    public void buildInfo(JTree parentTree) {
        this.treeModel = (DefaultTreeModel)parentTree.getModel();
    }

    public void start() {
        // find the clicked tree node
        Node parentNode = (Node)parent.getUserObject();
        Logger.msg(2, "DynamicTreeBuilder.start() - Filling in children of '"+parentNode.toString()+"'");
        if (state == IDLE)
            parentNode.subscribeNode(this);
        else
            parentNode.loadMore();
        state = LOADING;
    }

    /**
     * Used by the JTree to find the text representation of the node.
     */
    public String toString() {
        switch (state) {
            case IDLE:
                return Language.translate("Initializing Tree Node Loader");
            case LOADING:
                return Language.translate("Loading . . .");
            case PARTIAL:
                return Language.translate("Double-click to load more");
            case FINISHED:
                return Language.translate("Done");
            default:
                return "";
        }
        
    }
    
    public ImageIcon getIcon() {
        if (state == LOADING)
            return loadIcon;
        else
            return pauseIcon;
    }

    public DefaultMutableTreeNode getTreeNode() {
        return loading;
    }

    public void add(Node newNode) {
        Logger.msg(2, "DynamicTreeBuilder.add() - Received item for tree. Name: "+newNode);
        
        // have we inserted the node yet?
        SwingUtilities.invokeLater(new TreeAddThread(newNode));
    }
    
    class TreeAddThread implements Runnable {
        Node newNode;
        TreeAddThread(Node newNode) {
            this.newNode = newNode;
        }
        public void run() {
            boolean inserted = false;
            DefaultMutableTreeNode newTreeNode = newNode.getTreeNode();
            // loop though all children unless we have done the insertion
            for (int i=0; i<parent.getChildCount() && !inserted; i++) {
                DefaultMutableTreeNode child = (DefaultMutableTreeNode)treeModel.getChild(parent, i);
                if (child == loading)  continue; // skip loading node

                Node childNode = (Node)child.getUserObject();
                if (childNode.getName().equals(newNode.getName())) {
                    // we already have this one, skip it
                    inserted = true;
                    break;
                    }
                if (childNode.getName().compareTo(newNode.getName()) >= 0) {
                    // if the next string is 'greater than' ours, insert the node before
                    treeModel.insertNodeInto(newTreeNode, parent, i);
                    inserted = true;
                    break;
                }

            }
            // if we haven't inserted yet, it must go at the end.

            if (!inserted)
                treeModel.insertNodeInto(newTreeNode, parent, parent.getChildCount());
        }
        
    }
    
    class TreeRemoveThread implements Runnable {
        DefaultMutableTreeNode oldNode;
        TreeRemoveThread(DefaultMutableTreeNode oldNode) {
            this.oldNode = oldNode;
        }
        
        public void run() {
            treeModel.removeNodeFromParent(oldNode);
        }
    }
    
    public void end(boolean more) {
        if (more) {
            state = PARTIAL;
        }
        else {
            state = FINISHED;
            synchronized(treeModel) {
        	   if (loading.getParent() != null)
    	            SwingUtilities.invokeLater(new TreeRemoveThread(loading));
            }
        }
    }        
    
    public void remove(Path path) {
        synchronized (treeModel) {
            for (int i=0; i<parent.getChildCount(); i++) {
                DefaultMutableTreeNode child = (DefaultMutableTreeNode)treeModel.getChild(parent, i);
                if (!(child.getUserObject() instanceof Node)) continue;
                Node childNode = (Node)child.getUserObject();
                if (childNode.getPath().equals(path)) {
                    SwingUtilities.invokeLater(new TreeRemoveThread(child));
                    return;
                }
            }
        }
    }
}