001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.openide.explorer.view;
042:
043: import org.openide.nodes.Node;
044: import org.openide.util.*;
045:
046: import java.beans.*;
047:
048: import java.lang.ref.Reference;
049: import java.lang.ref.WeakReference;
050:
051: import java.util.*;
052:
053: import javax.swing.*;
054: import javax.swing.event.*;
055: import javax.swing.tree.*;
056:
057: /** Model for displaying the nodes in list and choice.
058: *
059: * @author Jaroslav Tulach
060: */
061: public class NodeListModel extends AbstractListModel implements
062: ComboBoxModel {
063: static final long serialVersionUID = -1926931095895356820L;
064:
065: /** listener used to listen to changes in trees */
066: private transient Listener listener;
067:
068: /** parent node */
069: private transient VisualizerNode parent;
070:
071: /** originally selected item */
072: private transient Object selectedObject;
073:
074: /** previous size */
075: private transient int size;
076:
077: /** depth to display */
078: private int depth = 1;
079:
080: /** map that assignes to each visualizer number of its children till
081: * the specified depth.
082: */
083: private Map<VisualizerNode, Info> childrenCount;
084:
085: /** Creates new model.
086: */
087: public NodeListModel() {
088: parent = VisualizerNode.EMPTY;
089: selectedObject = VisualizerNode.EMPTY;
090: clearChildrenCount();
091: }
092:
093: /** Creates new model.
094: * @param root the root of the model
095: */
096: public NodeListModel(Node root) {
097: this ();
098: setNode(root);
099: }
100:
101: /** Changes the root of the model. This is thread safe method.
102: * @param root the root of the model
103: */
104: public void setNode(final Node root) {
105: Mutex.EVENT.readAccess(new Runnable() {
106: public void run() {
107: VisualizerNode v = VisualizerNode.getVisualizer(null,
108: root);
109:
110: if (v == parent) {
111: // no change
112: return;
113: }
114:
115: removeAll();
116: parent.removeNodeModel(listener());
117:
118: parent = v;
119: selectedObject = v;
120: clearChildrenCount();
121:
122: addAll();
123: parent.addNodeModel(listener());
124: }
125: });
126: }
127:
128: /** Depth of nodes to display.
129: * @param depth the depth
130: */
131: public void setDepth(int depth) {
132: if (depth != this .depth) {
133: this .depth = depth;
134: clearChildrenCount();
135:
136: Mutex.EVENT.readAccess(new Runnable() {
137: public void run() {
138: removeAll();
139: addAll();
140: }
141: });
142: }
143: }
144:
145: /** Getter for depth.
146: * @return number of levels to display
147: */
148: public int getDepth() {
149: return depth;
150: }
151:
152: /** Getter for the listener. Only from AWT-QUEUE.
153: */
154: private Listener listener() {
155: if (listener == null) {
156: listener = new Listener(this );
157: }
158:
159: return listener;
160: }
161:
162: //
163: // model methods
164: //
165:
166: /** Number of elements in the model.
167: */
168: public int getSize() {
169: int s = findSize(parent, -1, depth);
170:
171: return s;
172: }
173:
174: /** Child at given index.
175: */
176: public Object getElementAt(int i) {
177: return findElementAt(parent, i, -1, depth);
178: }
179:
180: /** Finds index of given object.
181: * @param o object produced by this model
182: * @return index, or -1 if the object is not in the list
183: */
184: public int getIndex(Object o) {
185: getSize();
186:
187: Info i = childrenCount.get(o);
188:
189: return (i == null) ? (-1) : i.index;
190: }
191:
192: /** Currently selected item.
193: */
194: public void setSelectedItem(Object anObject) {
195: if (selectedObject != anObject) {
196: selectedObject = anObject;
197: fireContentsChanged(this , -1, -1);
198: }
199: }
200:
201: public Object getSelectedItem() {
202: return selectedObject;
203: }
204:
205: //
206: // modification of the counting model
207: //
208: private void clearChildrenCount() {
209: childrenCount = new HashMap<VisualizerNode, Info>(17);
210: }
211:
212: /** Finds size of sub children excluding vis node.
213: *
214: * @param vis the visualizer to find the size for
215: * @param index the index that should be assigned to vis
216: * @param depth the depth to scan
217: * @return number of children
218: */
219: private int findSize(VisualizerNode vis, int index, int depth) {
220: Info info = childrenCount.get(vis);
221:
222: if (info != null) {
223: return info.childrenCount;
224: }
225:
226: // only my children
227: int size = 0;
228:
229: info = new Info();
230: info.depth = depth;
231: info.index = index;
232:
233: if (depth-- > 0) {
234: Iterator it = vis.getChildren().iterator();
235:
236: while (it.hasNext()) {
237: VisualizerNode v = (VisualizerNode) it.next();
238:
239: // count node v
240: size++;
241:
242: // now count children of node v
243: size += findSize(v, index + size, depth);
244: }
245: }
246:
247: info.childrenCount = size;
248: childrenCount.put(vis, info);
249:
250: return size;
251: }
252:
253: /** Finds the child with requested index.
254: *
255: * @param vis the visualizer to find the size for
256: * @param indx the index of requested child
257: * @param depth the depth to scan
258: * @return the children
259: */
260: private VisualizerNode findElementAt(VisualizerNode vis, int indx,
261: int realIndx, int depth) {
262: if (--depth == 0) {
263: // last depth is handled in special way
264: return (VisualizerNode) vis.getChildAt(indx);
265: }
266:
267: Iterator it = vis.getChildren().iterator();
268:
269: while (it.hasNext()) {
270: VisualizerNode v = (VisualizerNode) it.next();
271:
272: if (indx-- == 0) {
273: return v;
274: }
275:
276: int s = findSize(v, ++realIndx, depth);
277:
278: if (indx < s) {
279: // search this child
280: return findElementAt(v, indx, realIndx, depth);
281: }
282:
283: // go to next child
284: indx -= s;
285: realIndx += s;
286: }
287:
288: return vis;
289: }
290:
291: /** Finds a depth for given model & object. Used from renderer.
292: * @param m model
293: * @param o the visualizer node
294: * @return depth or 0 if not found
295: */
296: static int findVisualizerDepth(ListModel m, VisualizerNode o) {
297: if (m instanceof NodeListModel) {
298: NodeListModel n = (NodeListModel) m;
299: Info i = n.childrenCount.get(o);
300:
301: if (i != null) {
302: return n.depth - i.depth - 1;
303: }
304: }
305:
306: return 0;
307: }
308:
309: //
310: // Modifications
311: //
312: final void addAll() {
313: size = getSize();
314:
315: if (size > 0) {
316: fireIntervalAdded(this , 0, size - 1);
317: }
318: }
319:
320: final void removeAll() {
321: if (size > 0) {
322: fireIntervalRemoved(this , 0, size - 1);
323: }
324: }
325:
326: final void changeAll() {
327: size = getSize();
328:
329: if (size > 0) {
330: fireContentsChanged(this , 0, size - 1);
331: }
332:
333: clearChildrenCount();
334: }
335:
336: final void added(VisualizerEvent.Added ev) {
337: VisualizerNode v = ev.getVisualizer();
338: int[] indices = ev.getArray();
339:
340: //fire that model has been changed only when event source's (visualizer)
341: //children are shown in the list
342: if ((cachedDepth(v) <= 0) || (indices.length == 0)) {
343: return;
344: }
345:
346: clearChildrenCount();
347: size = getSize();
348:
349: int seg = (parent == v) ? 0 : getIndex(v);
350: fireIntervalAdded(this , indices[0] + seg,
351: indices[indices.length - 1] + seg);
352: }
353:
354: final void removed(VisualizerEvent.Removed ev) {
355: VisualizerNode v = ev.getVisualizer();
356: int[] indices = ev.getArray();
357:
358: //fire that model has been changed only when event source's (visualizer)
359: //children are shown in the list
360: if ((cachedDepth(v) <= 0) || (indices.length == 0)) {
361: return;
362: }
363:
364: clearChildrenCount();
365:
366: int seg = (parent == v) ? 0 : getIndex(v);
367: fireIntervalRemoved(this , indices[0] + seg,
368: indices[indices.length - 1] + seg);
369: }
370:
371: final void update(VisualizerNode v) {
372: // ensure the model is computed
373: getSize();
374:
375: Info i = childrenCount.get(v);
376:
377: if (i != null) {
378: fireContentsChanged(this , i.index, i.index);
379: }
380: }
381:
382: private int cachedDepth(VisualizerNode v) {
383: getSize();
384:
385: Info i = childrenCount.get(v);
386:
387: if (i != null) {
388: return i.depth;
389: }
390:
391: // v is not in the model
392: return -1;
393: }
394:
395: /** The listener */
396: private static final class Listener implements NodeModel {
397: /** weak reference to the model */
398: private Reference<NodeListModel> model;
399:
400: /** Constructor.
401: */
402: public Listener(NodeListModel m) {
403: model = new WeakReference<NodeListModel>(m);
404: }
405:
406: /** Getter for the model or null.
407: */
408: private NodeListModel get(VisualizerEvent ev) {
409: NodeListModel m = model.get();
410:
411: if ((m == null) && (ev != null)) {
412: ev.getVisualizer().removeNodeModel(this );
413:
414: return null;
415: }
416:
417: return m;
418: }
419:
420: /** Notification of children addded event. Modifies the list of nodes
421: * and fires info to all listeners.
422: */
423: public void added(VisualizerEvent.Added ev) {
424: NodeListModel m = get(ev);
425:
426: if (m == null) {
427: return;
428: }
429:
430: m.added(ev);
431: }
432:
433: /** Notification that children has been removed. Modifies the list of nodes
434: * and fires info to all listeners.
435: */
436: public void removed(VisualizerEvent.Removed ev) {
437: NodeListModel m = get(ev);
438:
439: if (m == null) {
440: return;
441: }
442:
443: m.removed(ev);
444: }
445:
446: /** Notification that children has been reordered. Modifies the list of nodes
447: * and fires info to all listeners.
448: */
449: public void reordered(VisualizerEvent.Reordered ev) {
450: NodeListModel m = get(ev);
451:
452: if (m == null) {
453: return;
454: }
455:
456: m.changeAll();
457: }
458:
459: /** Update a visualizer (change of name, icon, description, etc.)
460: */
461: public void update(VisualizerNode v) {
462: NodeListModel m = get(null);
463:
464: if (m == null) {
465: return;
466: }
467:
468: m.update(v);
469: }
470:
471: /** Notification about big change in children
472: */
473: public void structuralChange(VisualizerNode v) {
474: NodeListModel m = get(null);
475:
476: if (m == null) {
477: return;
478: }
479:
480: m.changeAll();
481: }
482: }
483:
484: /** Info for a component in model
485: */
486: private static final class Info extends Object {
487: public int childrenCount;
488: public int depth;
489: public int index;
490:
491: Info() {
492: }
493:
494: public String toString() {
495: return "Info[childrenCount=" + childrenCount + ", depth="
496: + depth + // NOI18N
497: ", index=" + index; // NOI18N
498: }
499: }
500: }
|