001: /*
002: * The contents of this file are subject to the terms of the Common Development
003: * and Distribution License (the License). You may not use this file except in
004: * compliance with the License.
005: *
006: * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
007: * or http://www.netbeans.org/cddl.txt.
008: *
009: * When distributing Covered Code, include this CDDL Header Notice in each file
010: * and include the License file at http://www.netbeans.org/cddl.txt.
011: * If applicable, add the following below the CDDL Header, with the fields
012: * enclosed by brackets [] replaced by your own identifying information:
013: * "Portions Copyrighted [year] [name of copyright owner]"
014: *
015: * The Original Software is NetBeans. The Initial Developer of the Original
016: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
017: * Microsystems, Inc. All Rights Reserved.
018: */
019:
020: package org.netbeans.modules.sql.project.ui;
021:
022: import java.awt.Image;
023: import java.beans.PropertyChangeEvent;
024: import java.beans.PropertyChangeListener;
025: import java.util.ArrayList;
026: import java.util.Collections;
027: import java.util.List;
028: import java.util.StringTokenizer;
029: import javax.swing.Icon;
030: import javax.swing.ImageIcon;
031: import javax.swing.event.ChangeEvent;
032: import javax.swing.event.ChangeListener;
033: import javax.swing.event.EventListenerList;
034: import org.netbeans.api.project.SourceGroup;
035: import org.netbeans.api.queries.VisibilityQuery;
036: import org.openide.DialogDisplayer;
037: import org.openide.NotifyDescriptor;
038: import org.openide.filesystems.FileObject;
039: import org.openide.filesystems.FileUtil;
040: import org.openide.loaders.ChangeableDataFilter;
041: import org.openide.loaders.DataFilter;
042: import org.openide.loaders.DataFolder;
043: import org.openide.loaders.DataObject;
044: import org.openide.nodes.FilterNode;
045: import org.openide.nodes.Node;
046: import org.openide.nodes.NodeNotFoundException;
047: import org.openide.nodes.NodeOp;
048: import org.openide.util.Lookup;
049: import org.openide.util.NbBundle;
050: import org.openide.util.Utilities;
051: import org.openide.util.WeakListeners;
052: import org.openide.util.lookup.Lookups;
053: import org.openide.util.lookup.ProxyLookup;
054:
055: // XXX need unit test
056:
057: /**
058: * Displays a package root in a tree.
059: * @see "#42151"
060: * @author Jesse Glick
061: */
062: final class TreeRootNode extends FilterNode implements
063: PropertyChangeListener {
064:
065: private static final DataFilter VISIBILITY_QUERY_FILTER = new VisibilityQueryDataFilter();
066:
067: private final SourceGroup g;
068:
069: public TreeRootNode(SourceGroup g) {
070: this (DataFolder.findFolder(g.getRootFolder()), g);
071: }
072:
073: private TreeRootNode(DataFolder folder, SourceGroup g) {
074: this (new FilterNode(folder.getNodeDelegate(), folder
075: .createNodeChildren(VISIBILITY_QUERY_FILTER)), g);
076: }
077:
078: private TreeRootNode(Node originalNode, SourceGroup g) {
079: super (originalNode, new PackageFilterChildren(originalNode),
080: new ProxyLookup(new Lookup[] {
081: originalNode.getLookup(),
082: Lookups.singleton(new PathFinder(g)),
083: // no need for explicit search info
084: }));
085: this .g = g;
086: g.addPropertyChangeListener(WeakListeners.propertyChange(this ,
087: g));
088: }
089:
090: /** Copied from PackageRootNode with modifications. */
091: private Image computeIcon(boolean opened, int type) {
092: Icon icon = g.getIcon(opened);
093: if (icon == null) {
094: Image image = opened ? super .getOpenedIcon(type) : super
095: .getIcon(type);
096: return Utilities.mergeImages(image,
097: PackageRootNode.PACKAGE_BADGE, 7, 7);
098: } else {
099: if (icon instanceof ImageIcon) {
100: return ((ImageIcon) icon).getImage();
101: } else {
102: return PackageRootNode.icon2image(icon);
103: }
104: }
105: }
106:
107: public Image getIcon(int type) {
108: return computeIcon(false, type);
109: }
110:
111: public Image getOpenedIcon(int type) {
112: return computeIcon(true, type);
113: }
114:
115: public String getName() {
116: return g.getName();
117: }
118:
119: public String getDisplayName() {
120: return g.getDisplayName();
121: }
122:
123: public boolean canRename() {
124: return false;
125: }
126:
127: public boolean canDestroy() {
128: return false;
129: }
130:
131: public boolean canCut() {
132: return false;
133: }
134:
135: public void propertyChange(PropertyChangeEvent ev) {
136: // XXX handle SourceGroup.rootFolder change too
137: fireNameChange(null, null);
138: fireDisplayNameChange(null, null);
139: fireIconChange();
140: fireOpenedIconChange();
141: }
142:
143: /** Copied from PhysicalView and PackageRootNode. */
144: public static final class PathFinder {
145:
146: private final SourceGroup g;
147:
148: PathFinder(SourceGroup g) {
149: this .g = g;
150: }
151:
152: public Node findPath(Node rootNode, Object o) {
153: FileObject fo;
154: if (o instanceof FileObject) {
155: fo = (FileObject) o;
156: } else if (o instanceof DataObject) {
157: fo = ((DataObject) o).getPrimaryFile();
158: } else {
159: return null;
160: }
161: FileObject groupRoot = g.getRootFolder();
162: if (FileUtil.isParentOf(groupRoot, fo) /* && group.contains(fo) */) {
163: String relPath = FileUtil
164: .getRelativePath(groupRoot, fo);
165: List/*<String>*/path = new ArrayList();
166: StringTokenizer strtok = new StringTokenizer(relPath,
167: "/"); // NOI18N
168: while (strtok.hasMoreTokens()) {
169: path.add(strtok.nextToken());
170: }
171: // XXX this is really ugly... cf. #44739 and #33330.
172: path.set(path.size() - 1, fo.getName());
173: try {
174: return NodeOp.findPath(rootNode, Collections
175: .enumeration(path));
176: } catch (NodeNotFoundException e) {
177: try {
178: //#65555: DefaultDataObject cannot be selected
179: path.set(path.size() - 1, fo.getNameExt());
180: return NodeOp.findPath(rootNode, Collections
181: .enumeration(path));
182: } catch (NodeNotFoundException e2) {
183: return null;
184: }
185: }
186: } else if (groupRoot.equals(fo)) {
187: return rootNode;
188: } else {
189: return null;
190: }
191: }
192:
193: }
194:
195: /** Copied from PhysicalView. */
196: private static final class VisibilityQueryDataFilter implements
197: ChangeListener, ChangeableDataFilter {
198:
199: private static final long serialVersionUID = 1L; // in case a DataFolder.ClonedFilterHandle saves me
200:
201: private final EventListenerList ell = new EventListenerList();
202:
203: public VisibilityQueryDataFilter() {
204: VisibilityQuery.getDefault().addChangeListener(this );
205: }
206:
207: public boolean acceptDataObject(DataObject obj) {
208: FileObject fo = obj.getPrimaryFile();
209: return VisibilityQuery.getDefault().isVisible(fo);
210: }
211:
212: public void stateChanged(ChangeEvent e) {
213: Object[] listeners = ell.getListenerList();
214: ChangeEvent event = null;
215: for (int i = listeners.length - 2; i >= 0; i -= 2) {
216: if (listeners[i] == ChangeListener.class) {
217: if (event == null) {
218: event = new ChangeEvent(this );
219: }
220: ((ChangeListener) listeners[i + 1])
221: .stateChanged(event);
222: }
223: }
224: }
225:
226: public void addChangeListener(ChangeListener listener) {
227: ell.add(ChangeListener.class, listener);
228: }
229:
230: public void removeChangeListener(ChangeListener listener) {
231: ell.remove(ChangeListener.class, listener);
232: }
233:
234: }
235:
236: private static final class PackageFilterChildren extends
237: FilterNode.Children {
238:
239: public PackageFilterChildren(final Node originalNode) {
240: super (originalNode);
241: }
242:
243: protected/*@Override*/Node copyNode(final Node originalNode) {
244: DataObject dobj = (DataObject) originalNode.getLookup()
245: .lookup(DataObject.class);
246: return (dobj instanceof DataFolder) ? new PackageFilterNode(
247: originalNode)
248: : super .copyNode(originalNode);
249: }
250: }
251:
252: private static final class PackageFilterNode extends FilterNode {
253:
254: public PackageFilterNode(final Node origNode) {
255: super (origNode, new PackageFilterChildren(origNode));
256: }
257:
258: public/*@Override*/void setName(final String name) {
259: if (Utilities.isJavaIdentifier(name)) {
260: super .setName(name);
261: } else {
262: DialogDisplayer.getDefault().notify(
263: new NotifyDescriptor.Message(NbBundle
264: .getMessage(TreeRootNode.class,
265: "MSG_InvalidPackageName"),
266: NotifyDescriptor.INFORMATION_MESSAGE));
267: }
268: }
269:
270: }
271:
272: }
|